week1 刷题

1. [GXYCTF2019]simple CPP

​ IDA载入直接分析,这个程序的主要加密逻辑有几块:

image-20251224142022382

第一块是一个异或,和key进行异或

img

第二部分是将8个字符拼成一个QWORD,然后存放到v20当中

img

​ 最后就是将v20的值拿出来计算然后进行比较判断。在最后这个加密当中,运用了一大堆的运算,那么z3是最好的方式,直接z3求解即可。

​ 这里有一个注意的地方是,我们并不知道输入有多长,只知道最后比较的长度是32字节,所以这里的z3需要从分组之后开始求解。求解出来后再回推input。这里试了一下从开头写z3到结尾会导致解出来的值不太一样。

  • 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
from z3 import*
f1, f2, f3, f4 = BitVecs('f1 f2 f3 f4', 64)
s = Solver()

s.add(f3 & (~f1) == 0x11204161012)
s.add((f3 & (~f2)) & f1 | f3 & ((f2 & f1) | f2 & (~f1) | (~(f2 | f1))) == 0x8020717153E3013)
s.add((f3 & ~f1) | (f2 & f1) | (f3 & (~f2)) | (f1 & ~f2) == 0x3E3A4717373E7F1F)
s.add((((f3 & (~f1)) | (f2 & f1) | (f3 & (~f2)) | (f1 & ~f2)) ^ f4) == 0x3E3A4717050F791F)
s.add((((f3 & (~f1)) | (f2 & f1) | f2 & f3)) == (~f1 & f3 | 0xC00020130082C0C))
print(s.check())

d2 = []
d = s.model()
flag1 = hex(d[f1].as_long())[2:].rjust(16, "0")
flag2 = hex(d[f2].as_long())[2:].rjust(16, "0")
flag3 = hex(d[f3].as_long())[2:].rjust(16, "0")
flag4 = hex(d[f4].as_long())[2:-2]

def decode(flag, d):
le = len(flag)
for i in range(0, le, 2):
if flag[i:i+2] != "":
d.append(int(flag[i:i+2], 16))

decode(flag1, d2)
decode(flag2, d2)
decode(flag3, d2)
decode(flag4, d2)
key = 'i_will_check_is_debug_or_not'
fake_flag = ""
for i in range(len(d2)):
fake_flag += chr(ord(key[i]) ^ d2[i])
print(fake_flag)
# We1l_D0ndeajoa_Slgebra_am_i

right_flag = ''
right_flag += fake_flag[:8]
right_flag += 'e!P0or_a' # 多解,比赛的时候hint需要将这个替换上去
right_flag += fake_flag[16:]
print(right_flag)

# flag{We1l_D0ne!P0or_algebra_am_i}

2. [网鼎杯 2020 青龙组]singal

​ 非常基础的一道vm题,直接ida分析:

img

使用idapython将code字节码抓出来:

1
2
3
4
5
start = 0x403040
print("[")
for i in range(455):
print(ida_bytes.get_byte(start+i), end=", ")
print("\n]")

img

然后直接将vm逻辑用python抄出来,z3配合直接求解出来:

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
from z3 import *

s = Solver()
x = [BitVec(f"x{i}",8) for i in range(15)]

opcode = [
10, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 11, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 81, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 36, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 54, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 65, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 7, 0, 0, 0, 63, 0, 0, 0, 7, 0, 0, 0, 52, 0, 0, 0, 7, 0, 0, 0, 50, 0, 0, 0, 7, 0, 0, 0, 114, 0, 0, 0, 7, 0, 0, 0, 51, 0, 0, 0, 7, 0, 0, 0, 24, 0, 0, 0, 7, 0, 0, 0, 167, 255, 255, 255, 7, 0, 0, 0, 49, 0, 0, 0, 7, 0, 0, 0, 241, 255, 255, 255, 7, 0, 0, 0, 40, 0, 0, 0, 7, 0, 0, 0, 132, 255, 255, 255, 7, 0, 0, 0, 193, 255, 255, 255, 7, 0, 0, 0, 30, 0, 0, 0, 7, 0, 0, 0, 122, 0, 0, 0, 0
]

v9 = 0
Str = [0] * 1000
end = 114
v8 = 0
v7 = 0
v6 = 0
v5 = 0
v4 = 0
while v9 < end:
op = opcode[v9 * 4]
if op == 1:
Str[v6+100]=v4
v9 += 1
v6 += 1
v8 += 1
elif op == 2:
v4 = Str[v8] + opcode[(v9 + 1)*4]
v9 += 2
elif op == 3:
v4 = Str[v8] - opcode[(v9 + 1)*4]
v9 += 2
elif op == 4:
v4 = opcode[(v9 + 1)*4] ^ Str[v8]
v9 += 2
elif op == 5:
v4 = opcode[(v9 + 1)*4] * Str[v8]
v9 += 2
elif op == 6:
v9 += 1
elif op == 7:
print(Str[v7 + 100])
s.add(Str[v7 + 100] == opcode[(v9 + 1)*4])
v9 += 2
v7 += 1
elif op == 8:
Str[v5] = v4
v9 += 1
v5 += 1
elif op == 10:
print("Read input")
for i, v in enumerate(x):
Str[i] = v
v9 += 1
elif op == 11:
v4 = Str[v8] - 1
v9 += 1
elif op == 12:
v4 = Str[v8] + 1
v9 += 1
else:
print(f"unknown insn {op}")


