欢迎来到 无奈人生 安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

Totel Meltdown(CVE-2018-1038 )漏洞利用

来源: 作者: 时间:2019-02-24 19:29 点击: 我要投稿
广告位API接口通信错误,查看德得广告获取帮助

Ulf Frisk 在今年 3 月发现了一个作用于 Windows7 和 Server 2008 的漏洞。这个漏洞很有意思:微软发布了一个补丁来修复之前爆出的「Meltdown」漏洞,然后这个补丁却不经意导致了新的问题,让任意进程可以读取和修改页表项目。
有关这个漏洞的 wirteup 可以在 Ulf 的博客上找到,十分值得一读。
我这一周有了一些空闲时间,所以我决定好好地看看这个漏洞的详细细节。我的目标是打造一个用于进程提权的简洁的 exploit。为了完成这个目标,我第一次详细地分析了 Windows 的内存管理机制,因此我写下了这篇博客。
和往常一样,这篇文章主要是教大家学习这个 exploit 的技巧而不是仅仅提供一个随时可用的 exploit。所以,我们先看一下内存分页的一些基本概念。
分页机制
为了理解 CVE-2018-1038 漏洞,我们首先需要对 x86/x64 架构下的分页机制有所了解。
我们都知道,在一个 x64 架构上的 OS 中,虚拟地址大概是如下这个样子的:
0x7fffffd6001
接下来的事情就不是众所周知的了:这个虚拟地址并不是一个指向某个实际物理地址的指针。它实际上由许多字段组成,这些字段联合在一起被转化为物理地址。
我们首先将上述虚拟地址转为二进制表示。
0000000000000000 000001111 111111111 111111111 111010110000000000001
从左到右的前 16 位并不存在任何实际意义,它们仅仅是虚拟地址第 48 位的简单复制(译者注: 由于目前还用不到完整的 64 位寻址空间,AMD64 架构只支持 48 位的内存地址,剩下 16 位只是这 48 位地址的符号扩展)。
接下来从第 48 位开始看
1)前 9 位,000001111(十进制 15),是 PML4 表中的一个偏离。
2)之后的 9 位,11111111(十进制 511),是 PDPT 表中的一个偏移。
3)再之后的 9 位,11111111(十进制 511),是 PD 表中的一个偏移。
4)再之后的 9 位,111010110(十进制 470),是 PT 表的一个偏移。
5)最后的 12 位,0000000000000001(十进制 1),是内存页面的一个偏移。
于是理所当然的,下一个问题就是…PML4, PDPT, PD 和 PT 到底是什么?
PML4, PDPT, PD 和 PT
在 x64 架构中,将一个虚拟地址转化为物理地址,我们需要如下一系列的页表。CR3 寄存器指向了最开始的 PML4 页表。
1)PML4– Page Map Level 4
2)PDPT– Page Directory Pointer Table
3)PD– Page Directory
4)PT– Page Table
每一个页表都负责提供我们寻址过程中需要的物理地址,以及这个物理地址的一些标志位。
例如,一个页表中的一个项目可能会负责提供我们一个指向下一级页表的指针,同时也会负责设置页面的 NX 位,或者保证指向的内存页面属于 kernel,不能被操作系统中的进程访问。
将实际的概念简化后,虚拟地址将如下通过四个页表最终转为物理地址。

