pwn题学习_格式化字符串

信息收集

拿到题目后,先扔到ida里面看看,有个很明显的溢出和格式化字符串

image-20240625130748979

buf的长度是40,但是读入的长度是0x40。

再看看字符串

image-20240625131029325

没system,也没看到有命令执行啥的危险函数的调用。感觉大概率是ret2libc了

再看看checksec

image-20240625131200693

保护开着也是真全。基本上确定是用格式化字符串泄露libc地址再用ret2libc来getshell了

格式化字符串的利用

第一次接触格式化字符串,并且这是个64位的程序,这边有个地方有困惑,因为查询到的资料显示,64位下的*nix系统的函数参数传参是前6个参数传递给了RDI,RSI,RDX,RCX,R8,R9寄存器,后续的参数再用栈进行传递,所以我总觉得至少应该是第7个参数才会到栈上,但是其实用%6$p就可以获取到传到栈上的参数了。

格式化字符串的漏洞利用可以对任意内存进行读写,虽然在读内存时可能会遇到00截断的问题(%s)。

在经过尝试后,发现本题用%6$p获取到了栈上写入的可控数据。通过gdb调试查看

image-20240625135154381

第6位的参数地址是0x7fffffffe360,canary的地址是0x7fffffffe388,要覆盖的返回值地址是0x7fffffffe398。所以通过%11$p即可读取到canary的值,这里其实也可以静态分析

image-20240625135410493

查看stack算buf和var_8的差值是0x28,和gdb动态调试结果一致。又因为buf是用%6$p读取,x64是8个字节一组,所以0x28/0x8+6=11,canary的位置是11个参数处。

通过这些信息,我们目前可以做到了任意地址跳转和绕过canary。下一步就是绕过aslr,获取libc的基址。

首先我们得计算下程序加载的基址,通过前面gdb的调试,我们可以发现0x7fffffffe398处对应的是函数返回后要执行的地址,对应在ida中的地址就是

image-20240625135841169

两者做差即可得到加载基址,再通过pwntools获取read,printf的偏移再加上基址,即可读到read和printf在got表中的地址,又因为aslr实际上是不会改变低12位,所以通过多个函数加载的地址即可确定libc的版本。再确定libc后,也可以相应的确定system函数和/bin/sh字符串在libc中的相对偏移

image-20240625140306847

获取之后就可以开始尝试进行ret2libc了。但是这里会有个问题,因为64位的系统是1通过rdi来传递参数的,所以我们需要找一条gadget来pop rdi再ret。采用ROPgadget进行查找

image-20240625141537929

可能有细心的师傅会发现ROPgadget的地址有时和ida的对不上,这个没关系。以ROPgadget为准,机器码只要执行了就行。

但是问题又来了,溢出的长度只有16位,刚刚好只能覆盖到返回地址处,那么该如何进行rop呢。这里我们要充分利用格式化字符串的任意地址写。采用%hhn,单字节的长度写入以打印的字符长度。

%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100x%10$n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而%$hn表示写入的地址空间为2字节,%$hhn表示写入的地址空间为1字节,%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过%$hn或%$hhn来适时调整。

这里我们先需要获取栈的地址,通过%1$p可以获取到buf开始的栈地址。预留一定的空间(因为不能写到用于写的地址)采用%hhn来逐字节写入。

这样整体的思路就已经能串起来了。

  1. %11$p读canary
  2. %1$p获取栈地址
  3. %hhn写入ROP的bin_sh_addr和system_addr
  4. 栈溢出,跳转pop rdi;ret

但是实际上用的时候,却获得了EOF error。GDB调试发现也正常进入了system。这又是因为在64位的ubuntu下,调用system时栈要以16进制为基础进行对齐,这就要求我们得多ret一次才行。再次使用ROPgadget来寻找ret

image-20240625142002639

所以最终的流程是这样的:

  1. %11$p读canary
  2. %1$p获取栈地址
  3. %hhn写入ROP的pop rdi和bin_sh_addr和system_addr
  4. 栈溢出,跳转ret

最终脚本如下:

成功getshell

image-20240625142152955

参考文章

https://www.anquanke.com/post/id/194458

https://blog.eonew.cn/2019/05/11/%E5%9C%A8%E4%B8%80%E4%BA%9B64%E4%BD%8D%E7%9A%84glibc%E7%9A%84payload%E8%B0%83%E7%94%A8system%E5%87%BD%E6%95%B0%E5%A4%B1%E8%B4%A5%E9%97%AE%E9%A2%98/

https://xz.aliyun.com/t/12712

https://cloud.tencent.com/developer/article/1627892