前言

这题当时赛中很早就写出爆破解密脚本了,但是一直没爆出来,赛后和出题人咨询发现是密文错了,并且发现其实是程序本身出错了,xor 0x10的代码并没有给实际密文数据段进行xor,而是给一个空数据段xor了,所以导致密文没有xor 0x10,所以是错的密文,也就无法爆破。这题本身题目难度还行,WASM逆向+AES魔改+Siphash+排列爆破。

分析

程序流程分析

下载得到bin文件,xor 0x21得到wasm程序文件。

alt text

这边使用ghidra分析,ghidra插件链接:https://github.com/nneonneo/ghidra-wasm-plugin

从导出表定位到main函数。

alt text

第一部分,先输入字符串,然后srand(time(0)),取16个rand()%2,存到b70数组,所以b70是0101的随机排列数组,这边的需要的爆破次数就是2^16。

alt text

这边提一嘴srand和rand的实现,这样的实现就是对应C语言中的srand和rand,魔数也是很好看出来的,这边代码抄出来就可以用。

alt text

alt text

第二部分是将输入字符串取了一部分,然后将b70数组当作密钥,进行SipHash,得到hash值然后扩展成16字节的密钥,这边对输入字符串的操作有点迷惑看不懂,就得开始动调看看实际怎么取的。

alt text

alt text

这边使用的是TGCTF的index题的wasm环境,只是把里面的wasm调用改成这题的wasm。

输入48长度字符串,然后点确定再点一下取消,断点到wasm程序,func13就是main函数这边。

alt text

alt text

这边调用的func12就是对应SpiHash的call。然后堆栈的三个值就对应SpiHash的三个参数,第一个参数就是要看的传入字符串参数。

将值转到十六进制,点击箭头处的Memory按钮,在Memory窗口转到对应的地址,发现是传入是ISCC{123},也就是我们输入的字符串前八个字符加最后一个}。

alt text

alt text

alt text

最后一个部分就是将输入字符串用刚刚hash扩展生成的密钥进行AES加密,最后与密文进行比较。

alt text

解密流程简述

爆破16大小的01字节数组全排列,爆破三个未知可视字符(32-126),经过SpiHash然后扩展生成密钥进行AES解密,如果解密出ISCC{}格式字符串,即为爆破解密成功。

总体解密次数是 (2^16)*(95^3) = 56,188,928,000,由于量级过大,考虑使用CUDA进行爆破。

AES魔改分析

和标准AES代码流程进行对比,分析发现是mixColumns和addRoundKey进行了对调,其他地方都一样。

alt text

alt text

第二个魔改点是Sbox数值的魔改,导出然后手动算出InvSbox即用于解密。解密的时候也同样交换mixColumns和addRoundKey的调用即可。

1
2
3
4
unsigned char S[256] = { 0x3f, 0x1e, 0xfc, 0x3d, 0x68, 0x51, 0xf0, 0x20, 0x92, 0x02, 0x9d, 0xac, 0x54, 0x6e, 0xfb, 0x42, 0x29, 0xe8, 0x23, 0x2a, 0xd5, 0xa2, 0x3a, 0xc0, 0xd4, 0xba, 0xb5, 0x84, 0xa6, 0x74, 0x5c, 0x08, 0xf2, 0x22, 0xa7, 0x41, 0x8b, 0xeb, 0x32, 0x0d, 0x8a, 0x89, 0x46, 0x70, 0x5e, 0xa0, 0xef, 0x67, 0x49, 0xd3, 0x18, 0x76, 0xbd, 0xd0, 0x8d, 0x2f, 0xb4, 0x55, 0xc5, 0xc8, 0x36, 0x37, 0x66, 0x04, 0xd8, 0x01, 0x5a, 0x2e, 0xed, 0x91, 0xff, 0x15, 0x6c, 0x64, 0x5d, 0x24, 0x72, 0x1a, 0x75, 0x30, 0x56, 0xbf, 0xb1, 0x73, 0xc7, 0x95, 0x05, 0xb6, 0x52, 0x31, 0xb3, 0x10, 0x2b, 0x6f, 0x43, 0xbb, 0x62, 0x7c, 0x7b, 0xa3, 0xbe, 0xd9, 0xbc, 0xdc, 0xc9, 0x8f, 0xa4, 0xe3, 0xe7, 0x17, 0x5f, 0xe9, 0xca, 0x6d, 0x4e, 0xae, 0x83, 0x63, 0x82, 0x27, 0x4a, 0x21, 0x71, 0x2c, 0x57, 0x7d, 0xaf, 0x44, 0x85, 0xc1, 0x47, 0x4b, 0x48, 0xf4, 0xfd, 0x3c, 0xf1, 0x45, 0x1f, 0x5b, 0xb8, 0xa1, 0xc4, 0x79, 0x53, 0x09, 0xea, 0xee, 0x0c, 0xd6, 0x61, 0xc6, 0xaa, 0xb0, 0x69, 0x81, 0xb9, 0x7f, 0xec, 0x94, 0xce, 0xa9, 0x97, 0x3b, 0xda, 0x8e, 0xe5, 0x86, 0x16, 0x11, 0xad, 0xd1, 0xd7, 0x40, 0xb2, 0x65, 0xcb, 0xb7, 0x1c, 0x7a, 0xf6, 0x87, 0xcd, 0x4f, 0x9f, 0xab, 0x4d, 0x0f, 0x6a, 0xa8, 0xde, 0xc3, 0x39, 0x50, 0xfa, 0x35, 0x33, 0x90, 0xdf, 0xf8, 0x25, 0x8c, 0x9c, 0xe0, 0xf7, 0x07, 0xe2, 0x99, 0x77, 0x00, 0x26, 0x6b, 0x0b, 0x3e, 0x1d, 0xe1, 0x58, 0x38, 0xc2, 0x78, 0x0e, 0x59, 0x93, 0x1b, 0x88, 0xdd, 0x9b, 0xd2, 0x19, 0x7e, 0xf9, 0xdb, 0xfe, 0x60, 0x13, 0x4c, 0xcc, 0xf3, 0xa5, 0x14, 0x34, 0x96, 0x80, 0xe6, 0x9a, 0xf5, 0x9e, 0xe4, 0x2d, 0x03, 0x12, 0x0a, 0xcf, 0x98, 0x28, 0x06 };