print(s.check())
if s.check():
m = s.model()
for i in x:
val = m.eval(i, model_completion=True)
print(chr(val.as_long()), end="")

from z3 import *

s = Solver()
x = [BitVec(f"x{i}",8) for i in range(15)]

opcode = [
10, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 11, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 81, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 36, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 54, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 65, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 7, 0, 0, 0, 63, 0, 0, 0, 7, 0, 0, 0, 52, 0, 0, 0, 7, 0, 0, 0, 50, 0, 0, 0, 7, 0, 0, 0, 114, 0, 0, 0, 7, 0, 0, 0, 51, 0, 0, 0, 7, 0, 0, 0, 24, 0, 0, 0, 7, 0, 0, 0, 167, 255, 255, 255, 7, 0, 0, 0, 49, 0, 0, 0, 7, 0, 0, 0, 241, 255, 255, 255, 7, 0, 0, 0, 40, 0, 0, 0, 7, 0, 0, 0, 132, 255, 255, 255, 7, 0, 0, 0, 193, 255, 255, 255, 7, 0, 0, 0, 30, 0, 0, 0, 7, 0, 0, 0, 122, 0, 0, 0, 0
]

v9 = 0
Str = [0] * 1000
end = 114
v8 = 0
v7 = 0
v6 = 0
v5 = 0
v4 = 0
while v9 < end:
op = opcode[v9 * 4]
if op == 1:
Str[v6+100]=v4
v9 += 1
v6 += 1
v8 += 1
elif op == 2:
v4 = Str[v8] + opcode[(v9 + 1)*4]
v9 += 2
elif op == 3:
v4 = Str[v8] - opcode[(v9 + 1)*4]
v9 += 2
elif op == 4:
v4 = opcode[(v9 + 1)*4] ^ Str[v8]
v9 += 2
elif op == 5:
v4 = opcode[(v9 + 1)*4] * Str[v8]
v9 += 2
elif op == 6:
v9 += 1
elif op == 7:
print(Str[v7 + 100])
s.add(Str[v7 + 100] == opcode[(v9 + 1)*4])
v9 += 2
v7 += 1
elif op == 8:
Str[v5] = v4
v9 += 1
v5 += 1
elif op == 10:
print("Read input")
for i, v in enumerate(x):
Str[i] = v
v9 += 1
elif op == 11:
v4 = Str[v8] - 1
v9 += 1
elif op == 12:
v4 = Str[v8] + 1
v9 += 1
else:
print(f"unknown insn {op}")


print(s.check())
if s.check():
m = s.model()
for i in x:
val = m.eval(i, model_completion=True)
print(chr(val.as_long()), end="")


# Read input
# (16 ^ x0) - 5
# 3*(32 ^ x1)
# x2 - 2 - 1
# 4 ^ x3 + 1
# 3*x4 - 33
# x5 - 1 - 1
# (9 ^ x6) - 32
# 36 ^ x7 + 81
# x8 + 1 - 1
# 2*x9 + 37
# 65 ^ x10 + 54
# 1*(x11 + 32)
# 3*x12 + 37
# (9 ^ x13) - 32
# x14 + 65 + 1
# sat
# 757515121f3d478 <-- flag

3. [buuctf]firmware

​ 这个是个路由器的固件题,有点misc的味道。下载题目之后是一个.bin文件,丢虚拟机用binwalk看看是个什么玩意。看起来有不少东西
img

​ 使用binwalk直接将文件拆开:

1
binwalk -e 51475f91-7b90-41dd-81a3-8b82df4f29d0.bin 

img

整出来之后发现文件夹当中有一个.squashfs的文件,这个文件是一种只读的压缩文件系统。可以使用firmware-mod-kit工具来获取解压缩,这里可以直接去github中将源码下载下来,然后丢到虚拟机,在src文件夹make一下,就可以使用了:

1
unsquashfs_all.sh 120200.squashfs

然后就能够发现多了一个root-1的文件夹,最后在这个文件夹当中/tmp找到了一个backdoor的文件,复制出来丢die看看有没有壳,发现有一个upx,直接工具脱一下壳,然后丢ida里面,字符串查找就能够发现有一个邮箱的字符串了:
img

接着直接交叉引用到调用的位置,可以发现这个v3其实就是端口号,如果传入的字串有自己的端口就使用自己的端口,没有的话就使用36667端口

img

最后直接将echo.bytehost51.com:36667进行md5提交即可。

4. [2019红帽杯]xx