这样我们可以看到,通过一个页表项目及指向下一级页表,进程会遍历上述四个页表,最终最后一个页表会指向虚拟地址对应的物理内存页面,然后再加上最后的偏移量形成实际物理地址。
我们可以想象,对一个操作系统来说保存和管理上述页表会需要一些开销。因此 OS 开发者会使用一个叫做「自引用页表」的技术来尽量避免上述繁杂的流程。
自引用页表
简单的说,「自引用页表」就是在 PML4 页表中的某一项中自我引用。例如,我们在 PML4 表的偏移为 0×100 位置中创建新的条目,其指向的内存就是 PML4 所在的内存地址,我们就有了一个「自引用条目」
为什么需要这么做呢?实际上,这样就提供了我们一些虚拟地址,使得我们可以通过这些地址来查看和修改页表。
例如,如果我们想修改 PML4,我们可以直接引用虚拟地址 0×804020100000。这个虚拟地址会如下地进行转换:
1)查找 PML4 的 0×100 条目:得到 PML4 的物理地址
2)查找 PDPT 的 0×100 条目:同样的,还是得到 PML4 的物理地址
3)查找 PD 的 0×100 条目:同样的,还是得到 PML4 的物理地址
4)查找 PT 的 0×100 条目:同样的,还是得到 PML4 的物理地址
再加上最后 12 位的偏移量,我们最终得到的物理地址还是 PML4 的物理地址。
希望你们看到这的时候能够对自引用页表的概念有所了解…对于我来说我花了好几个晚上盯着屏幕才把这玩意搞明白 :D
我们用下面的代码作为进一步的例子。我们可以看到虚拟地址 0xffff804020100000 可以让我们编辑 PML4。在这个例子中,PML4 的 0×100 个条目是自引用的。
package main
import (
    "fmt"
)
func VAtoOffsets(va uint64) {
    phy_offset := va & 0xFFF
    pt_index := (va >> 12) & 0x1FF
    pde_index := (va >> (12 + 9)) & 0x1FF
    pdpt_index := (va >> (12 + 9 + 9)) & 0x1FF
    pml4_index := (va >> (12 + 9 + 9 + 9)) & 0x1FF
    fmt.Printf("PML4 Index: %03x\n", pml4_index)
    fmt.Printf("PDPT Index: %03x\n", pdpt_index)
    fmt.Printf("PDE Index: %03x\n", pde_index)
    fmt.Printf("PT Index: %03x\n", pt_index)
    fmt.Printf("Page offset: %03x\n", phy_offset)
}
func OffsetsToVA(phy_offset, pt_index, pde_index, pdpt_index, pml4_index uint64) {
    var va uint64

[1] [2] [3]  下一页

Ulf Frisk 在今年 3 月发现了一个作用于 Windows7 和 Server 2008 的漏洞。这个漏洞很有意思:微软发布了一个补丁来修复之前爆出的「Meltdown」漏洞,然后这个补丁却不经意导致了新的问题,让任意进程可以读取和修改页表项目。
有关这个漏洞的 wirteup 可以在 Ulf 的博客上找到,十分值得一读。
我这一周有了一些空闲时间,所以我决定好好地看看这个漏洞的详细细节。我的目标是打造一个用于进程提权的简洁的 exploit。为了完成这个目标,我第一次详细地分析了 Windows 的内存管理机制,因此我写下了这篇博客。
和往常一样,这篇文章主要是教大家学习这个 exploit 的技巧而不是仅仅提供一个随时可用的 exploit。所以,我们先看一下内存分页的一些基本概念。
分页机制
为了理解 CVE-2018-1038 漏洞,我们首先需要对 x86/x64 架构下的分页机制有所了解。
我们都知道,在一个 x64 架构上的 OS 中,虚拟地址大概是如下这个样子的:
0x7fffffd6001
接下来的事情就不是众所周知的了:这个虚拟地址并不是一个指向某个实际物理地址的指针。它实际上由许多字段组成,这些字段联合在一起被转化为物理地址。

内容来自无奈安全网

我们首先将上述虚拟地址转为二进制表示。
0000000000000000 000001111 111111111 111111111 111010110000000000001
从左到右的前 16 位并不存在任何实际意义,它们仅仅是虚拟地址第 48 位的简单复制(译者注: 由于目前还用不到完整的 64 位寻址空间,AMD64 架构只支持 48 位的内存地址,剩下 16 位只是这 48 位地址的符号扩展)。
接下来从第 48 位开始看
1)前 9 位,000001111(十进制 15),是 PML4 表中的一个偏离。
2)之后的 9 位,11111111(十进制 511),是 PDPT 表中的一个偏移。
3)再之后的 9 位,11111111(十进制 511),是 PD 表中的一个偏移。
4)再之后的 9 位,111010110(十进制 470),是 PT 表的一个偏移。
5)最后的 12 位,0000000000000001(十进制 1),是内存页面的一个偏移。
于是理所当然的,下一个问题就是…PML4, PDPT, PD 和 PT 到底是什么?
PML4, PDPT, PD 和 PT
在 x64 架构中,将一个虚拟地址转化为物理地址,我们需要如下一系列的页表。CR3 寄存器指向了最开始的 PML4 页表。
1)PML4– Page Map Level 4
2)PDPT– Page Directory Pointer Table
3)PD– Page Directory 内容来自无奈安全网
4)PT– Page Table
每一个页表都负责提供我们寻址过程中需要的物理地址,以及这个物理地址的一些标志位。
例如,一个页表中的一个项目可能会负责提供我们一个指向下一级页表的指针,同时也会负责设置页面的 NX 位,或者保证指向的内存页面属于 kernel,不能被操作系统中的进程访问。
将实际的概念简化后,虚拟地址将如下通过四个页表最终转为物理地址。