unsigned char inv_S[] = { 0xD1, 0x41, 0x09, 0xF9, 0x3F, 0x56, 0xFF, 0xCD, 0x1F, 0x91, 0xFB, 0xD4, 0x94, 0x27, 0xDC, 0xBB, 0x5B, 0xA9, 0xFA, 0xEA, 0xEF, 0x47, 0xA8, 0x6D, 0x32, 0xE4, 0x4D, 0xDF, 0xB2, 0xD6, 0x01, 0x8A, 0x07, 0x79, 0x21, 0x12, 0x4B, 0xC8, 0xD2, 0x77, 0xFE, 0x10, 0x13, 0x5C, 0x7B, 0xF8, 0x43, 0x37, 0x4F, 0x59, 0x26, 0xC4, 0xF0, 0xC3, 0x3C, 0x3D, 0xD9, 0xC0, 0x16, 0xA3, 0x87, 0x03, 0xD5, 0x00, 0xAD, 0x23, 0x0F, 0x5E, 0x7F, 0x89, 0x2A, 0x82, 0x84, 0x30, 0x78, 0x83, 0xEB, 0xBA, 0x72, 0xB7, 0xC1, 0x05, 0x58, 0x90, 0x0C, 0x39, 0x50, 0x7C, 0xD8, 0xDD, 0x42, 0x8B, 0x1E, 0x4A, 0x2C, 0x6E, 0xE9, 0x96, 0x60, 0x75, 0x49, 0xAF, 0x3E, 0x2F, 0x04, 0x9A, 0xBC, 0xD3, 0x48, 0x71, 0x0D, 0x5D, 0x2B, 0x7A, 0x4C, 0x53, 0x1D, 0x4E, 0x33, 0xD0, 0xDB, 0x8F, 0xB3, 0x62, 0x61, 0x7D, 0xE5, 0x9D, 0xF2, 0x9B, 0x76, 0x74, 0x1B, 0x80, 0xA7, 0xB5, 0xE0, 0x29, 0x28, 0x24, 0xC9, 0x36, 0xA5, 0x69, 0xC5, 0x45, 0x08, 0xDE, 0x9F, 0x55, 0xF1, 0xA2, 0xFD, 0xCF, 0xF4, 0xE2, 0xCA, 0x0A, 0xF6, 0xB8, 0x2D, 0x8D, 0x15, 0x63, 0x6A, 0xEE, 0x1C, 0x22, 0xBD, 0xA1, 0x98, 0xB9, 0x0B, 0xAA, 0x73, 0x7E, 0x99, 0x52, 0xAE, 0x5A, 0x38, 0x1A, 0x57, 0xB1, 0x8C, 0x9C, 0x19, 0x5F, 0x66, 0x34, 0x64, 0x51, 0x17, 0x81, 0xDA, 0xBF, 0x8E, 0x3A, 0x97, 0x54, 0x3B, 0x68, 0x70, 0xB0, 0xEC, 0xB6, 0xA0, 0xFC, 0x35, 0xAB, 0xE3, 0x31, 0x18, 0x14, 0x95, 0xAC, 0x40, 0x65, 0xA4, 0xE7, 0x67, 0xE1, 0xBE, 0xC6, 0xCB, 0xD7, 0xCE, 0x6B, 0xF7, 0xA6, 0xF3, 0x6C, 0x11, 0x6F, 0x92, 0x25, 0x9E, 0x44, 0x93, 0x2E, 0x06, 0x88, 0x20, 0xED, 0x85, 0xF5, 0xB4, 0xCC, 0xC7, 0xE6, 0xC2, 0x0E, 0x02, 0x86, 0xE8, 0x46 };

alt text

错误代码部分

function_8处代码本意是要将静态的明文xor上0x10,但是地址不对,xor到了一个空白的内存,所以解密的时候得把密文都xor 0x10,才能成功爆破出来密钥。

alt text

加密同构

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
#include <iostream>
#include "AES.h"
#include <stdint.h>
#include <Windows.h>

#define _le64toh(x) ((uint64_t)(x))

#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )

#define HALF_ROUND(a,b,c,d,s,t) \
a += b; c += d; \
b = ROTATE(b, s) ^ a; \
d = ROTATE(d, t) ^ c; \
a = ROTATE(a, 32);

#define DOUBLE_ROUND(v0,v1,v2,v3) \
HALF_ROUND(v0,v1,v2,v3,13,16); \
HALF_ROUND(v2,v1,v0,v3,17,21); \
HALF_ROUND(v0,v1,v2,v3,13,16); \
HALF_ROUND(v2,v1,v0,v3,17,21);

