web
密码在哪里呢
反弹shell,然后进到里面之后发现/root不让访问(没权限),同时没sudo命令.
但是api给了一个读文件的程序,这个程序是root的.
所以可以让root程序去读取flag.
抢课
1 | public void CutDown(){ |
竞态条件利用.
就是说程序有一个什么什么锁,多线程同时访问就会爆掉…
然后就能拿到flag,但是你要再做一个行为才会回显flag,因为没回显被卡了半个月.
弱得离谱的签名 JWT
JWT密钥爆破.
misc
stream2
是lfsr文件流…
通过文件流能恢复出另一张图片,然后两张图片抑或(可以先转成png然后再抑或)
得到第三张图片就是答案(红色和绿色的高位).
一般情况下LFSR隐写的压缩包都叫rar,而且必须用winrar打开,而且能在rar末尾看到隐写的那张图片的名称和dat的类别,Flu看到的的叫Service (Quick Open) block
no more taowa
神人题目…
ref
pcapng
GPT神力,直接把flag给出来了…
重点关注python解码rot13和解压缩zlib的代码:1
2
3
4
5
6
7
8
9
10import codecs, base64, zlib
rot13_text = """-----ORTVA PREGVSVPNGR-----
rWjSDZfWtQNZaruOCmUIPGdNRlGcB/DtvUtGql/aCM/5qei4IHMhpDv0rVRZ37Oo
IaOHMsAVlrEsrNxBvN==
-----RAQ PREGVSVPNGR-----"""
decoded = codecs.decode(rot13_text, 'rot_13')
b64 = decoded.splitlines()[1] # 取中间那行 base64
data = base64.b64decode(b64)
print(zlib.decompress(data).decode())
Reverse
最终审计
给定代码函数名称极其混乱,然后还有三个没用过的函数进行混淆.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
uint8_t box[256] = {
101, 27, 192, 94, 58, 31, 23, 165, 105, 244,
153, 151, 71, 160, 194, 152, 89, 73, 95, 119,
130, 51, 197, 186, 212, 65, 25, 219, 148, 235,
86, 164, 138, 9, 143, 111, 158, 171, 246, 18,
55, 134, 250, 245, 233, 248, 3, 222, 90, 204,
45, 39, 96, 54, 10, 229, 154, 48, 181, 145,
166, 238, 126, 97, 38, 109, 150, 70, 207, 216,
146, 170, 92, 228, 60, 177, 247, 182, 180, 13,
139, 50, 74, 22, 118, 26, 53, 77, 185, 61,
203, 230, 172, 123, 218, 116, 68, 178, 135, 190,
43, 37, 173, 36, 208, 11, 40, 24, 14, 240,
84, 8, 52, 239, 83, 191, 193, 196, 122, 210,
6, 140, 254, 242, 69, 110, 132, 195, 17, 253,
20, 115, 56, 124, 1, 217, 213, 241, 252, 120,
75, 224, 108, 30, 201, 78, 85, 237, 211, 188,
255, 33, 44, 128, 141, 42, 19, 87, 167, 214,
2, 93, 174, 249, 121, 100, 209, 67, 161, 156,
162, 205, 21, 234, 176, 206, 49, 175, 99, 103,
114, 163, 113, 80, 59, 225, 76, 183, 159, 112,
5, 251, 184, 79, 149, 117, 35, 168, 236, 200,
133, 88, 63, 29, 102, 227, 98, 16, 4, 0,
81, 155, 198, 34, 142, 202, 12, 131, 136, 157,
57, 189, 107, 129, 199, 64, 47, 41, 179, 104,
7, 106, 91, 137, 215, 72, 232, 187, 15, 231,
144, 147, 28, 66, 221, 226, 127, 243, 46, 82,
62, 169, 125, 32, 220, 223
};
uint8_t dic(uint8_t a, uint8_t b){
uint8_t i;
uint8_t temp;
uint8_t result[8];
uint8_t sum = 0;
result[0] = a;
for (i = 1; i < 8; i++)
{
temp = result[i - 1];
temp = temp >> 7;
if (temp == 1)
{
result[i] = result[i - 1] << 1 ^ 0x1b;
}
else
{
result[i] = result[i - 1] << 1;
}
}
for (i = 0; i < 8; i++) {
temp = b << i & 0x80;
if (temp == 0x80)
{
sum ^= result[7 - i];
}
}
return sum;
}
void inv(uint8_t K[16], uint8_t k[11][16])
{
uint8_t RC[10];
uint32_t v4[10];
RC[0] = 1;
int i;
for (i = 1; i < 10; i++)
RC[i] = dic(0x02, RC[i - 1]);
for (i = 0; i < 16; i++)
k[0][i] = K[i];
for (i = 1; i < 11; i++)
{
k[i][0] = k[i - 1][0] ^ box[k[i - 1][13]] ^ RC[10 - i];
k[i][1] = k[i - 1][1] ^ box[k[i - 1][14]];
k[i][2] = k[i - 1][2] ^ box[k[i - 1][15]];
k[i][3] = k[i - 1][3] ^ box[k[i - 1][12]];
k[i][4] = k[i - 1][4] ^ k[i][0];
k[i][5] = k[i - 1][5] ^ k[i][1];
k[i][6] = k[i - 1][6] ^ k[i][2];
k[i][7] = k[i - 1][7] ^ k[i][3];
k[i][8] = k[i - 1][8] ^ k[i][4];
k[i][9] = k[i - 1][9] ^ k[i][5];
k[i][10] = k[i - 1][10] ^ k[i][6];
k[i][11] = k[i - 1][11] ^ k[i][7];
k[i][12] = k[i - 1][12] ^ k[i][8];
k[i][13] = k[i - 1][13] ^ k[i][9];
k[i][14] = k[i - 1][14] ^ k[i][10];
k[i][15] = k[i - 1][15] ^ k[i][11];
}
}
void xxor(uint8_t* a, uint8_t* IIIlIIIIIllllIIl) {
for (int i = 0; i < 16; i++)
a[i] ^= IIIlIIIIIllllIIl[i];
}
void pprj(uint8_t* input) {
for (int i = 0; i < 16; i++)
input[i] = box[input[i]];
}
void depprj(uint8_t* input){
for (int i = 0; i < 16; i++)
for(int j=0;j<256;++j){
if(box[j]==input[i]){
input[i]=j;
break;
}
}
}
void rrmm(uint8_t* a) {
uint8_t b[16];
b[0] = a[0]; b[4] = a[4]; b[8] = a[8]; b[12] = a[12];
b[1] = a[5]; b[5] = a[9]; b[9] = a[13]; b[13] = a[1];
b[2] = a[10]; b[6] = a[14]; b[10] = a[2]; b[14] = a[6];
b[3] = a[15]; b[7] = a[3]; b[11] = a[7]; b[15] = a[11];
for (int i = 0; i < 16; i++)
a[i] = b[i];
}
void derrmm(uint8_t* a) {
uint8_t b[16];
b[0] = a[0];
b[1] = a[13];
b[2] = a[10];
b[3] = a[7];
b[4] = a[4];
b[5] = a[1];
b[6] = a[14];
b[7] = a[11];
b[8] = a[8];
b[9] = a[5];
b[10] = a[2];
b[11] = a[15];
b[12] = a[12];
b[13] = a[9];
b[14] = a[6];
b[15] = a[3];
for (int i = 0; i < 16; i++) {
a[i] = b[i];
}
}
void dict(uint8_t* a) {
uint8_t b[16];
b[0] = dic(0x02, a[0]) ^ dic(0x03, a[1]) ^ a[2] ^ a[3];
b[1] = dic(0x02, a[1]) ^ dic(0x03, a[2]) ^ a[3] ^ a[0];
b[2] = dic(0x02, a[2]) ^ dic(0x03, a[3]) ^ a[0] ^ a[1];
b[3] = dic(0x02, a[3]) ^ dic(0x03, a[0]) ^ a[1] ^ a[2];
b[4] = dic(0x02, a[4]) ^ dic(0x03, a[5]) ^ a[6] ^ a[7];
b[5] = dic(0x02, a[5]) ^ dic(0x03, a[6]) ^ a[7] ^ a[4];
b[6] = dic(0x02, a[6]) ^ dic(0x03, a[7]) ^ a[4] ^ a[5];
b[7] = dic(0x02, a[7]) ^ dic(0x03, a[4]) ^ a[5] ^ a[6];
b[8] = dic(0x02, a[8]) ^ dic(0x03, a[9]) ^ a[10] ^ a[11];
b[9] = dic(0x02, a[9]) ^ dic(0x03, a[10]) ^ a[11] ^ a[8];
b[10] = dic(0x02, a[10]) ^ dic(0x03, a[11]) ^ a[8] ^ a[9];
b[11] = dic(0x02, a[11]) ^ dic(0x03, a[8]) ^ a[9] ^ a[10];
b[12] = dic(0x02, a[12]) ^ dic(0x03, a[13]) ^ a[14] ^ a[15];
b[13] = dic(0x02, a[13]) ^ dic(0x03, a[14]) ^ a[15] ^ a[12];
b[14] = dic(0x02, a[14]) ^ dic(0x03, a[15]) ^ a[12] ^ a[13];
b[15] = dic(0x02, a[15]) ^ dic(0x03, a[12]) ^ a[13] ^ a[14];
for (int i = 0; i < 16; i++)
a[i] = b[i];
}
void dedict(uint8_t* a) {
uint8_t b[16];
// 第一组: a[0], a[1], a[2], a[3]
b[0] = dic(0x0e, a[0]) ^ dic(0x0b, a[1]) ^ dic(0x0d, a[2]) ^ dic(0x09, a[3]);
b[1] = dic(0x09, a[0]) ^ dic(0x0e, a[1]) ^ dic(0x0b, a[2]) ^ dic(0x0d, a[3]);
b[2] = dic(0x0d, a[0]) ^ dic(0x09, a[1]) ^ dic(0x0e, a[2]) ^ dic(0x0b, a[3]);
b[3] = dic(0x0b, a[0]) ^ dic(0x0d, a[1]) ^ dic(0x09, a[2]) ^ dic(0x0e, a[3]);
// 第二组: a[4], a[5], a[6], a[7]
b[4] = dic(0x0e, a[4]) ^ dic(0x0b, a[5]) ^ dic(0x0d, a[6]) ^ dic(0x09, a[7]);
b[5] = dic(0x09, a[4]) ^ dic(0x0e, a[5]) ^ dic(0x0b, a[6]) ^ dic(0x0d, a[7]);
b[6] = dic(0x0d, a[4]) ^ dic(0x09, a[5]) ^ dic(0x0e, a[6]) ^ dic(0x0b, a[7]);
b[7] = dic(0x0b, a[4]) ^ dic(0x0d, a[5]) ^ dic(0x09, a[6]) ^ dic(0x0e, a[7]);
// 第三组: a[8], a[9], a[10], a[11]
b[8] = dic(0x0e, a[8]) ^ dic(0x0b, a[9]) ^ dic(0x0d, a[10]) ^ dic(0x09, a[11]);
b[9] = dic(0x09, a[8]) ^ dic(0x0e, a[9]) ^ dic(0x0b, a[10]) ^ dic(0x0d, a[11]);
b[10] = dic(0x0d, a[8]) ^ dic(0x09, a[9]) ^ dic(0x0e, a[10]) ^ dic(0x0b, a[11]);
b[11] = dic(0x0b, a[8]) ^ dic(0x0d, a[9]) ^ dic(0x09, a[10]) ^ dic(0x0e, a[11]);
// 第四组: a[12], a[13], a[14], a[15]
b[12] = dic(0x0e, a[12]) ^ dic(0x0b, a[13]) ^ dic(0x0d, a[14]) ^ dic(0x09, a[15]);
b[13] = dic(0x09, a[12]) ^ dic(0x0e, a[13]) ^ dic(0x0b, a[14]) ^ dic(0x0d, a[15]);
b[14] = dic(0x0d, a[12]) ^ dic(0x09, a[13]) ^ dic(0x0e, a[14]) ^ dic(0x0b, a[15]);
b[15] = dic(0x0b, a[12]) ^ dic(0x0d, a[13]) ^ dic(0x09, a[14]) ^ dic(0x0e, a[15]);
for (int i = 0; i < 16; i++) {
a[i] = b[i];
}
}
void ill(uint8_t tmp[16], uint8_t rres[16], uint8_t k[11][16], int Round){
int i, round;
for (i = 0; i < 16; i++) {
rres[i] = tmp[i];
}
xxor(rres, k[0]);
for (round = 1; round < Round; round++) {
dict(rres);
pprj(rres);
rrmm(rres);
xxor(rres, k[round]);
}
pprj(rres);
rrmm(rres);
xxor(rres, k[Round]);
}
void deill(uint8_t tmp[16], uint8_t rres[16], uint8_t k[11][16], int Round) {
int i, round;
// 将输入密文复制到 rres
for (i = 0; i < 16; i++) {
rres[i] = tmp[i];
}
// 反转最后一轮的异或操作
xxor(rres, k[Round]);
// 反转最后一轮的 rrmm 和 pprj 操作(注意顺序相反)
derrrm(rres);
depprj(rres);
// 从 Round-1 到 1 轮,逆向处理每一轮
for (round = Round-1; round >= 1; round--) {
xxor(rres, k[round]); // 反转该轮的异或
derrrm(rres); // 反转 rrmm
depprj(rres); // 反转 pprj
dedict(rres); // 反转 dict
}
// 反转初始异或
xxor(rres, k[0]);
}
int main(){
int i = 0;
uint8_t cnt[11][16];
uint8_t chk[16] = { 218,14,205,50,168,89,88,62,25,222,96,18,18,129,198,36};
uint8_t rres[17];
uint8_t tmp[17];
uint8_t arr[16] = {
0x5A, 0x0D, 0x9D, 0x04, 0x60, 0xAE, 0x05, 0xBB, 0x85, 0x6B,
0x13, 0xB5, 0x73, 0xCF, 0x3D, 0x1E
};
for (int a = 0; a < 16; a++) {
tmp[a] = getchar();
rres[a] = 0;
}
tmp[16] = 0;
for(int a=0;a<11;a++){
for(int b=0;b<16;b++){
cnt[a][b] = 0;
}
}
inv(arr, cnt);
ill(tmp,rres,cnt,10);
for(i=0;i<16;i++){
if (rres[i] != chk[i]) {
printf("Error!");
return 0;
}
}
printf("Right!");
printf("Your flag: flag{%16s}", tmp);
return 0;
}
手动把变量名换成一些稍微熟悉的名字之后,再丢给AI说是自己实现了一个AES加密,S盒自定义的,而且密钥就在main函数里.
把AES拆成一段一段的函数丢给ds分别写解密函数即可.(就是上面的结果…)
GoodCoding
把所有神秘函数综合在一起…
那种纯抑或找字符串数据区在哪的没意思,真的.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
233fastio::IN fin;
fastio::OUT fout;
int __cdecl sub_401100(_BYTE *a1)
{
int result; // eax
result = 1;
*a1 = 102;
return result;
}
int __cdecl sub_401120(_BYTE * a1)
{
int result; // eax
result = 1;
*(a1 + 1) = 108;
return result;
}
int __cdecl sub_401140(_BYTE * a1)
{
int n2; // eax
n2 = 2;
*(a1 + 2) = 97;
return n2;
}
int __cdecl sub_401160(_BYTE * a1)
{
int result; // eax
result = 1;
*(a1 + 3) = 103;
return result;
}
int __cdecl sub_401180(_BYTE * a1)
{
int n4; // eax
n4 = 4;
*(_BYTE *)(a1 + 4) = 123;
return n4;
}
int __cdecl sub_4011A0(_BYTE * a1)
{
int result; // eax
result = 1;
*(a1 + 44) = 125;
return result;
}
unsigned char Buf2_[]={
0x00,0x8F,0xDE,0x51,0x16,0x27,0xAD,0x35,
0xB4,0xE1,0x49,0x6D,0x1A,0x58,0xE5,0xA4,
0xCF,0x1F,0x56,0xAD,0x5D,0x52,0x32,0xD8,
0x28,0x15,0x76,0x14,0xF2,0x1C,0xBD,0xA6,
0x03,0xD4,0x1C,0x40,0x5D,0x1C,0xFB,0xE1,
0xEB,0x30,0xF5,0x29,0xFE,0x00,0x00,0x00
};
// Buf2_ db 0 ; DATA XREF: _main+139↑o
// .data:0040402D db 8Fh
// .data:0040402E db 0DEh
// .data:0040402F db 51h ; Q
// .data:00404030 db 16h
// .data:00404031 db 27h ; '
// .data:00404032 db 0ADh
// .data:00404033 db 35h ; 5
// .data:00404034 db 0B4h
// .data:00404035 db 0E1h
// .data:00404036 db 49h ; I
// .data:00404037 db 6Dh ; m
// .data:00404038 db 1Ah
// .data:00404039 db 58h ; X
// .data:0040403A db 0E5h
// .data:0040403B db 0A4h
// .data:0040403C db 0CFh
// .data:0040403D db 1Fh
// .data:0040403E db 56h ; V
// .data:0040403F db 0ADh
// .data:00404040 db 5Dh ; ]
// .data:00404041 db 52h ; R
// .data:00404042 db 32h ; 2
// .data:00404043 db 0D8h
// .data:00404044 db 28h ; (
// .data:00404045 db 15h
// .data:00404046 db 76h ; v
// .data:00404047 db 14h
// .data:00404048 db 0F2h
// .data:00404049 db 1Ch
// .data:0040404A db 0BDh
// .data:0040404B db 0A6h
// .data:0040404C db 3
// .data:0040404D db 0D4h
// .data:0040404E db 1Ch
// .data:0040404F db 40h ; @
// .data:00404050 db 5Dh ; ]
// .data:00404051 db 1Ch
// .data:00404052 db 0FBh
// .data:00404053 db 0E1h
// .data:00404054 db 0EBh
// .data:00404055 db 30h ; 0
// .data:00404056 db 0F5h
// .data:00404057 db 29h ; )
// .data:00404058 db 0FEh
// .data:00404059 db 0
// .data:0040405A db 0
// .data:0040405B db 0
char aFppv[]="`fppv``";
int sub_4011E0()
{
int i; // [esp+0h] [ebp-4h]
for ( i = 0; i < 7; ++i )
aFppv[i] ^= 0x33u; // "`fppv``"
// "`fppv``"
return printf("%s", aFppv);
}
unsigned int __cdecl sub_401670(char *Buf1,unsigned char *a2)
{
unsigned int j_1; // eax
unsigned int v3; // [esp+4h] [ebp-23Ch]
unsigned int j_2; // [esp+10h] [ebp-230h]
int v5; // [esp+18h] [ebp-228h]
int v6; // [esp+1Ch] [ebp-224h]
unsigned int j; // [esp+20h] [ebp-220h]
int v8; // [esp+24h] [ebp-21Ch]
int n256; // [esp+30h] [ebp-210h]
int i; // [esp+34h] [ebp-20Ch]
unsigned __int8 v11; // [esp+3Ah] [ebp-206h]
unsigned __int8 v12; // [esp+3Bh] [ebp-205h]
_BYTE v13[256]; // [esp+3Ch] [ebp-204h] BYREF
_BYTE v14[256]; // [esp+13Ch] [ebp-104h] BYREF
memset(v14, 0, sizeof(v14));
memset(v13, 0, sizeof(v13));
j_2=0x2D;
// j_2 = strlen(a2);
v3 = strlen(Buf1);
for ( i = 0; i < 256; ++i )
{
v14[i] = i;
v13[i] = Buf1[i % v3];
}
n256 = 0;
v6 = 0;
while ( n256 < 256 )
{
v11 = v14[n256];
v6 = (v6 + v11 + (char)v13[n256]) % 256;
v14[n256] = v14[v6];
v14[v6] = v11;
++n256;
}
v8 = 0;
v5 = 0;
for ( j = 0; ; ++j )
{
j_1 = j;
if ( j >= j_2 )
break;
v8 = (v8 + 1) % 256;
v12 = v14[v8];
v5 = (v5 + v12) % 256;
v14[v8] = v14[v5];
v14[v5] = v12;
a2[j] ^= v14[(unsigned __int8)(v14[v8] + v12)];
}
return j_1;
}
char byte_404020[]={
0x01,0x07,0x0C,0x75,0x14,
0x12,0x14,0x1C,0x1B,0x74,
0x00,0x00
};
// .data:00404020 ; char byte_404020[12]
// .data:00404020 byte_404020 db 1 ; DATA XREF: sub_401230+1F↑r
// .data:00404020 ; sub_401230+2C↑w ...
// .data:00404021 db 7
// .data:00404022 db 0Ch
// .data:00404023 db 75h ; u
// .data:00404024 db 14h
// .data:00404025 db 12h
// .data:00404026 db 14h
// .data:00404027 db 1Ch
// .data:00404028 db 1Bh
// .data:00404029 db 74h ; t
// .data:0040402A db 0
// .data:0040402B db 0
int sub_401230()
{
int i; // [esp+0h] [ebp-4h]
for ( i = 0; i < 10; ++i )
byte_404020[i] ^= 0x55u;
return printf("%s", byte_404020);
}
int __cdecl sub_401450(_BYTE * a1){
sub_401100(a1);
sub_401120(a1);
sub_401140(a1);
sub_401160(a1);
sub_401180(a1);
return sub_4011A0(a1);
}
int mian(char* Buf1,char* Buf1_1){
sub_401450(Buf1);
for(int i=0;i<48;++i){
Buf1[i]=Buf2_[i];
}
sub_401670(Buf1_1, Buf2_);
sub_401450(Buf1);
for(int i=0;i<=46;++i){
fout<<(char)Buf2_[i];
}enter
if ( !memcmp(Buf1, &Buf2_, 0x2Du) ){
si;
sub_401450(Buf1);
sub_401450(Buf1);
sub_4011E0();//success
}
else{
si;si;
}
sub_401450(Buf1);
sub_401450(Buf1);
return 0;
}
char Buf1_1[]="Simple.Game.of.Reverse,+SO+EASY!";
char Buf[1000];
long long n,m,res;
//#define NaraFluorine
int main(){
mian(Buf,Buf1_1);
return 0;
}
MediumPyc
给了一个神秘pyc编译的exe文件.首先使用pyinstxtractor解包:1
python pyinstxtractor.py MediumPyc.exe
然后使用编译好的pycdc(尝试还原源代码)或者pycdas(分析字节码)尝试还原:1
2pycdc.exe fast.pyc
pycdas.exe fast.pyc
(这俩不会生成任何文件,输出都是直接打在cmd里面的)
回到这道题,补全魔数之后发现还是无法反编译,所以我们分析字节码.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
99D:\SucksFluorine\download\MediumPyc>pycdc.exe fast.pyc
CreateObject: Got unsupported type 0x0
Error loading file fast.pyc: std::bad_cast
D:\SucksFluorine\download\MediumPyc>pycdas.exe fast.pyc
fast.pyc (Python 3.9)
[Code]
File Name: fast.py
Object Name: <module>
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 0
Stack Size: 5
Flags: 0x00000040 (CO_NOFREE)
[Names]
'ans'
'enumerate'
'i'
'v'
'print'
'join'
[Var Names]
[Free Vars]
[Cell Vars]
[Constants]
...太长了省略
[Code]
File Name: fast.py
Object Name: <genexpr>
Arg Count: 1
Pos Only Arg Count: 0
KW Only Arg Count: 0
Locals: 2
Stack Size: 3
Flags: 0x00000063 (CO_OPTIMIZED | CO_NEWLOCALS | CO_GENERATOR | CO_NOFREE)
[Names]
'chr'
[Var Names]
'.0'
'i'
[Free Vars]
[Cell Vars]
[Constants]
None
[Disassembly]
0 LOAD_FAST 0: .0
2 FOR_ITER 14 (to 18)
4 STORE_FAST 1: i
6 LOAD_GLOBAL 0: chr
8 LOAD_FAST 1: i
10 CALL_FUNCTION 1
12 YIELD_VALUE
14 POP_TOP
16 JUMP_ABSOLUTE 2
18 LOAD_CONST 0: None
20 RETURN_VALUE
'<genexpr>'
None
[Disassembly]
0 LOAD_CONST 0: 1
2 LOAD_CONST 1: 0
4 COMPARE_OP 2 (==)
6 POP_JUMP_IF_TRUE 12
8 LOAD_ASSERTION_ERROR
10 RAISE_VARARGS 1
12 BUILD_LIST 0
14 LOAD_CONST 2: (121, 90, 67, 88, 67, 94, 81, 122, 83, 27, 68, 89, 94, 75, 70, 70, 79, 88, 11, 23, 99, 68, 94, 79, 88, 30, 89, 94, 67, 68, 77, 87)
16 LIST_EXTEND 1
18 STORE_NAME 0: ans
20 LOAD_NAME 1: enumerate
22 LOAD_NAME 0: ans
24 CALL_FUNCTION 1
26 GET_ITER
28 FOR_ITER 20 (to 50)
30 UNPACK_SEQUENCE 2
32 STORE_NAME 2: i
34 STORE_NAME 3: v
36 LOAD_NAME 3: v
38 LOAD_CONST 3: 42
40 BINARY_XOR
42 LOAD_NAME 0: ans
44 LOAD_NAME 2: i
46 STORE_SUBSCR
48 JUMP_ABSOLUTE 28
50 LOAD_NAME 4: print
52 LOAD_CONST 4: ''
54 LOAD_METHOD 5: join
56 LOAD_CONST 5: <CODE> <genexpr>
58 LOAD_CONST 6: '<genexpr>'
60 MAKE_FUNCTION 0
62 LOAD_NAME 0: ans
64 GET_ITER
66 CALL_FUNCTION 1
68 CALL_METHOD 1
70 CALL_FUNCTION 1
72 POP_TOP
74 LOAD_CONST 7: None
76 RETURN_VALUE
发现一串神秘字符串很可疑,打印出来加个偏移发现是集中抑或某个神秘值,flag到手.
Ex
首先pycdc不是万能的
再者pycdc如果想要反编译成python的话,就需要字节码是这些
如果你不想让你的pyc被pycdc简单的解出来的话,添加这些不支持的opcode,那解到一半的时候就会报错了,比如涉及到控制流转移的处理起来就比较麻烦.
或者说作者还没更新…
number game
答案是这堆质数的乘积.主要讲为什么代码会这么写: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
72void __cdecl f1(){
v1 = (v1 + 1) % 2;
next();
}
void __cdecl f2(){
v2 = (v2 + 1) % 3;
next();
}
void __cdecl f3(){
v3 = (v3 + 1) % 5;
next();
}
void __cdecl f4(){
v4 = (v4 + 1) % 7;
next();
}
void __cdecl f5(){
v5 = (v5 + 1) % 11;
next();
}
void __cdecl f6(){
v6 = (v6 + 1) % 13;
next();
}
void __cdecl f7(){
v7 = (v7 + 1) % 17;
next();
}
void __cdecl f8(){
v8 = (v8 + 1) % 19;
next();
}
void __cdecl f9(){
v9 = (v9 + 1) % 23;
next();
}
void __cdecl f10(){
v10 = (v10 + 1) % 29;
next();
}
void __cdecl f11(){
v11 = (v11 + 1) % 31;
next();
}
void __cdecl f12(){
v12 = (v12 + 1) % 37;
next();
}
void __cdecl next(){
funcInd = (funcInd + 1) % 12;
funcTable[funcInd]();
}
void __cdecl func_hook(){
if ( func_hook(void)::flag ){
free(func_hook(void)::flag);
func_hook(void)::flag = 0LL;
}else{
func_hook(void)::flag = malloc(4uLL);
f_backup();
}
}
int __cdecl starter(int start){
FUNC *funcTable; // rax
funcInd = start;
f_backup = funcTable[start];
funcTable[start] = func_hook;
funcTable[start]();
funcTable = funcTable;
funcTable[start] = f_backup;
return (int)funcTable;
}
讲讲为什么上面的代码在调用starter的时候不会死循环:
上面首先生成了一个函数表,里面包含所有的函数.
然后在调用的时候由于functable[start]被修改为f_backup,第二层的循环会被停止.
所以上面代码并不会死循环.
Crypto
d高位coppersmith
1 | def get_full_p(p_high, n,d_high,kbits): |
d绝对值很小的coppersmith
1 | from Crypto.Util.number import * |
加密脚本很短,d很小但是是负数.
hint:注意 $e-(ed-1)/phi(n)$ 的尺寸.
赛时一直被模意义下的负数勾走了全部注意力,一直在想d实际上是一个很大但是和phi有一丢丢差距的d,然后一直在想怎么搞,而不是直接把d当成就是负数带进去.
设 $s=p+q$ ,易知
因为d不大,所以我们 模e 重写这个式子:
d是负数,乘个负号进去:
其中k和s相对e很小.
于是有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
71from sage.all import *
from Crypto.Util.number import long_to_bytes
import itertools
def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()
if isinstance(f, Polynomial):
x, = polygens(f.base_ring(), f.variable_name(), 1)
f = f(x)
R = f.base_ring()
N = R.cardinality()
f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)
G = Sequence([], f.parent())
for i in range(m+1):
base = N**(m-i) * f**i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(pow, f.variables(), shifts))
G.append(g)
B, monomials = G.coefficient_matrix()
monomials = vector(monomials)
factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)
print(B.dimensions())
B = B.dense_matrix().LLL()
B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, QQ(1)/factor)
H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B*monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots
return []
n = 15425573671310522242443233746310042356010853473818779398996082170048567920118903681647987050437827476913593832140428830079145356506759781679910740377921852505076061481509359602427721750479160787169664264051601089514008111429884419691138271831739082569598985466474557813544198040214986436278455435986026770676924475450399939298815488163616243041363787783962074104279847511568615583301057627557564500487351753188613907753680349679738865135580863009439093285204444919006821721593430250207889013892442639687535905431874786143108315990529242269940558109859388267058928250425298772293217740622765655823152565113123923668763
e = 6327896556094253658091659747817429016856309408223961473436189764962070708842237303975014061823644906347752556361281697369789352104420663408346153770487875230284945025757165322724674918235859941960496109640341557214144096200220842694000276854218573600170424950171069117292606035040028300347830463121907243469437544334463474385688805125872252275430155205268386655868804799918703077669963435094113657930181088370605609979145651653040814955685490046815107715494698297046996041104363384752763054510338109663193238262157980276143877128191648541907859079950162479302046677570441450717578777789735482678087230318134288988821
c = 5475801009485342952728196694456030407918197307336985418171034018093532133078620703384196948359167250839791850276381898952700516800696564114585589757825210269752869192009824899469495374079950070566028398387345642338708146308408285379972269045531178705443124429741740620536581303639550183992924966455905800211495026220301107141605352717625377246526111322887069149636557059363604462796789995221078881649570057345061701830406867272793448661252769484093736788748691697722275957340631743777620368716400287850649194356828671277924972545427720251564065359140554273211854162576253356785240336290941903470838097920938027634391
P, (kx, sx) = PolynomialRing(Zmod(e), 'k, s').objgens()
f = kx*(n-sx+1) - 1
roots = small_roots(f, (2**545, 2**1025), m=5, d=5)
print(roots)
k, s = roots[0]
p = ZZ((sqrt(s**2 - 4*n) + s) / 2)
q = ZZ(n // p)
assert p * q == n and p > 1 and q > 1
print(long_to_bytes(pow(c, pow(e, -1, (p-1)*(q-1)), n)))
fun fact
不知道为什么最后求p和q非常慢,sage特性吗,还是/2触发sage的float了?
Earthy OL
1 | from Crypto.Util.number import * |
问管理员说,所谓的secret库实际上是同文件夹下的secret.py,里面往往只有一句 flag=b'xxx' ,实际上根本不神秘.
这个程序实现了一个加密系统,并放出了论文给出了几种格构造的攻击方式.
Flu这里选取第二种格:即
然后用lll求出来的是这个向量:
我们只期望得到u,所以lll得到的u第一列可以删掉.脚本:1
2
3
4
5
6
7
8
9
10b_s = [ ... ]
alpha=2**750
mat=Matrix(ZZ,10,11)
for i in range(10):
mat[i,0]=b_s[i]
for i in range(10):
mat[i,i+1]=alpha
M=mat.LLL()
for i in M:
print(i)
记得求出来的分量都除一个 $2^\rho$ 才是本来的u!
然后我们尝试恢复q:已知所有的u都和q垂直,只有最后一个分量乘q的值是 $±1$ ,这意味着
所以
至此,我们成功恢复了q👏!脚本:1
2
3
4
5M=Matrix(ZZ,[ ... ])
res=M.inverse()
b=Matrix(ZZ,[[0],[0],[0],[0],[0],[0],[0],[0],[0],[-1]])
rres=res*b
print(rres)
然后我们尝试恢复大质数N:应用论文中GACD或者PACD的方法,Flu让AI写的脚本…1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16b_s = [...]
a_s = [...]
# 选择两个样本
i, j = 0, 1 # 可以尝试多组
num = b_s[i] - b_s[j]
den = a_s[i] - a_s[j]
N_candidate = abs(num) // abs(den)
for k in range(len(b_s)):
e_candidate = b_s[k] - a_s[k] * N_candidate
if e_candidate.bit_length() > 800: # 如果误差太大,说明不对
print(f"样本 {k} 验证失败,误差位数: {e_candidate.bit_length()}")
break
else:
print("成功恢复 N:")
print(N_candidate)
然后成功恢复N,本题结束.
f14g $\neq$ flag
1 | from secret import flag, secret |
大意:给了一个置换类,范围 $[0,n)$ ,假设为 $\sigma$ ,现在给定大数 $p$ ,给你 $\sigma^p$ ,你要完整还原原来的 $\sigma$ .
几个性质:
- 原来的排列可以用找出来的一堆子环表示.
- 环的size和p不互质的时候会断子环,这个时候会出现几个size相同的子环,考虑将这样的环O(n)合并:交错枚举,假设有两个环分别是[1,2,3,4]和[5,6,7,8],我们考虑枚举[1,5,2,6,3,7,4,8],[1,6,2,7,3,8,4,5]…
- 环的size和p互质的时候可以快速求出原来的排列,你知道了 $\sigma^p$ ,那么有 $\alpha^p\mod size$ ,直接对置换求逆元即可.
- 对于剩下拼起来的环,因为他们是被拼起来的,所以对size除掉公因子的部分求逆元,比如p含有2,两个4拼起来的环就是
invert(p//2,4)乘上这个逆元即可.
回归正题,我们先找环,发现数据由以下构成:1
[117899, 66053, 48605, 687, 34, 34, 4, 4, 3, 3, 3, 3, 1]
所以前面的求逆元,后面的34因为有两个,考虑合并.又因为34是2的倍数,所以他们呢不可能单独成环.
因为加密的时候因为环大小不互质,信息会丢失,所以逆推回去会有很多个解,我们只能在解密的时候检查这些解对不对.
1 | import random |
经过询问,发现pickle是一个反序列化的神秘代码库,有可能被利用一些神秘的漏洞…
Glichy RSA
1 | from Crypto.Util.number import * |
首先能发现p-1和e不互质,也就意味着m在p-1的群里面会丢失信息.1
6750311=105*65537
这样就有两个解法:
原理: $e/\gcd(p-1,e)\times d1 \equiv 1\mod ((p-1)/\gcd(p-1,e))$
所以用d1去解,能得到 $m^{\gcd(p-1,e)}\mod p$ ,然后再有限域开根号.
第一种解法是直接开根号,脚本长这样:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16phi=(p-1)*(q-1)
e1=e//GCD(p-1,e)
d1 =inverse(e1,p-1)
m1 =pow(c,d1,P)
mp =GF(p)(m1).nth_root(GCD(p-1,e),all=True)
d2=inverse(e//GCD(q-1,e),q-1)
m2 =pow(c,d2,q)
mq = GF(q)(m2).nth_root(GCD(q-1,e),all=True)
print(len(mp),len(mq))
for x in mp:
for y in mq:
X = ZZ(x)
y = ZZ(y)
m = crt([x,y],[p,q])
if(long_to_bytes(m).startswith(b'Spirit{')):
print(long_to_bytes(m))
算了很久才算出来.
是出题人的解法.
首先RSA的明密文空间是
也就是所有0~N之间和N互质的数。然后因为N=pq,所以
然后公钥指数e和 是互质的,所以密文在 中的那部分可以逆回去,也就是说可以找到 使得 ,模q的部分很简单。
但是 $p-1$ 和 $e$ 有一个公因子,我记得是 $103$ 乘上 $65537^2$ ,
不存在 $d_p$ 使得 $e*d_p=1(\mod (p-1))$ 。
所以这个时候就得穷举这两个子群中的元素, $103×65537$ 次.
不是 $103×65537^2$ 是因为公钥指数里面有一个 $65537$ .所以 $\mathbb Z/65537^2\mathbb Z$ 中对应 $65537*k,k-0,…,65536$
Z/(p-1)Z中每个元素都可以映射到一个三元组(g1, g2, g3)$g1\in Z/103Z$ ,以此类推
然后如果对这个Z/(p-1)Z的元素乘上103(对应 里的103次乘方) ,那这个映射的像就变成
因为 $g1\in Z/103Z$ ,所以就相当于
算103分别模 和 的逆,就可以从 恢复出(0,g2,g3)
但是g1求不了,g1的信息永远地丢失了
1 | from sage.all import * |
Extravagant Cigarette Battlefield
encrypt.py1
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'''
pip install opencv-python
pip install numpy
'''
import cv2
import numpy as np
from utils import Cipher, Mode
import random
flag = cv2.imread('flag.png', cv2.IMREAD_COLOR)
flag = cv2.cvtColor(flag, cv2.COLOR_BGR2RGB)
height, width, channels = flag.shape
assert height % 4 == 0 and width % 4 == 0 and channels == 3
enc = np.zeros((height, width, channels), dtype=np.uint8)
cipher = Cipher(random.randbytes(16), Mode.CBC, iv = random.randbytes(16))
for c in range(channels):
message = b''
for i in range(0, height, 4):
for j in range(0, width, 4):
block = bytes(flag[i+ii,j+jj,c] for ii in range(4) for jj in range(4))
message += block
enc_msg = cipher.encrypt(message)
for i in range(0, height, 4):
for j in range(0, width, 4):
enc[i:i+4,j:j+4,c] = [[enc_msg[(i*width+j*4)+ii+jj] for jj in range(4)] for ii in range(0,16,4)]
cv2.imwrite('encrypted.png', enc)
utils.py1
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
102from enum import Enum
class Mode(Enum):
ECB = 0x0
CBC = 0x1
StupidBOX = [
0x2a, 0x70, 0x7e, 0x24, 0x43, 0x19, 0x17, 0x4d, 0xf8, 0xa2, 0xac, 0xf6, 0x91, 0xcb, 0xc5, 0x9f,
0x8e, 0xd4, 0xda, 0x80, 0xe7, 0xbd, 0xb3, 0xe9, 0x5c, 0x06, 0x08, 0x52, 0x35, 0x6f, 0x61, 0x3b,
0x6b, 0x31, 0x3f, 0x65, 0x02, 0x58, 0x56, 0x0c, 0xb9, 0xe3, 0xed, 0xb7, 0xd0, 0x8a, 0x84, 0xde,
0xcf, 0x95, 0x9b, 0xc1, 0xa6, 0xfc, 0xf2, 0xa8, 0x1d, 0x47, 0x49, 0x13, 0x74, 0x2e, 0x20, 0x7a,
0xa9, 0xf3, 0xfd, 0xa7, 0xc0, 0x9a, 0x94, 0xce, 0x7b, 0x21, 0x2f, 0x75, 0x12, 0x48, 0x46, 0x1c,
0x0d, 0x57, 0x59, 0x03, 0x64, 0x3e, 0x30, 0x6a, 0xdf, 0x85, 0x8b, 0xd1, 0xb6, 0xec, 0xe2, 0xb8,
0xe8, 0xb2, 0xbc, 0xe6, 0x81, 0xdb, 0xd5, 0x8f, 0x3a, 0x60, 0x6e, 0x34, 0x53, 0x09, 0x07, 0x5d,
0x4c, 0x16, 0x18, 0x42, 0x25, 0x7f, 0x71, 0x2b, 0x9e, 0xc4, 0xca, 0x90, 0xf7, 0xad, 0xa3, 0xf9,
0x2d, 0x77, 0x79, 0x23, 0x44, 0x1e, 0x10, 0x4a, 0xff, 0xa5, 0xab, 0xf1, 0x96, 0xcc, 0xc2, 0x98,
0x89, 0xd3, 0xdd, 0x87, 0xe0, 0xba, 0xb4, 0xee, 0x5b, 0x01, 0x0f, 0x55, 0x32, 0x68, 0x66, 0x3c,
0x6c, 0x36, 0x38, 0x62, 0x05, 0x5f, 0x51, 0x0b, 0xbe, 0xe4, 0xea, 0xb0, 0xd7, 0x8d, 0x83, 0xd9,
0xc8, 0x92, 0x9c, 0xc6, 0xa1, 0xfb, 0xf5, 0xaf, 0x1a, 0x40, 0x4e, 0x14, 0x73, 0x29, 0x27, 0x7d,
0xae, 0xf4, 0xfa, 0xa0, 0xc7, 0x9d, 0x93, 0xc9, 0x7c, 0x26, 0x28, 0x72, 0x15, 0x4f, 0x41, 0x1b,
0x0a, 0x50, 0x5e, 0x04, 0x63, 0x39, 0x37, 0x6d, 0xd8, 0x82, 0x8c, 0xd6, 0xb1, 0xeb, 0xe5, 0xbf,
0xef, 0xb5, 0xbb, 0xe1, 0x86, 0xdc, 0xd2, 0x88, 0x3d, 0x67, 0x69, 0x33, 0x54, 0x0e, 0x00, 0x5a,
0x4b, 0x11, 0x1f, 0x45, 0x22, 0x78, 0x76, 0x2c, 0x99, 0xc3, 0xcd, 0x97, 0xf0, 0xaa, 0xa4, 0xfe
]
PeroPero = [0, 8, 16, 24, 1, 9, 17, 25, 2, 10, 18, 26, 3, 11, 19, 27, 4, 12, 20, 28, 5, 13, 21, 29, 6, 14, 22, 30, 7,
15, 23, 31]
rol = lambda a, b: (a >> 8 - b | a << b) & 0xFF
def schedule(key: bytes):
i = 0
while True:
i += 1
key = key[1:] + bytes([rol(sum(114 * (k << i) % 42 for i, k in enumerate(key)) & 0xFF, 3)])
if i % 16 == 0:
i = 0
yield key
class Cipher:
def __init__(self, key: bytes, mode: Mode, iv: bytes = None):
if len(key) != 16:
raise ValueError('Wrong key length :(')
self.key = key
self.mode = mode
self.rounds = 9
if self.mode == Mode.CBC:
if iv is None:
raise ValueError('I need IV :(')
if len(iv) != 16:
raise ValueError('Wrong IV length :(')
self.iv = iv
self.round_keys = []
sche = schedule(self.key)
for r in range(self.rounds + 1):
self.round_keys.append(next(sche))
def K(self, block: bytes, key: bytes):
return bytes(a ^ b for a, b in zip(block, key))
def S(self, block: bytes):
return bytes(StupidBOX[a] for a in block)
def A_(self, block32: bytes):
ret = [None] * 32
for p, a in zip(PeroPero, block32):
ret[p] = a
return ''.join(ret)
def A(self, block: bytes):
bits = ''.join(f'{a:08b}' for a in block)
enc = ''.join(self.A_(bits[i:i + 32]) for i in range(0, 128, 32))
enc = hex(int(enc, 2))[2:].zfill(32)
return bytes.fromhex(enc)
def encrypt_block(self, block: bytes):
for r in range(self.rounds):
key = self.round_keys[r]
block = self.K(block, key)
block = self.S(block)
block = self.A(block)
key = self.round_keys[r + 1]
block = self.K(block, key)
return block
def encrypt(self, msg: bytes):
assert len(msg) % 16 == 0, 'Wrong message length :('
if self.mode == Mode.ECB:
blocks = []
for i in range(0, len(msg), 16):
blocks.append(self.encrypt_block(msg[i:i + 16]))
return b''.join(blocks)
elif self.mode == Mode.CBC:
blocks = []
last = self.iv
for i in range(0, len(msg), 16):
blocks.append(self.encrypt_block(self.K(msg[i:i + 16], last)))
last = blocks[-1]
return b''.join(blocks)
上面仍然是一个线性盒,也就是说满足 box[i^j]=box[i]^box[j] .
放到图片上,我们设C[i]=第i个密文块,然后1
2C[i] = F(M[i] + C[i-1], key) + X
C[i+1] = F(M[i+1] + C[i], key) + X
其中X是一个常数.上式相加,对F求逆,则有1
M[i] + M[i+1] = F^-1(C[i] + C[i+1]) + C[i] + C[i-1]
所以我们可以通过块与块之间的diff看出来这张图片是什么.
hint:AES变换的九轮过程可以打包一起分析.
所以最后…要到的exp是这么写的:(其中iv和key可以随便填)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
95import cv2
import numpy as np
from utils import *
from tqdm import trange
xor = lambda a, b: bytes(aa^bb for aa, bb in zip(a, b))
StupidBOX_inv = [StupidBOX.index(i) for i in range(256)]
class Cipher2(Cipher):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def F(self, block: bytes):
for r in range(9):
block = self.S(block)
block = bytes(b ^ StupidBOX[0] for b in block)
block = self.A(block)
return block
def S_inv(self, block: bytes):
return bytes(StupidBOX_inv[a] for a in block)
def A_inv_(self, block32: bytes):
ret = [None]*32
for i, p in enumerate(PeroPero):
ret[i] = block32[p]
return ''.join(ret)
def A_inv(self, block: bytes):
bits = ''.join(f'{a:08b}' for a in block)
enc = ''.join(self.A_inv_(bits[i:i+32]) for i in range(0, 128, 32))
enc = hex(int(enc, 2))[2:].zfill(32)
return bytes.fromhex(enc)
def F_inv(self, block: bytes):
for r in range(9):
block = self.A_inv(block)
block = bytes(b ^ StupidBOX[0] for b in block)
block = self.S_inv(block)
return block
def vuln(self, msg: bytes):
assert len(msg) % 16 == 0, 'Wrong message length :('
assert self.mode == Mode.CBC, "Baka :p"
'''
C[i] = F(M[i] + C[i-1]) + X
C[i+1] = F(M[i+1] + C[i]) + X
M[i] + M[i+1] = F^-1(C[i] + C[i+1]) + C[i] + C[i-1]
'''
ret = b''
last = None
lastlast = None
for i in trange(0, len(msg), 16):
block = msg[i:i+16]
if i == 0:
last = block
ret += block
continue
if i == 16:
lastlast = last
last = block
ret += block
continue
ret += xor(
xor(last, lastlast),
self.F_inv(xor(last, block))
)
lastlast = last
last = block
return ret
enc = cv2.imread('encrypted.png', cv2.IMREAD_COLOR)
enc = cv2.cvtColor(enc, cv2.COLOR_BGR2RGB)
height, width, channels = enc.shape
assert height % 4 == 0 and width % 4 == 0 and channels == 3
vuln = np.zeros((height, width, channels), dtype=np.uint8)
cipher_vuln = Cipher2(b'aaaaaaaaaaaaaaaa', Mode.CBC, iv = b'7hiS 1s 16 byt3s')
for c in range(channels):
message = b''
for i in range(0, height, 4):
for j in range(0, width, 4):
block = bytes(enc[i+ii,j+jj,c] for ii in range(4) for jj in range(4))
message += block
vuln_msg = cipher_vuln.vuln(message)
for i in range(0, height, 4):
for j in range(0, width, 4):
vuln[i:i+4,j:j+4,c] = [[vuln_msg[(i*width+j*4)+ii+jj] for jj in range(4)] for ii in range(0,16,4)]
cv2.imwrite('unchained.png', vuln)
PWN
fmtstr
标准字符串利用漏洞,使用printf输出的字符串有%什么的就可以操作了,然后就能读变量改变量…1
2
3
4
5
6
7c读一个字符
d读一个数字
n写入
p读取地址
注意栈对齐(%9$n前面加上四个补齐栈区)
Flu
Flu设想出一系列的套题,这些题是如此操作:
AES很高级,所以我们给密文和一些数据,还有一道”密码体系”,用于计算密钥.
这个”密码体系”可以是有缺陷的RSA等,这是标准密码题.
既然这是一道算数题,那么…我们为什么不在”密码体系”这里放一道时间复杂度爆炸的算法题,然后让选手优化算法去高效算数得到密钥和flag呢…
又或者说,反正数据结构上都上了,为什么不编译一下丢到Reverse里呢…
但这是不对的:
not faithful to the source.
代码是描述性的.一般来说,一道好的密码题要有一个明确的明文和密文,和清晰的加密逻辑.