续之前的 HelloUser,向第二层出发。
Kernel分析
由于第一层对 User已经成功,现在就是对第二层的 Kernel进行分析。说实话这个如果完全靠自己逆的话,我觉得代码量有点大,但是我觉得出题人一般为了简化逆向难度,是会把漏洞放在几个特定的套路地点里的,还需要以后多做题摸索摸索吧。
backdoor分析

可以看到在 syscall_handler里处理系统调用号时,会检查系统调用号是否超过了 0x2333,这个大小是比较奇怪的,然后我们可以去系统调用表里找到最后一个 0x2333系统调用函数,我把它命名为 backdoor:

进入 backdoor函数后,我们就能够发现一些有点熟悉的场景了:


这就是一个典型的菜单题环境,出题人还是很友好的。实现了4个功能,调用 kmalloc来创建chunk,将chunk_ptr和size 放到内存里的一个地址,我们定义为 chunk_list。通过 kfree实现了释放chunk,这里就存在漏洞,释放后未对 chunk_list里的 chunk_ptr和size清空,存在 UAF 或者 double free 漏洞。先通过 get_addr函数获取 chunk的物理地址,再通过 hp_write函数实现了 对chunk数据的读写,这里也存在漏洞,未对size进行检测,可以实现越界读取。通过 qmemcpy对实现对chunk的写入,需要从一个 srcbuf传入数据。
Kmalloc 和 Kfree分析
可以参照上一节所用源码,继续分析,差别不是太大。首先分析 Kmalloc,我们申请的 len 必须大于等于0x80,且为 0x80的倍数

我们申请的 size 必须与 0x10对齐

这两点是 申请 chunk时需要注意的限制。
Kfree的逻辑更好分析一点,首先检查 size 是否与 0x10对齐,然后是清空 user_data,如果 chunk_addr与 MEMORY相邻直接合并,类似于 malloc合并到 top chunk。

如果与 MEMORY不相邻,则执行一个插入到空闲chunk链表操作,这里插入是按照大小排序,从小到大,然后使用 next指针连接。可以看到 free是没有做类似于 _int_free里的各种检查。

那么,我们可以初步猜测 free_chunk的结构如下:
1 | chunk_header: | size | nop| |
Kernel内存分析
还需要对 Kernel 内存有一个详细的了解,才能更好的帮助我们利用堆漏洞。
上面 执行读取功能时,会先利用 get_addr来获取 chunk的物理地址:

在 get_addr函数里,是通过 与 0x8000000000 异或获得物理地址。
这一点与我们上一节分析 Kernelmain函数中 init_alloctor函数一致。

在 initalloctor函数中,设置了 MEMORY也就是 arena.top类似于 top chunk的地址为 0x8000000000,以及size,并对其进行了 memset(0)的操作。

如果进一步分析 initpagetable函数是能够发现 Kernel所作的内存映射,将 0x8000000000~0x8002000000 映射到0x0~0x2000000 物理空间。
我们现在就已经知道 虚拟地址的基址为 0x8000000000,可以通过加上相对地址获取到变量的虚拟地址,从而获取到 chunklist 和 sizelist 在 hellokernel 中的虚拟地址。
利用分析
经过对 Kernel的分析,我们已经能够大致清楚Kernel 漏洞,但是还需要思考一点,如何利用这个漏洞?这道题的思路我觉得tql了,xxrw 真是带我走进了 Kernel和虚拟化的大门呀,膜一下。
首先按照我们之前分析,可以发现 virtual应该是将 内核代码和用户代码 映射到了如下0x2000000的内存上。

- 构建
double free分配到chunk_list
首先,我们需要构建一个 double free,然后分配chunk到 chunk_list。
我们先申请 0x30 的堆块,将系统里原有的 chunk碎片整理一下。
然后申请一个 0x100的chunk,此时 会在 chunk_list的size留下 0x100,为我们后续伪造堆块做好了 fake_chunk_header的准备
随后申请2个 0xf0 的 chunk2, chunk3。并释放2次 chunk2,此时 double_free漏洞已经形成:

随后,我们修改 chunk2 的 next指针,指向 0x159d8,也就是 size为 0x100的地址处。但是此前,我们需要先将我们需要的数据布在程序内存中,这里我们就布置在第一节中的 stack处。如下图所示,next指针已经修改。

- 劫持
chunk_list,修改sys_open
接下来,我们就可以劫持chunk_list,并实现任意地址修改了,我们选择修改修改 之前的 sys_open函数。我们只需要将 sys_open函数中0xb73这个 jnz跳转两字节使用 x90 patch掉,我们后续就可以读取任意文件了。

所以,我们先修改 chunk_list上的chunk_ptr使其指向 0xb73,然后再调用 editKernel修改2字节,即可。如下图已经成功patch掉 jnz命令。

- 打开
flag,并输出
随后我们就可以执行 ORW来打开flag并输出。
EXP
1 | from pwn import * |
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2020/10/27/TSCTF-helloKernel/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!