uint64_t siphash24(const void* src, unsigned long src_sz, const char key[16]) {
const uint64_t* _key = (uint64_t*)key;
uint64_t k0 = _le64toh(_key[0]);
uint64_t k1 = _le64toh(_key[1]);
uint64_t b = (uint64_t)src_sz << 56;
const uint64_t* in = (uint64_t*)src;

uint64_t v0 = k0 ^ 0x102030405060708;
uint64_t v1 = k1 ^ 0x90a0b0c0d0e0f00;
uint64_t v2 = k0 ^ k1 ^ 0x123456789abcdef;
uint64_t v3 = k1 ^ k0 ^ 0x4953434343435349;

while (src_sz >= 8) {
uint64_t mi = _le64toh(*in);
in += 1; src_sz -= 8;
v3 ^= mi;
DOUBLE_ROUND(v0, v1, v2, v3);
v0 ^= mi;
}

uint64_t t = 0; uint8_t* pt = (uint8_t*)&t; uint8_t* m = (uint8_t*)in;
if (src_sz == 7)
{
pt[6] = m[6];
}
if (src_sz == 6)
{
pt[5] = m[5];
}
if (src_sz == 5)
{
pt[4] = m[4];
}
if (src_sz == 4)
{
*((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]);
}
if (src_sz == 3)
{
pt[2] = m[2];
}
if (src_sz == 2)
{
pt[1] = m[1];
}
if (src_sz == 1)
{
pt[0] = m[0];
}
b |= _le64toh(t);

v3 ^= b;
DOUBLE_ROUND(v0, v1, v2, v3);
v0 ^= b; v2 ^= 0xff;
DOUBLE_ROUND(v0, v1, v2, v3);
DOUBLE_ROUND(v0, v1, v2, v3);
return (v0 ^ v1) ^ (v2 ^ v3);
}

uint64_t lRam00000c68{};

uint32_t my_rand()
{
lRam00000c68 = lRam00000c68 * 0x5851f42d4c957f2d + 1;
return (uint32_t)((uint64_t)lRam00000c68 >> 0x21);
}

void my_srand(uint32_t seed)
{
lRam00000c68 = (uint64_t)(seed - 1);
}

int main()
{
// 随机数生成列表
my_srand(1746373320);
uint8_t rndList[16]{};

for (int i = 0; i < 16; i++)
{
rndList[i] = i;
}

for (int i = 0; i < 16; i++)
{
rndList[i] += (uint8_t)(my_rand() % 2);
}

// 输入48长度
uint8_t input[] = "ISCC{1234123544556ASDASeffgghhiijj12321312312kk}";
uint8_t out[16]{};

// hash明文取input前八字节和最后一个字节
uint8_t hash_plaintext[9]{};
memcpy(hash_plaintext, input, 8);
hash_plaintext[8] = '}';

// spihash计算
uint64_t hash = siphash24(hash_plaintext, 9, (const char*)rndList);

// 用hash生成Key
uint8_t key[16]{};
for (int i = 0; i < 8; i++)
{
key[i] = (uint8_t)(hash >> (i * 8 & 0x3f));
key[i * 2 + 1] = (uint8_t)(hash >> (i * -8 + 0x38 & 0x3f));
}

for (int i = 0; i < 48; i += 16)
{
aesEncrypt(key, 16, input + i, out, 16);
for (int i = 0; i < 16; i++)
{
printf("0x%02x,", out[i]);
}
}

return 0;
}

AES魔改后解密代码:

代码基于:https://github.com/lmshao/AES

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
unsigned char inv_S[] = { 0xD1, 0x41, 0x09, 0xF9, 0x3F, 0x56, 0xFF, 0xCD, 0x1F, 0x91, 0xFB, 0xD4, 0x94, 0x27, 0xDC, 0xBB, 0x5B, 0xA9, 0xFA, 0xEA, 0xEF, 0x47, 0xA8, 0x6D, 0x32, 0xE4, 0x4D, 0xDF, 0xB2, 0xD6, 0x01, 0x8A, 0x07, 0x79, 0x21, 0x12, 0x4B, 0xC8, 0xD2, 0x77, 0xFE, 0x10, 0x13, 0x5C, 0x7B, 0xF8, 0x43, 0x37, 0x4F, 0x59, 0x26, 0xC4, 0xF0, 0xC3, 0x3C, 0x3D, 0xD9, 0xC0, 0x16, 0xA3, 0x87, 0x03, 0xD5, 0x00, 0xAD, 0x23, 0x0F, 0x5E, 0x7F, 0x89, 0x2A, 0x82, 0x84, 0x30, 0x78, 0x83, 0xEB, 0xBA, 0x72, 0xB7, 0xC1, 0x05, 0x58, 0x90, 0x0C, 0x39, 0x50, 0x7C, 0xD8, 0xDD, 0x42, 0x8B, 0x1E, 0x4A, 0x2C, 0x6E, 0xE9, 0x96, 0x60, 0x75, 0x49, 0xAF, 0x3E, 0x2F, 0x04, 0x9A, 0xBC, 0xD3, 0x48, 0x71, 0x0D, 0x5D, 0x2B, 0x7A, 0x4C, 0x53, 0x1D, 0x4E, 0x33, 0xD0, 0xDB, 0x8F, 0xB3, 0x62, 0x61, 0x7D, 0xE5, 0x9D, 0xF2, 0x9B, 0x76, 0x74, 0x1B, 0x80, 0xA7, 0xB5, 0xE0, 0x29, 0x28, 0x24, 0xC9, 0x36, 0xA5, 0x69, 0xC5, 0x45, 0x08, 0xDE, 0x9F, 0x55, 0xF1, 0xA2, 0xFD, 0xCF, 0xF4, 0xE2, 0xCA, 0x0A, 0xF6, 0xB8, 0x2D, 0x8D, 0x15, 0x63, 0x6A, 0xEE, 0x1C, 0x22, 0xBD, 0xA1, 0x98, 0xB9, 0x0B, 0xAA, 0x73, 0x7E, 0x99, 0x52, 0xAE, 0x5A, 0x38, 0x1A, 0x57, 0xB1, 0x8C, 0x9C, 0x19, 0x5F, 0x66, 0x34, 0x64, 0x51, 0x17, 0x81, 0xDA, 0xBF, 0x8E, 0x3A, 0x97, 0x54, 0x3B, 0x68, 0x70, 0xB0, 0xEC, 0xB6, 0xA0, 0xFC, 0x35, 0xAB, 0xE3, 0x31, 0x18, 0x14, 0x95, 0xAC, 0x40, 0x65, 0xA4, 0xE7, 0x67, 0xE1, 0xBE, 0xC6, 0xCB, 0xD7, 0xCE, 0x6B, 0xF7, 0xA6, 0xF3, 0x6C, 0x11, 0x6F, 0x92, 0x25, 0x9E, 0x44, 0x93, 0x2E, 0x06, 0x88, 0x20, 0xED, 0x85, 0xF5, 0xB4, 0xCC, 0xC7, 0xE6, 0xC2, 0x0E, 0x02, 0x86, 0xE8, 0x46 };

