PWN x64漏洞之构造多级指针链实现任意地址读写:linux下的一道格式化字符串漏洞题分析分享
简单分享一下解题过程。
下载题目,里面有三个文件,如图:
DockerFIle文件:
net.sh文件:
shell文件是一个elf,文件情况:
64位,Full RELO,NX,PIE
丢进IDA看一下,只有一个main和一个sub:
函数功能是:
(1)main 循环 50 次接收输入的命令,并交给 sub 处理;
(2)命令 ‘exit’ 退出程序;
(3)命令 ’ls‘ ,调用 puts 输出一个 “file.txt” 字符串
(4)命令 ‘cat’ ,从 open 的 file.txt 中读 100 个字节
(5)命令 ‘echo ’ ,将字符串 ‘echo ’ 后面的字符串写入 stream 。
分析函数的问题,很明显,在子函数里,一个字符串溢出漏洞:
打开Linux,根据DockerFile搭好环境............................
连接IDA开始调试,在 printf 处下断点观察栈的情况,蓝色位置是此时的 rsp 位置,观察栈上的数据,有 libc、main、和栈指针可以利用,
考虑到是 x64 程序 + Full Relro保护,漏洞思路就是构建栈指针链,实现任意地址读写。
接下来就是找指针链,发现参数 17 的 指向 参数 45 ,而参数 45 也是一个在栈上的地址
参数 17:
参数 45:
这样一来,通过参数 17 修改 参数45 的最低位,就可以改写参数 45 指向的一个完整的 QWORD 数据,实现任意地址读写
接下来就是找 get shell 的办法,观察程序内可利用的函数或者代码段,,,,,
------------------没有-----------------------
除了几个初始化的函数,就是 main、sub、跳转用的 bnd jmp 和 extern,,,,那只能自己构建ret2system了。
程序是 x64 ,所以我们在选择 ret2 的方法时,还要考虑 system 参数的构造问题,这里通过动态调试,发现 fclose 函数在结束之后,rdi 会指向一个不变的堆地址:
并且这个堆地址和 main 函数下变量 s 的偏移是 0x258
为了保证在调用 system 时,rdi 的值不被覆盖,这里要求控制 main 的循环次数,使 sub 返回到 main 时,循环正好结束并执行 leave retn,执行流程如图所示(丑不拉几的线条):
所以接下来就是枯燥无聊的写 python 脚本,
要完成的任务有:
(1)在栈的高处构建 system 的栈空间(我选了 main 栈上方 0x3000处,为了防止栈数据被其他函数覆盖),并保证 ret2system 时,rsp 对齐到 0x10;
(2)main 的返回地址定向到 main 的 leave,这样进行 两轮 mov rsp , rbp pop rbp 之后,就可以开辟出 system 调用需要的栈空间;
(3)在 fclose 返回时 rdi 的地址处写入字符串 ‘/bin/sh\n’ ,这里要注意从高地址向地址写,因为这里的 rdi 低位数据里存放了堆的数据,修改掉之后会导致 fopen 异常;
(4)修改 main 函数存放 for 循环次数的变量 i ,并且要求 i 的值满足:最后一轮向 fclose 返回的 rdi 地址写入数据后,main 函数的 for 循环结束(如果不结束循环,rdi的值会被覆盖)
main 函数的栈空间修改效果(修改了 rbp,这里本来是 0 ,修改了 ret 的返回栈,这里原本是 libc_start_main + 0xXX
main + 0xA4:
为调用 system 构造的高位栈空间:
然后大功告成:
写在最后,本人是个纯纯 0 基础菜鸟,以前只学过一丢 win 逆向,从这道题目我才开始接触 linux 和格式化字符串的漏洞,,,,,,
文章是赶时间写的,如果有什么不正确的地方或者疑惑的地方,欢迎留言交流讨论~~~