可以看得出来这篇文章的标题十分的水,因为确实是很难找到这两道题又什么闪光点来作为标题。这篇文章更新后会暂停学习qemu了,后续会进一步学习AFL以及开始接触docker逃逸。
FastCP-ctf
关于设备的分析可以参考前面两篇文章,这里就不再赘述了。
函数分析
1 | uint64_t __fastcall fastcp_mmio_read(FastCPState *opaque, hwaddr addr, unsigned int size) |
首先就是read函数这里是非常常规的一些内容。
1 | void __fastcall fastcp_mmio_write(FastCPState *opaque, hwaddr addr, uint64_t val, unsigned int size) |
再就是write函数,可以修改opaque->cp_state.cmd
、opaque->cp_state.CP_list_cnt
、opaque->cp_state.CP_list_src
,并且可以看到中间会触发timer。
1 | void __fastcall fastcp_cp_timer(FastCPState *opaque) |
在timer中存在三个由cmd属性控制的分支,这里直接说三个分支的功能:
opaque->cp_state.cmd = 2
; 从opaque->cp_state.CP_list_src
读取内容到栈上,通过( cp_info.CP_cnt <= 0x1000 )
验证之后再将cp_info.CP_src
内容读取到opaque->CP_buffer
上。opaque->cp_state.cmd = 4
; 从opaque->cp_state.CP_list_src
读取内容到栈上,未通过任何验证,直接将opaque->CP_buffer
写到cp_info.CP_dst
上opaque->cp_state.cmd = 1
; 从opaque->cp_state.CP_list_src + 8 * v9
读取内容到栈上,未通过任何验证,将cp_info.CP_src
读取到opaque->CP_buffer
上,再将opaque->CP_buffer
写到cp_info.CP_dst
上。
1 | 00000000 FastCPState struc ; (sizeof=0x1A30, align=0x10, copyof_4530) |
结合上述结构体再加上上面的分析结果漏洞已经呼之欲出了。因为情况2和3中没有对len进行验证导致可以越界使用结构体产生的漏洞。
利用分析
因为漏洞点较为简单,所以利用方式也比较简单
- 首先通过情况2越界读取到
cp_timer
成员中的内容。该成员中cb的值为fastcp_cp_timer
函数的地址(在pci_FastCP_realize
中完成赋值),进而泄漏出system的地址。顺便泄漏出opaque成员地址。 - 通过情况3越界写入内容到
cp_timer
成员,劫持cb和opaque。 - 最后触发timer完成利用
注意!!
虽然利用方式特别简单,但是这道题目有一点是非常容易被忽略的。那就是物理地址连续不代表虚拟地址连续!
在下面exp中,在第一次读取到cp_timer
成员到内容后并没有使用*(unsigned long long *)(userbuf + 0x1010)
来读取,因为程序中实际写入到函数是cpu_physical_memory_rw(cp_info.CP_dst, opaque->CP_buffer, cp_info.CP_cnt, 1);
而这里写入到的是物理地址,但是物理地址并不连续,所以这里是读取不到的。所以最后往cp_timer
成员写入的时候使用的也是va2pa(userbuf + 0x1000) - 0x1000
写入。
综上,exp
1 |
|
d3dev
先看看结构体
1 | struct __attribute__((aligned(16))) d3devState |
分析函数
1 | uint64_t __fastcall d3dev_mmio_read(d3devState *opaque, hwaddr addr, unsigned int size) |
mmio_read
函数这里,首先是根据seek和addr定位到数据,随后将数据进行tea解密,然后第一次输出低32位,第二次输出高32位。
1 | void __fastcall d3dev_mmio_write(d3devState *opaque, hwaddr addr, uint64_t val, unsigned int size) |
在mmio_write
函数中首先一样先通过seek和addr得到index,第一次使用时是直接在低32位写入输入的数据,后面的则是低32位和高32位进行tea加密随后写入到地址位置。
1 | // local variable allocation has failed, the output may be wrong! |
这里不说pmio_read
函数了,因为确实没啥用就不浪费篇幅了。这里看pmio_write
函数,可以喊到在port等于28时会给r_seek
赋值,并且会调用opaque->rand_r
第一个参数就是opaque->r_seed
的地址。而在addr等于8并且val小于0x100时则是往seek中写入值。可以看出来这里漏洞一样是存在越界使用结构体。
利用分析
这里的利用思路也是较为清晰的
- 首先修改seek配合addr实现使用
mmio_write
函数实现越界写,将opaque->rand_r
原有的函数地址进行tea加密并且写入到当前位置。 - 两次调用
mmio_read
函数,分别读取opaque->rand_r
高位和低位泄漏出libc地址,进而拿到system地址。 - 应为
r_seek
成员和blocks
成员紧邻的缘故,恢复seek为0并通过addr在blocks
成员开始位置写入flag
- 最后直接调用
pmio_write
并且port为28修改r_seed
为nl /
即可调用nl /flag
综上,exp
1 |
|
题目链接:
https://github.com/196082/196082/tree/main/qemu_escape