int aesDecrypt(const uint8_t *key, uint32_t keyLen, const uint8_t *ct, uint8_t *pt, uint32_t len)
{
AesKey aesKey;
uint8_t *pos = pt;
const uint32_t *rk = aesKey.dK;
uint8_t out[BLOCKSIZE] = {0};
uint8_t actualKey[16] = {0};
uint8_t state[4][4] = {0};

memcpy(actualKey, key, keyLen);
keyExpansion(actualKey, 16, &aesKey);

for (int i = 0; i < len; i += BLOCKSIZE) {
loadStateArray(state, ct);
addRoundKey(state, rk);
for (int j = 1; j < 10; ++j) {
rk += 4;
invShiftRows(state);
invSubBytes(state);
// 解密交换addRoundKey和invMixColumns的调用顺序
invMixColumns(state);
addRoundKey(state, rk);
}
invSubBytes(state);
invShiftRows(state);
addRoundKey(state, rk+4);

storeStateArray(state, pos);
pos += BLOCKSIZE;
ct += BLOCKSIZE;
rk = aesKey.dK;
}
return 0;
}

CPU爆破样板代码

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
void Crack(uint8_t* KeyOut)
{
// 密文
uint8_t enc1[48]{0x9f, 0xb3, 0xb8, 0x2f, 0x23, 0x82, 0x19, 0x7d, 0xe7, 0x2a, 0x8b, 0x20, 0x0c, 0x5b, 0x79, 0x72, 0xf7, 0x07, 0xa0, 0x63, 0x53, 0x99, 0x49, 0xc2, 0x51, 0xde, 0x86, 0x1b, 0x87, 0x2f, 0x31, 0x6b, 0xbc, 0xfa, 0x5b, 0x9a, 0x6b, 0x12, 0x16, 0x8b, 0x31, 0xf6, 0x6c, 0x1d, 0x94, 0xba, 0x2f, 0x37 };
// 解密明文
uint8_t out[16]{};
// 爆破三个字节全排列
for (int c1 = 32; c1 < 127; c1++)
{
for (int c2 = 32; c2 < 127; c2++)
{
for (int c3 = 32; c3 < 127; c3++)
{
// 爆破16个01的全排列
for (int rnd_v = 0; rnd_v < 0xffff; rnd_v++)
{
uint8_t rndList[16]{ };
for (int i = 0; i < 16; i++)
rndList[i] = i;

for (int i = 0; i < 16; i++)
{
rndList[i] += (rnd_v >> (15-i)) & 1;
}

// 组合目标hash字符串
uint8_t hash_plaintext[] = "ISCC{xxx}";
hash_plaintext[5] = c1;
hash_plaintext[6] = c2;
hash_plaintext[7] = c3;

uint64_t hash = siphash24(hash_plaintext, 9, (const char*)rndList);

// 扩展生成Key
uint8_t key[16]{};
for (int i = 0; i < 8; i++)
{
key[i] = (uint8_t)(hash >> (i * 8 & 0x3f));
key[i * 2 + 1] = (uint8_t)(hash >> (i * -8 + 0x38 & 0x3f));
}
// 仅解密前16字节用于校验Key
aesDecrypt(key, 16, enc1, out, 16);

if (out[0] == 'I' && out[1] == 'S' && out[2] == 'C' && out[3] == 'C')
{
memcpy(KeyOut, key, 16);
for (int i = 0; i < 16; i++)
{
printf("%x ", key[i]);
}
printf("\n");
return;
}
}
}
}
}
}

CPU爆破代码转CUDA爆破代码

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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
#include <iostream>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
// 密文
__constant__ uint8_t EncFlag[48];
// 逆sbox
__constant__ uint8_t inv_S[256];
// sbox
__constant__ uint8_t S[256];
// 轮常量
__constant__ uint32_t rcon[10];
// hash明文模板
__constant__ uint8_t hash_plaintext_template[10];

