做题的时候有考虑过CVE,但当时没去查……
CVE–2018-1000001
该题思路来源于glibc的CVE–2018-1000001,是一个glibc的缓冲区溢出漏洞,分析后发现能在堆上进行溢出。
以下分析stdlib/canonicalize.c
中的__realpath()
函数(__canonicalize_file_name
仅仅调用__realpath()
,没有其它操作)。
1 | /* Return the canonical absolute name of a given file. |
从源代码中可以发现,如果getcwd返回的地址不以/开头的话,就会产生堆的上溢的问题,同时能够向这个上溢的地址写入数据。
easyexp
本题的原理即__canonicalize_file_name
。在本题中,由于程序变更工作目录后,并没有更新当前目录的根目录,因此getcwd()
会在返回的路径前加上(unreachable)
,即getcwd()
在本题中返回(unreachable)/tmp
。随后为了保证程序正常运行,需要通过__lxstat64()
的检查,所以需要保证(unreachable)/tmp
存在,故将用户名设置为(unreachable)
并在该文件夹下创建名为tmp
的文件。
由于存在堆上的前溢且程序构造了堆的使用,因此可以修改chunk的pre_inuse
,利用unlink
获得shell。
程序创建文件的过程
在程序中定义了如下数据结构:
1 | struct FILE_CACHE { |
在bss上存在一个FILE_CACHE[3]
数组用于保存相关信息。
1 | if ( filename ) |
以上程序位于创建文件的函数中,当用户创建文件时,会现在“缓存”中查找,如果文件名相同或“缓存”未满,则会在“缓存”上保存一份数据,如已满则重置一个“缓存”。这里的结构可以在unlink
中进行利用。
程序创建文件夹的过程
1 | for ( i = 0; ; i = *(_DWORD *)v3 + 1 ) |
当程序调用mkdir()
函数后,会将用户输入的路径传入canonicalize_file_name()
进行验证是否创建成功,此处即为触发漏洞的位置。
利用思路
当“缓存”满后,将会重用最后使用的“缓存”的下一个“缓存”。首先将三个“缓存”都填满,第二个缓存内容均为’/‘,利用CVE漏洞改写第三个“缓存”指向的内容的chunk的size
域,将size
改小(防止和top chunk合并)并布置合适的fake chunk。
随后进行unlink
攻击,通过改写“缓存”结构体中的内容指针来泄漏地址和修改__free_hook
。
Exp
1 | # coding=utf-8 |