这样我们可以看到,通过一个页表项目及指向下一级页表,进程会遍历上述四个页表,最终最后一个页表会指向虚拟地址对应的物理内存页面,然后再加上最后的偏移量形成实际物理地址。
我们可以想象,对一个操作系统来说保存和管理上述页表会需要一些开销。因此 OS 开发者会使用一个叫做「自引用页表」的技术来尽量避免上述繁杂的流程。

本文来自无奈人生安全网


自引用页表
简单的说,「自引用页表」就是在 PML4 页表中的某一项中自我引用。例如,我们在 PML4 表的偏移为 0×100 位置中创建新的条目,其指向的内存就是 PML4 所在的内存地址,我们就有了一个「自引用条目」
为什么需要这么做呢?实际上,这样就提供了我们一些虚拟地址,使得我们可以通过这些地址来查看和修改页表。
例如,如果我们想修改 PML4,我们可以直接引用虚拟地址 0×804020100000。这个虚拟地址会如下地进行转换:
1)查找 PML4 的 0×100 条目:得到 PML4 的物理地址
2)查找 PDPT 的 0×100 条目:同样的,还是得到 PML4 的物理地址
3)查找 PD 的 0×100 条目:同样的,还是得到 PML4 的物理地址
4)查找 PT 的 0×100 条目:同样的,还是得到 PML4 的物理地址
再加上最后 12 位的偏移量,我们最终得到的物理地址还是 PML4 的物理地址。
希望你们看到这的时候能够对自引用页表的概念有所了解…对于我来说我花了好几个晚上盯着屏幕才把这玩意搞明白 :D
我们用下面的代码作为进一步的例子。我们可以看到虚拟地址 0xffff804020100000 可以让我们编辑 PML4。在这个例子中,PML4 的 0×100 个条目是自引用的。

内容来自无奈安全网


package main
import (
    "fmt"
)
func VAtoOffsets(va uint64) {
    phy_offset := va & 0xFFF
    pt_index := (va >> 12) & 0x1FF
    pde_index := (va >> (12 + 9)) & 0x1FF
    pdpt_index := (va >> (12 + 9 + 9)) & 0x1FF
    pml4_index := (va >> (12 + 9 + 9 + 9)) & 0x1FF
    fmt.Printf("PML4 Index: %03x\n", pml4_index)
    fmt.Printf("PDPT Index: %03x\n", pdpt_index)
    fmt.Printf("PDE Index: %03x\n", pde_index)
    fmt.Printf("PT Index: %03x\n", pt_index)
    fmt.Printf("Page offset: %03x\n", phy_offset)
}
func OffsetsToVA(phy_offset, pt_index, pde_index, pdpt_index, pml4_index uint64) {
    var va uint64
无奈人生安全网

[1] [2] [3]  下一页 www.wnhack.com

。 (责任编辑:admin)
【声明】:无奈人生安全网(http://www.wnhack.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱472701013@qq.com,我们会在最短的时间内进行处理。