uint8_t inv_S_user[256] {
0xD1, 0x41, 0x09, 0xF9, 0x3F, 0x56, 0xFF, 0xCD, 0x1F, 0x91, 0xFB, 0xD4, 0x94, 0x27, 0xDC, 0xBB,
0x5B, 0xA9, 0xFA, 0xEA, 0xEF, 0x47, 0xA8, 0x6D, 0x32, 0xE4, 0x4D, 0xDF, 0xB2, 0xD6, 0x01, 0x8A,
0x07, 0x79, 0x21, 0x12, 0x4B, 0xC8, 0xD2, 0x77, 0xFE, 0x10, 0x13, 0x5C, 0x7B, 0xF8, 0x43, 0x37,
0x4F, 0x59, 0x26, 0xC4, 0xF0, 0xC3, 0x3C, 0x3D, 0xD9, 0xC0, 0x16, 0xA3, 0x87, 0x03, 0xD5, 0x00,
0xAD, 0x23, 0x0F, 0x5E, 0x7F, 0x89, 0x2A, 0x82, 0x84, 0x30, 0x78, 0x83, 0xEB, 0xBA, 0x72, 0xB7,
0xC1, 0x05, 0x58, 0x90, 0x0C, 0x39, 0x50, 0x7C, 0xD8, 0xDD, 0x42, 0x8B, 0x1E, 0x4A, 0x2C, 0x6E,
0xE9, 0x96, 0x60, 0x75, 0x49, 0xAF, 0x3E, 0x2F, 0x04, 0x9A, 0xBC, 0xD3, 0x48, 0x71, 0x0D, 0x5D,
0x2B, 0x7A, 0x4C, 0x53, 0x1D, 0x4E, 0x33, 0xD0, 0xDB, 0x8F, 0xB3, 0x62, 0x61, 0x7D, 0xE5, 0x9D,
0xF2, 0x9B, 0x76, 0x74, 0x1B, 0x80, 0xA7, 0xB5, 0xE0, 0x29, 0x28, 0x24, 0xC9, 0x36, 0xA5, 0x69,
0xC5, 0x45, 0x08, 0xDE, 0x9F, 0x55, 0xF1, 0xA2, 0xFD, 0xCF, 0xF4, 0xE2, 0xCA, 0x0A, 0xF6, 0xB8,
0x2D, 0x8D, 0x15, 0x63, 0x6A, 0xEE, 0x1C, 0x22, 0xBD, 0xA1, 0x98, 0xB9, 0x0B, 0xAA, 0x73, 0x7E,
0x99, 0x52, 0xAE, 0x5A, 0x38, 0x1A, 0x57, 0xB1, 0x8C, 0x9C, 0x19, 0x5F, 0x66, 0x34, 0x64, 0x51,
0x17, 0x81, 0xDA, 0xBF, 0x8E, 0x3A, 0x97, 0x54, 0x3B, 0x68, 0x70, 0xB0, 0xEC, 0xB6, 0xA0, 0xFC,
0x35, 0xAB, 0xE3, 0x31, 0x18, 0x14, 0x95, 0xAC, 0x40, 0x65, 0xA4, 0xE7, 0x67, 0xE1, 0xBE, 0xC6,
0xCB, 0xD7, 0xCE, 0x6B, 0xF7, 0xA6, 0xF3, 0x6C, 0x11, 0x6F, 0x92, 0x25, 0x9E, 0x44, 0x93, 0x2E,
0x06, 0x88, 0x20, 0xED, 0x85, 0xF5, 0xB4, 0xCC, 0xC7, 0xE6, 0xC2, 0x0E, 0x02, 0x86, 0xE8, 0x46
};

uint8_t S_user[256]{
0x3f, 0x1e, 0xfc, 0x3d, 0x68, 0x51, 0xf0, 0x20, 0x92, 0x02, 0x9d, 0xac, 0x54, 0x6e, 0xfb, 0x42,
0x29, 0xe8, 0x23, 0x2a, 0xd5, 0xa2, 0x3a, 0xc0, 0xd4, 0xba, 0xb5, 0x84, 0xa6, 0x74, 0x5c, 0x08,
0xf2, 0x22, 0xa7, 0x41, 0x8b, 0xeb, 0x32, 0x0d, 0x8a, 0x89, 0x46, 0x70, 0x5e, 0xa0, 0xef, 0x67,
0x49, 0xd3, 0x18, 0x76, 0xbd, 0xd0, 0x8d, 0x2f, 0xb4, 0x55, 0xc5, 0xc8, 0x36, 0x37, 0x66, 0x04,
0xd8, 0x01, 0x5a, 0x2e, 0xed, 0x91, 0xff, 0x15, 0x6c, 0x64, 0x5d, 0x24, 0x72, 0x1a, 0x75, 0x30,
0x56, 0xbf, 0xb1, 0x73, 0xc7, 0x95, 0x05, 0xb6, 0x52, 0x31, 0xb3, 0x10, 0x2b, 0x6f, 0x43, 0xbb,
0x62, 0x7c, 0x7b, 0xa3, 0xbe, 0xd9, 0xbc, 0xdc, 0xc9, 0x8f, 0xa4, 0xe3, 0xe7, 0x17, 0x5f, 0xe9,
0xca, 0x6d, 0x4e, 0xae, 0x83, 0x63, 0x82, 0x27, 0x4a, 0x21, 0x71, 0x2c, 0x57, 0x7d, 0xaf, 0x44,
0x85, 0xc1, 0x47, 0x4b, 0x48, 0xf4, 0xfd, 0x3c, 0xf1, 0x45, 0x1f, 0x5b, 0xb8, 0xa1, 0xc4, 0x79,
0x53, 0x09, 0xea, 0xee, 0x0c, 0xd6, 0x61, 0xc6, 0xaa, 0xb0, 0x69, 0x81, 0xb9, 0x7f, 0xec, 0x94,
0xce, 0xa9, 0x97, 0x3b, 0xda, 0x8e, 0xe5, 0x86, 0x16, 0x11, 0xad, 0xd1, 0xd7, 0x40, 0xb2, 0x65,
0xcb, 0xb7, 0x1c, 0x7a, 0xf6, 0x87, 0xcd, 0x4f, 0x9f, 0xab, 0x4d, 0x0f, 0x6a, 0xa8, 0xde, 0xc3,
0x39, 0x50, 0xfa, 0x35, 0x33, 0x90, 0xdf, 0xf8, 0x25, 0x8c, 0x9c, 0xe0, 0xf7, 0x07, 0xe2, 0x99,
0x77, 0x00, 0x26, 0x6b, 0x0b, 0x3e, 0x1d, 0xe1, 0x58, 0x38, 0xc2, 0x78, 0x0e, 0x59, 0x93, 0x1b,
0x88, 0xdd, 0x9b, 0xd2, 0x19, 0x7e, 0xf9, 0xdb, 0xfe, 0x60, 0x13, 0x4c, 0xcc, 0xf3, 0xa5, 0x14,
0x34, 0x96, 0x80, 0xe6, 0x9a, 0xf5, 0x9e, 0xe4, 0x2d, 0x03, 0x12, 0x0a, 0xcf, 0x98, 0x28, 0x06
};