​ 直接丢IDA看看反汇编,main函数看起来非常的乱。开头就是一个输入长度的判断,输入如果不等于19直接退出:

image-20251111112427360

然后直接动调看看关键位置:

img

第一个关键位置就是这里,这里的v30其实是输入的前4字节,然后传入了这个sub_140001AB0函数当中,进去看了一下发现是一个xxtea加密,然后key就是输入的前四个字节了:

img

​ 继续往下分析:
img

这里很明显就是一个乱序了,接着继续往下:

img

最后这里很明显能看出来是一个异或的加密,而且加密过程是这样的,如果索引值 / 3大于0的话就和从0开始的字符进行逐个异或加密,这里解密的时候需要倒过来。

​ 最后这一块就是比较了:

img

这里的几块变量的内存都是连续的所以这个就是密文。那么大致的逻辑就是:输入 –> xxtea –> 乱序 —> 异或 –> 比较可以直接写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
139
140
141
142
143
144
145
#pragma once
#include <iostream>
#include <stdio.h>
#include <vector>


void decrypt_xor(unsigned char* enc)
{
for (int i = 23; i > 0; i--)
{
int j = 0;
if (i / 3 > 0)
{
do {
enc[i] ^= enc[j++];
} while (j < i / 3);
}
}
}

void sort(unsigned char* enc)
{
std::vector<unsigned char> v;
for (int i = 0; i < 24; i++)
{
v.push_back(enc[i]);
}

char index[] = { 2,0,3,1,6,4,7,5,10,8,11,9,14,12,15,13,18,16,19,17,22,20,23,21 };
for (int i = 0; i < 24; i++)
{
enc[index[i]] = v[i];
}

}

uint8_t* tea_decrypt(const uint8_t* cipher, uint64_t cipherlen, const uint8_t* key) {
if (!cipherlen || cipherlen < 4) return NULL;

// 去掉可能的结尾 0
uint64_t usable = cipherlen;
if (cipher[cipherlen - 1] == 0 && (cipherlen % 4) == 1) {
usable = cipherlen - 1;
}
if (usable % 4 != 0) return NULL;

size_t n = (size_t)(usable / 4);

// 组装 v
uint32_t* v = (uint32_t*)calloc(n, sizeof(uint32_t));
if (!v) return NULL;
for (uint64_t i = 0; i < usable; i++) {
size_t idx = (size_t)(i >> 2);
size_t shift = (size_t)((i & 3) * 8);
v[idx] |= ((uint32_t)cipher[i]) << shift;
}

// 组装 key
uint32_t k[4];
for (int j = 0; j < 4; j++) {
int base = j * 4;
k[j] = (uint32_t)key[base]
| ((uint32_t)key[base + 1] << 8)
| ((uint32_t)key[base + 2] << 16)
| ((uint32_t)key[base + 3] << 24);
}

// XXTEA 解密核心
if (n > 1) {
uint32_t delta = 0x9E3779B9;
size_t rounds = 6 + 52 / n;
uint32_t sum = (uint32_t)(rounds * delta);
uint32_t z, y;
while (sum) {
uint32_t e = (sum >> 2) & 3;
y = v[0];
for (size_t p = n - 1; p > 0; p--) {
z = v[p - 1];
v[p] -= (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))
^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z));
y = v[p];
}
z = v[n - 1];
v[0] -= (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))
^ ((sum ^ y) + (k[(0 & 3) ^ e] ^ z));
sum -= delta;
}
}

// 恢复原始长度
uint32_t orig_len = v[n - 1];
uint64_t max_plain = (uint64_t)((n - 1) * 4);
if ((uint64_t)orig_len > max_plain) {
free(v);
return NULL;
}

// 导出明文
uint8_t* out = (uint8_t*)malloc((uint64_t)orig_len);
if (!out) {
free(v);
return NULL;
}
for (uint64_t i = 0; i < (uint64_t)orig_len; i++) {
out[i] = (uint8_t)(v[i >> 2] >> (8 * (i & 3)));
}


free(v);
return out;
}



void decrypt()
{
unsigned char enc[] =
{
0xCE, 0xBC, 0x40, 0x6B, 0x7C, 0x3A, 0x95, 0xC0, 0xEF, 0x9B,
0x20, 0x20, 0x91, 0xF7, 0x02, 0x35, 0x23, 0x18, 0x02, 0xC8,
0xE7, 0x56, 0x56, 0xFA
};

// 解密xor
decrypt_xor(enc);

printf("[*] : xor finish \r\n");

// 乱序
sort(enc);

printf("[*] : sort success\r\n");

// xxtea
char key[16] = { 'f','l','a','g'};

size_t size = 0;
uint8_t* decrpyt = tea_decrypt((const uint8_t*)enc,24,(uint8_t*)key);
printf("[*] : xxtea success\r\n");


for (int i = 0; i < 19; i++)
printf("%c", decrpyt[i]);
}
// flag{CXX_and_++tea}