uint32_t rcon_user[10]{
0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL
};
#define LOAD32H(x, y) do { \
uint32_t _temp = 0; \
_temp = ((uint32_t)((y)[0] & 0xFF) << 24); \
_temp |= ((uint32_t)((y)[1] & 0xFF) << 16); \
_temp |= ((uint32_t)((y)[2] & 0xFF) << 8); \
_temp |= ((uint32_t)((y)[3] & 0xFF)); \
(x) = _temp; \
} while(0)

#define STORE32H(x, y) \
(y)[0] = (uint8_t)(((x)>>24) & 0xff); (y)[1] = (uint8_t)(((x)>>16) & 0xff); \
(y)[2] = (uint8_t)(((x)>>8) & 0xff); (y)[3] = (uint8_t)((x) & 0xff);

#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)

#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^ \
((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))

#define ROF32(x, n) (((x) << (n)) | ((x) >> (32-(n))))

#define ROR32(x, n) (((x) >> (n)) | ((x) << (32-(n))))

__device__ uint32_t MixWord(uint32_t x, const uint8_t* LocalS) {
union {
uint32_t word;
uint8_t bytes[4];
} in, out;

in.word = x;

out.bytes[0] = LocalS[in.bytes[1]]; // BYTE(x, 1)
out.bytes[1] = LocalS[in.bytes[0]]; // BYTE(x, 0)
out.bytes[2] = LocalS[in.bytes[3]]; // BYTE(x, 3)
out.bytes[3] = LocalS[in.bytes[2]]; // BYTE(x, 2)

return ((uint32_t)out.bytes[3] << 24) |
((uint32_t)out.bytes[2] << 16) |
((uint32_t)out.bytes[1] << 8) |
((uint32_t)out.bytes[0]);
}


__device__ int keyExpansion(const uint8_t * key, uint32_t keyLen, void * aesKey)
{
typedef struct {
uint32_t eK[44], dK[44];
int Nr;
}AesKey;
AesKey* p_aesKey = (AesKey*)aesKey;
uint32_t* w = p_aesKey->eK;
uint32_t* v = p_aesKey->dK;

for (int i = 0; i < 4; ++i) {
LOAD32H(w[i], key + 4 * i);
}

for (int i = 0; i < 10; ++i) {
w[4] = w[0] ^ MIX(w[3]) ^ rcon[i];
w[5] = w[1] ^ w[4];
w[6] = w[2] ^ w[5];
w[7] = w[3] ^ w[6];
w += 4;
}

w = p_aesKey->eK + 44 - 4;
for (int j = 0; j < 11; ++j) {

for (int i = 0; i < 4; ++i) {
v[i] = w[i];
}
w -= 4;
v += 4;
}

return 0;
}

__device__ int loadStateArray(uint8_t(*state)[4], const uint8_t* in) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[j][i] = *in++;
}
}
return 0;
}

__device__ int storeStateArray(uint8_t(*state)[4], uint8_t* out) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
*out++ = state[j][i];
}
}
return 0;
}

__device__ int addRoundKey(uint8_t(*state)[4], const uint32_t* key) {

uint8_t k[4][4];

for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
k[i][j] = (uint8_t)BYTE(key[j], 3 - i);
state[i][j] ^= k[i][j];
}
}

return 0;
}


__device__ int invSubBytes(uint8_t(*state)[4]) {
uint8_t LocalInvS[256];
for (int i = 0; i < 256; i++) {
LocalInvS[i] = inv_S[i];
}
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[i][j] = LocalInvS[state[i][j]];
}
}
return 0;
}

__device__ int invShiftRows(uint8_t(*state)[4]) {
uint8_t temp[4][4];

for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
temp[i][j] = state[i][j];
}
}

state[0][0] = temp[0][0];
state[0][1] = temp[0][1];
state[0][2] = temp[0][2];
state[0][3] = temp[0][3];

state[1][0] = temp[1][3];
state[1][1] = temp[1][0];
state[1][2] = temp[1][1];
state[1][3] = temp[1][2];

state[2][0] = temp[2][2];
state[2][1] = temp[2][3];
state[2][2] = temp[2][0];
state[2][3] = temp[2][1];

state[3][0] = temp[3][1];
state[3][1] = temp[3][2];
state[3][2] = temp[3][3];
state[3][3] = temp[3][0];

return 0;
}

__device__ uint8_t GMul(uint8_t u, uint8_t v) {
uint8_t p = 0;
for (int i = 0; i < 8; ++i) {
if (u & 0x01) {
p ^= v;
}

int flag = (v & 0x80);
v <<= 1;
if (flag) {
v ^= 0x1B;
}

u >>= 1;
}

return p;
}

__device__ int invMixColumns(uint8_t(*state)[4]) {

uint8_t M[4][4] = { {0x0E, 0x0B, 0x0D, 0x09},
{0x09, 0x0E, 0x0B, 0x0D},
{0x0D, 0x09, 0x0E, 0x0B},
{0x0B, 0x0D, 0x09, 0x0E} };
uint8_t tmp[4][4]{};
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
tmp[i][j] = state[i][j];
}
}
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
state[i][j] =
GMul(M[i][0], tmp[0][j])
^ GMul(M[i][1], tmp[1][j])
^ GMul(M[i][2], tmp[2][j])
^ GMul(M[i][3], tmp[3][j]);
}
}

return 0;
}

__device__ int aesDecrypt(const uint8_t* key, uint32_t keyLen, const uint8_t* ct, uint8_t* pt, uint32_t len)
{
typedef struct {
uint32_t eK[44]{}, dK[44]{};
int Nr;
}AesKey;

AesKey aesKey;
uint8_t* pos = pt;

uint8_t state[4][4];

keyExpansion(key, 16, &aesKey);
const uint32_t* rk = aesKey.dK;

loadStateArray(state, ct);
addRoundKey(state, rk);
#pragma unroll
for (int i = 1; i < 10; i++)
{
rk += 4;
invShiftRows(state);
invSubBytes(state);
// 解密同样交换addRoundKey和invMixColumns的调用顺序
invMixColumns(state);
addRoundKey(state, rk);

}
invSubBytes(state);
invShiftRows(state);
addRoundKey(state, rk + 4);

storeStateArray(state, pos);

return 0;
}


#define _le64toh(x) ((uint64_t)(x))

#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) )

#define HALF_ROUND(a,b,c,d,s,t) \
a += b; c += d; \
b = ROTATE(b, s) ^ a; \
d = ROTATE(d, t) ^ c; \
a = ROTATE(a, 32);

#define DOUBLE_ROUND(v0,v1,v2,v3) \
HALF_ROUND(v0,v1,v2,v3,13,16); \
HALF_ROUND(v2,v1,v0,v3,17,21); \
HALF_ROUND(v0,v1,v2,v3,13,16); \
HALF_ROUND(v2,v1,v0,v3,17,21);

__device__ uint64_t siphash24_gpu(const void* src, unsigned long src_sz, const char key[16])
{
const uint64_t* _key = (uint64_t*)key;
uint64_t k0 = _le64toh(_key[0]);
uint64_t k1 = _le64toh(_key[1]);
uint64_t b = (uint64_t)src_sz << 56;
const uint64_t* in = (uint64_t*)src;

uint64_t v0 = k0 ^ 0x102030405060708ULL;
uint64_t v1 = k1 ^ 0x90a0b0c0d0e0f00ULL;
uint64_t v2 = k0 ^ k1 ^ 0x123456789abcdefULL;
uint64_t v3 = k1 ^ k0 ^ 0x4953434343435349ULL;

while (src_sz >= 8) {
uint64_t mi = _le64toh(*in);
in += 1; src_sz -= 8;
v3 ^= mi;
DOUBLE_ROUND(v0, v1, v2, v3);
v0 ^= mi;
}

uint64_t t = 0; uint8_t* pt = (uint8_t*)&t; uint8_t* m = (uint8_t*)in;
if (src_sz == 7)
{
pt[6] = m[6];
}
if (src_sz == 6)
{
pt[5] = m[5];
}
if (src_sz == 5)
{
pt[4] = m[4];
}
if (src_sz == 4)
{
*((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]);
}
if (src_sz == 3)
{
pt[2] = m[2];
}
if (src_sz == 2)
{
pt[1] = m[1];
}
if (src_sz == 1)
{
pt[0] = m[0];
}
b |= _le64toh(t);

v3 ^= b;
DOUBLE_ROUND(v0, v1, v2, v3);
v0 ^= b; v2 ^= 0xff;
DOUBLE_ROUND(v0, v1, v2, v3);
DOUBLE_ROUND(v0, v1, v2, v3);
return (v0 ^ v1) ^ (v2 ^ v3);
}

__global__ void crack(uint8_t* KeyOut, bool* found, uint64_t offset)
{
// 是否找到
if (atomicOr((int*)found,0))
return;

uint64_t tid = blockIdx.x * blockDim.x + threadIdx.x;

if (tid > 94ULL * 95ULL * 0xffffULL)
{
return;
}

uint8_t rndList[16];
uint8_t hash_plaintext[9];
uint8_t key[16];
uint8_t out[16];

const int CHAR_RANGE = 95;
const int BASE_CHAR = 33;

// 通过tid生成当前数值
int rnd_v = tid % 0xFFFF;
unsigned long long char_pos = tid / 0xFFFF;

int c3 = BASE_CHAR + (char_pos % CHAR_RANGE);
char_pos /= CHAR_RANGE;
int c2 = BASE_CHAR + (char_pos % CHAR_RANGE);
char_pos /= CHAR_RANGE;
int c1 = BASE_CHAR + offset;

if (c1 < 127 && c2 < 127 && c3 < 127)
{
// 生成01排列
#pragma unroll
for (int i = 0; i < 16; i++)
{
rndList[i] = i + ((rnd_v >> (15 - i)) & 1);
}

#pragma unroll
for (int i = 0; i < 9; i++) {
hash_plaintext[i] = hash_plaintext_template[i];
}
// ISCC{$c1$c2$c3}
hash_plaintext[5] = c1;
hash_plaintext[6] = c2;

hash_plaintext[7] = c3;
// hash
uint64_t hash = siphash24_gpu(hash_plaintext, 9, (const char*)rndList);

// 用hash扩展生成AES密钥
#pragma unroll
for (int i = 0; i < 8; i++)
{
key[i] = (uint8_t)(hash >> (i * 8 & 0x3f));
key[i * 2 + 1] = (uint8_t)(hash >> (i * -8 + 0x38 & 0x3f));
}
// AES解密
aesDecrypt(key, 16, EncFlag, out, 16);
if (out[0] == 'I' && out[1] == 'S' && out[2] == 'C' && out[3] == 'C' && out[4] == '{')
{
atomicExch((int*)found, 1);
#pragma unroll
for (int i = 0; i < 16; i++) {
KeyOut[i] = key[i];
}
}
}
}

int main()
{
uint8_t EncFlag_user[]{ 0x9f, 0xb3, 0xb8, 0x2f, 0x23, 0x82, 0x19, 0x7d, 0xe7, 0x2a, 0x8b, 0x20, 0x0c, 0x5b, 0x79, 0x72, 0xf7, 0x07, 0xa0, 0x63, 0x53, 0x99, 0x49, 0xc2, 0x51, 0xde, 0x86, 0x1b, 0x87, 0x2f, 0x31, 0x6b, 0xbc, 0xfa, 0x5b, 0x9a, 0x6b, 0x12, 0x16, 0x8b, 0x31, 0xf6, 0x6c, 0x1d, 0x94, 0xba, 0x2f, 0x37 };

for (int i = 0; i < 48; i++)
EncFlag_user[i] ^= 0x10;

cudaMemcpyToSymbol(S, S_user, 256, 0, cudaMemcpyHostToDevice);

cudaMemcpyToSymbol(inv_S, inv_S_user, 256, 0, cudaMemcpyHostToDevice);

cudaMemcpyToSymbol(EncFlag, EncFlag_user, 16, 0, cudaMemcpyHostToDevice);

cudaMemcpyToSymbol(rcon, rcon_user, 40, 0, cudaMemcpyHostToDevice);

cudaMemcpyToSymbol(hash_plaintext_template, "ISCC{xxx}", 9, 0, cudaMemcpyHostToDevice);

uint8_t resultKey[16]{};
bool found_ = false;

uint8_t* KeyOut;
bool* found;

cudaMalloc(&KeyOut, 16 * sizeof(uint8_t));
cudaMalloc(&found, 1 * sizeof(bool));
cudaMemset(found, 0, sizeof(bool));

// 第一个字节在用户层循环
for (int i = 0; i < 94; i++)
{
printf("Tring crack the block %d\n", i);

const uint64_t TOTAL_COMBINATIONS = 94ULL * 95ULL * 0xffffULL;

uint64_t blockSize = 256;
uint64_t gridSize = (TOTAL_COMBINATIONS + blockSize - 1) / blockSize;
crack << <gridSize, blockSize >> > (KeyOut, found,i);
cudaDeviceSynchronize();

cudaMemcpy(&found_, found, 1, cudaMemcpyDeviceToHost);
if (found_)
{
cudaMemcpy(resultKey, KeyOut, 16, cudaMemcpyDeviceToHost);
printf("Found key: ");
for (int i = 0; i < 16; i++)
{
printf("0x%02x,", resultKey[i]);
}
printf("\n");
cudaMemset(found, 0, 1);
break;
}
else
{
printf("No result.\n");
}
}
cudaFree(KeyOut);
cudaFree(found);
return 0;
}

获得密钥解密

CUDA爆破得到密钥为:

0x54,0x8e,0x7a,0x3a,0x13,0x57,0xf3,0x00,0x00,0x3a,0x00,0x7a,0x00,0x8e,0x00,0x54

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main()
{
uint8_t EncFlag[]{ 0x9f, 0xb3, 0xb8, 0x2f, 0x23, 0x82, 0x19, 0x7d, 0xe7, 0x2a, 0x8b, 0x20, 0x0c, 0x5b, 0x79, 0x72, 0xf7, 0x07, 0xa0, 0x63, 0x53, 0x99, 0x49, 0xc2, 0x51, 0xde, 0x86, 0x1b, 0x87, 0x2f, 0x31, 0x6b, 0xbc, 0xfa, 0x5b, 0x9a, 0x6b, 0x12, 0x16, 0x8b, 0x31, 0xf6, 0x6c, 0x1d, 0x94, 0xba, 0x2f, 0x37 };
uint8_t AesKey[16]{ 0x54,0x8E,0x7A,0x3A,0x13,0x57,0xF3,0x00,0x00,0x3A,0x00,0x7A,0x00,0x8E,0x00,0x54 };
uint8_t Flag[48]{};

for (int i = 0; i < 48; i++)
{
EncFlag[i] ^= 0x10;
}

for (int i = 0; i < 48; i+=16)
{
aesDecrypt(AesKey, 16, EncFlag + i, Flag + i, 16);
}

printf("%.48s\n", Flag);
return 0;
}

解密得到Flag:

ISCC{A35_128_51pH4sh_2-4_CTF_K3y3d_H4sh}