这比赛3.2就开始了,我们当时没有听说,3.6才知道的,不过最后还是取得了不错的成绩. 在这里仅给出我个人解出的题目的WriteUp.
Reverse方向: ASM?Signin!: 编写解密脚本:
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 data1 = [ 0x26 , 0x27 , 0x24 , 0x25 , 0x2A , 0x2B , 0x28 , 0x00 , 0x2E , 0x2F , 0x2C , 0x2D , 0x32 , 0x33 , 0x30 , 0x00 , 0x36 , 0x37 , 0x34 , 0x35 , 0x3A , 0x3B , 0x38 , 0x39 , 0x3E , 0x3F , 0x3C , 0x3D , 0x3F , 0x27 , 0x34 , 0x11 ] blocks = [data1[i*4 :(i+1 )*4 ] for i in range (8 )] for i in range (8 ): si = i * 4 di = si + 4 if di >= 28 : di -= 28 block_si = si // 4 block_di = di // 4 blocks[block_si], blocks[block_di] = blocks[block_di], blocks[block_si] processed_data1 = [] for block in blocks: processed_data1.extend(block) data2 = [ 0x69 , 0x77 , 0x77 , 0x66 , 0x73 , 0x72 , 0x4F , 0x46 , 0x03 , 0x47 , 0x6F , 0x79 , 0x07 , 0x41 , 0x13 , 0x47 , 0x5E , 0x67 , 0x5F , 0x09 , 0x0F , 0x58 , 0x63 , 0x7D , 0x5F , 0x77 , 0x68 , 0x35 , 0x62 , 0x0D , 0x0D , 0x50 ] data2_blocks = [data2[i*4 :(i+1 )*4 ] for i in range (8 )] flag = bytearray () for i in range (8 ): block = processed_data1[i*4 : i*4 +4 ] b1, b2 = block[1 ], block[2 ] word1 = (b2 << 8 ) | b1 word2 = (block[3 ] << 8 ) | b2 enc_block = data2_blocks[i] enc_word1 = enc_block[0 ] | (enc_block[1 ] << 8 ) enc_word2 = enc_block[2 ] | (enc_block[3 ] << 8 ) dec_word1 = enc_word1 ^ word1 dec_word2 = enc_word2 ^ word2 flag.append(dec_word1 & 0xFF ) flag.append((dec_word1 >> 8 ) & 0xFF ) flag.append(dec_word2 & 0xFF ) flag.append((dec_word2 >> 8 ) & 0xFF ) flag_str = flag.decode('latin-1' ) print ("Flag:" , flag_str)
运行脚本得到flag:NSSCTF{W0w_y0u’re_g00d_@t_@5M!!} .
FishingKit: 查看main:
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 int __fastcall main (int argc, const char **argv, const char **envp) { memset (v4, 0 , 0x14u i64); memset (v5, 0 , 0x32u i64); memset (Str1, 0 , 0x32u i64); printf ("Give me the bait:" ); scanf ("%s" , v4); if ( check((unsigned __int8 *)v4) ) { printf ("Yes!This bait is a good one.\n\n" ); printf ("Give me the second thing:" ); scanf ("%s" , v5); printf ("\nFishing...\n\n" ); Sleep(0x3E8u ); RC4(v5, (__int64)Str1, v4); if ( !strcmp (Str1, &Str2) ) printf ("Did the fish take the bait?\n" ); else printf ("Didn't the fish take the bait?\n" ); } else { printf ("Oops...This bait is terrible.\n" ); } system("pause" ); return 0 ; }
发现首先要输入一个满足第一个校验函数的字符串bait,然后才能输入flag并用bait作为密钥将flag加密并和密文str2进行比较输出结果,先查看第一个校验函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 bool __fastcall check (unsigned __int8 *x) { return 202 * x[8 ] + 216 * x[5 ] - 4 * x[4 ] - 330 * x[9 ] - 13 * x[4 ] - 268 * x[6 ] == -14982 && 325 * x[8 ] + 195 * *x + 229 * x[1 ] - 121 * x[6 ] - 409 * x[6 ] - (x[1 ] << 7 ) == 22606 && 489 * x[1 ] + 480 * x[6 ] + 105 * x[2 ] + 367 * x[3 ] - 135 * x[4 ] - 482 * x[9 ] == 63236 && 493 * x[1 ] - 80 * x[4 ] - 253 * x[8 ] - 121 * x[2 ] - 177 * *x - 243 * x[9 ] == -39664 && 275 * x[4 ] + 271 * x[6 ] + 473 * x[7 ] - 72 * x[5 ] - 260 * x[4 ] - 367 * x[4 ] == 14255 && 286 * *x + 196 * x[7 ] + 483 * x[2 ] + 442 * x[1 ] - 495 * x[8 ] - 351 * x[4 ] == 41171 && 212 * x[2 ] + 283 * x[7 ] - 329 * x[8 ] - 429 * x[9 ] - 362 * x[2 ] - 261 * x[6 ] == -90284 && 456 * x[5 ] + 244 * x[7 ] + 92 * x[4 ] + 348 * x[7 ] - 225 * x[1 ] - 31 * x[2 ] == 88447 && 238 * x[9 ] + 278 * x[7 ] + 216 * x[6 ] + 237 * *x + 8 * x[2 ] - 17 * x[9 ] == 83838 && 323 * x[9 ] + 121 * x[1 ] + 370 * x[7 ] - (x[4 ] << 6 ) - 196 * x[9 ] - 422 * *x == 26467 && 166 * x[9 ] + 90 * x[1 ] + 499 * x[2 ] + 301 * x[8 ] - 31 * x[2 ] - 206 * x[2 ] == 88247 && 355 * *x + 282 * x[4 ] + 44 * x[9 ] + 359 * x[8 ] - 167 * x[5 ] - 62 * x[3 ] == 76658 && 488 * x[6 ] + 379 * x[9 ] + 318 * x[2 ] - 85 * x[1 ] - 357 * x[2 ] - 277 * x[5 ] == 35398 && 40 * *x + 281 * x[4 ] + 217 * x[5 ] - 241 * x[1 ] - 407 * x[7 ] - 309 * x[7 ] == -35436 && 429 * x[3 ] + 441 * x[3 ] + 115 * x[1 ] + 96 * x[8 ] + 464 * x[1 ] - 133 * x[7 ] == 157448 ; }
发现是一个矩阵,编写脚本解矩阵:
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 import numpy as npfrom scipy.optimize import fsolvedef equations (vars ): x = np.array(vars ) eq1 = 202 * x[8 ] + 216 * x[5 ] - 4 * x[4 ] - 330 * x[9 ] - 13 * x[4 ] - 268 * x[6 ] + 14982 eq2 = 325 * x[8 ] + 195 * x[0 ] + 229 * x[1 ] - 121 * x[6 ] - 409 * x[6 ] - 128 * x[1 ] - 22606 eq3 = 489 * x[1 ] + 480 * x[6 ] + 105 * x[2 ] + 367 * x[3 ] - 135 * x[4 ] - 482 * x[9 ] - 63236 eq4 = 493 * x[1 ] - 80 * x[4 ] - 253 * x[8 ] - 121 * x[2 ] - 177 * x[0 ] - 243 * x[9 ] + 39664 eq5 = 275 * x[4 ] + 271 * x[6 ] + 473 * x[7 ] - 72 * x[5 ] - 260 * x[4 ] - 367 * x[4 ] - 14255 eq6 = 286 * x[0 ] + 196 * x[7 ] + 483 * x[2 ] + 442 * x[1 ] - 495 * x[8 ] - 351 * x[4 ] - 41171 eq7 = 212 * x[2 ] + 283 * x[7 ] - 329 * x[8 ] - 429 * x[9 ] - 362 * x[2 ] - 261 * x[6 ] + 90284 eq8 = 456 * x[5 ] + 244 * x[7 ] + 92 * x[4 ] + 348 * x[7 ] - 225 * x[1 ] - 31 * x[2 ] - 88447 eq9 = 238 * x[9 ] + 278 * x[7 ] + 216 * x[6 ] + 237 * x[0 ] + 8 * x[2 ] - 17 * x[9 ] - 83838 eq10 = 323 * x[9 ] + 121 * x[1 ] + 370 * x[7 ] - 64 * x[4 ] - 196 * x[9 ] - 422 * x[0 ] - 26467 eq11 = 166 * x[9 ] + 90 * x[1 ] + 499 * x[2 ] + 301 * x[8 ] - 31 * x[2 ] - 206 * x[2 ] - 88247 eq12 = 355 * x[0 ] + 282 * x[4 ] + 44 * x[9 ] + 359 * x[8 ] - 167 * x[5 ] - 62 * x[3 ] - 76658 eq13 = 488 * x[6 ] + 379 * x[9 ] + 318 * x[2 ] - 85 * x[1 ] - 357 * x[2 ] - 277 * x[5 ] - 35398 eq14 = 40 * x[0 ] + 281 * x[4 ] + 217 * x[5 ] - 241 * x[1 ] - 407 * x[7 ] - 309 * x[7 ] + 35436 eq15 = 429 * x[3 ] + 441 * x[3 ] + 115 * x[1 ] + 96 * x[8 ] + 464 * x[1 ] - 133 * x[7 ] - 157448 return [eq1, eq2, eq3, eq4, eq5, eq6, eq7, eq8, eq9, eq10, eq11, eq12, eq13, eq14, eq15] initial_guess = np.zeros(15 ) solution = fsolve(equations, initial_guess) print (solution)for i in range (10 ): print (round (solution[i]),end=" " ) print ()for i in range (10 ): print (hex (round (solution[i])), end=" " ) print ()
运行脚本得到bait为DeluxeBait,接着看后面的RC4,发现多异或了0x14
,提取密文:[0xE9, 0x37, 0xF8, 0xE2, 0x0C, 0x0F, 0x3D, 0xB9, 0x5C, 0xA3, 0xDE, 0x2D, 0x55, 0x96, 0xDF, 0xA2, 0x35, 0xFE, 0xB3, 0xDD, 0x7F, 0x91, 0x3C] ,RC4解密,发现得到的是一个fakeflag,检查发现藏了很多个版本的RC4,有异或0x33、0x66和0x14的,并且还藏了一个生成24字节密文的函数,提取这个密文:[0x21, 0x56, 0x97, 0xa6, 0x1a, 0xd5, 0xc4, 0xde, 0xa4, 0x9c, 0x82, 0x4d, 0xd1, 0x45, 0xc8, 0x56, 0xa7, 0xb4, 0x96, 0x5c, 0x4d, 0x49, 0x87, 0x20] ,动态调试发现在比较时跳转到了一个有多重XTEA加密的函数:
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 __int64 __fastcall dup_XTEA (_BYTE *a1, unsigned __int8 *a2) { v34 = a1; memset (v32, 0 , 0x14u i64); memset (v33, 0 , 0x32u i64); for ( i = 0 ; i < 20 ; ++i ) *((_BYTE *)v32 + i) = a1[i - 80 ]; for ( j = 0 ; j < 50 ; ++j ) *((_BYTE *)v33 + j) = a1[j - 56 ]; v3 = 0 ; v6 = v33[0 ]; v9 = v33[1 ]; for ( k = 0 ; k < 24 ; ++k ) { v6 += (v32[v3 & 3 ] + v3) ^ (v9 + ((v9 >> 5 ) ^ (16 * v9))); v3 += 0x66778899 ; v9 += (v32[(v3 >> 11 ) & 3 ] + v3) ^ (v6 + ((v6 >> 5 ) ^ (16 * v6))); } v33[0 ] = v6; v33[1 ] = v9; v4 = 0 ; v7 = v33[2 ]; v10 = v33[3 ]; for ( m = 0 ; m < 24 ; ++m ) { v7 += (v32[v4 & 3 ] + v4) ^ (v10 + ((v10 >> 5 ) ^ (16 * v10))); v4 += 0x66778899 ; v10 += (v32[(v4 >> 11 ) & 3 ] + v4) ^ (v7 + ((v7 >> 5 ) ^ (16 * v7))); } v33[2 ] = v7; v33[3 ] = v10; v5 = 0 ; v8 = v33[4 ]; v11 = v33[5 ]; for ( n = 0 ; n < 24 ; ++n ) { v8 += (v32[v5 & 3 ] + v5) ^ (v11 + ((v11 >> 5 ) ^ (16 * v11))); v5 += 0x66778899 ; v11 += (v32[(v5 >> 11 ) & 3 ] + v5) ^ (v8 + ((v8 >> 5 ) ^ (16 * v8))); } v33[4 ] = v8; v33[5 ] = v11; v12 = 1 ; for ( ii = 0 ; ii < 24 ; ++ii ) { if ( cipher[ii] != *((unsigned __int8 *)v33 + ii) ) { v12 = 0 ; break ; } } if ( v12 ) { strcpy (LibFileName, "dbtc\"#?u}}" ); for ( jj = 0 ; jj < 10 ; ++jj ) LibFileName[jj] ^= 0x11u ; hModule = LoadLibraryA(LibFileName); strcpy (ProcName, "\\tbbpvtS~iP" ); for ( kk = 0 ; kk < 11 ; ++kk ) ProcName[kk] ^= 0x11u ; ProcAddress = GetProcAddress(hModule, ProcName); strcpy (v31, "H~d6gt1rpdvye1p1sxv1wxby0" ); for ( mm = 0 ; mm < 25 ; ++mm ) v31[mm] ^= 0x11u ; qmemcpy(v28, "R~" , 2 ); v28[2 ] = 127 ; qmemcpy(v29, "vcped}pex~" , 10 ); v29[10 ] = 127 ; strcpy (v30, "000" ); for ( nn = 0 ; nn < 17 ; ++nn ) v28[nn] ^= 0x11u ; ((void (__fastcall *)(_QWORD, char *, char *, _QWORD))ProcAddress)(0 i64, v31, v28, 0 i64); } while ( 1 ) { v20 = (unsigned __int8)*v34 - *a2; if ( v20 || !*v34 ) break ; ++v34; ++a2; } if ( v20 > 0 ) return 1 i64; if ( v20 >= 0 ) return 0 i64; return 0xFFFFFFFF i64; }
发现在这个函数中用DeluxeBait
作为密钥,输入的字符串作为密文进行了delta为0x66778899
的XTEA加密并和密文进行了比较,而这里的密文正是之前所见的第二个密文,编写解密脚本进行解密:
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 def bytes_to_dwords_little_endian (byte_array ): return [int .from_bytes(byte_array[i:i+4 ], byteorder='little' , signed=False ) for i in range (0 , len (byte_array), 4 )] def dwords_to_bytes_little_endian (dword_array ): byte_list = [] for dword in dword_array: byte_list.extend(dword.to_bytes(4 , byteorder='little' )) return byte_list delta = 0x66778899 key = [0x756C6544 , 0x61426578 , 0x00007469 , 0x00000000 ] ttl = 0 encflag = [0x21 , 0x56 , 0x97 , 0xa6 , 0x1a , 0xd5 , 0xc4 , 0xde , 0xa4 , 0x9c , 0x82 , 0x4d , 0xd1 , 0x45 , 0xc8 , 0x56 , 0xa7 , 0xb4 , 0x96 , 0x5c , 0x4d , 0x49 , 0x87 , 0x20 ] dwordenc = bytes_to_dwords_little_endian(encflag) result = [] for j in range (0 ,len (dwordenc)//2 ): m1 = dwordenc[2 *j] m2 = dwordenc[2 *j+1 ] ttl = (0 + 24 * delta) & 0xFFFFFFFF for j in range (24 ): m2 = (m2 - ((((m1 >> 5 ) ^ (m1 << 4 )) + m1) ^ (ttl + key[(ttl >> 11 ) & 3 ]))) & 0xFFFFFFFF ttl = (ttl - delta) & 0xFFFFFFFF m1 = (m1 - ((((m2 >> 5 ) ^ (m2 << 4 )) + m2) ^ (ttl + key[ttl & 3 ]))) & 0xFFFFFFFF result.append(m1) result.append(m2) flag = dwords_to_bytes_little_endian(result) for i in range (len (flag)): print (hex (flag[i]),end=", " ) print ()
运行得到flag:NSSCTF{Wh@t_@_b1g_F1sh} .
LockedSecret: 查壳发现UPX012和UPX!标识符被换成了LIVV,改回去之后脱壳,查看main:
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 int __cdecl main (int argc, const char **argv, const char **envp) { int i; char Str[256 ]; memset (Str, 0 , sizeof (Str)); printf ("Input your flag:" ); scanf ("%32s" , (char )Str); if ( strlen (Str) != 32 ) { printf ("Wrong length!\n" ); system("pause" ); exit (0 ); } XorKeyGen(); MyTEA((int )Str); for ( i = 0 ; i < 32 ; ++i ) { if ( cipher[i] != Str[i] ) { printf ("Wrong!\n" ); system("pause" ); exit (0 ); } } printf ("Right!\n" ); system("pause" ); return 0 ; }
显然flag长度为32,查看XorKeyGen,发现用固定的rand种子生成了一个密钥,动态调试得到值[0x64, 0x96, 0x50, 0x16, 0x69, 0xff, 0xbe, 0x60] ,查看加密,发现是一个8轮的TEA加密,密钥为IamTheKeyYouKnow
循环异或刚才的8字节的值,delta为0x5E2377FF
,编写脚本解密:
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 def dwords_to_bytes_little_endian (dword_array ): byte_list = [] for dword in dword_array: byte_list.extend(dword.to_bytes(4 , byteorder='little' )) return byte_list def bytes_to_dwords_little_endian (byte_array ): return [int .from_bytes(byte_array[i:i+4 ], byteorder='little' , signed=False )for i in range (0 , len (byte_array), 4 )] test = [0x61 , 0x62 , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6a , 0x6b , 0x6c , 0x6d , 0x6e , 0x6f , 0x70 , 0x71 , 0x72 , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7a , 0x31 , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 ] key = [0x49 , 0x61 , 0x6d , 0x54 , 0x68 , 0x65 , 0x4b , 0x65 , 0x79 , 0x59 , 0x6f , 0x75 , 0x4b , 0x6e , 0x6f , 0x77 ] xor = [0x64 , 0x96 , 0x50 , 0x16 , 0x69 , 0xff , 0xbe , 0x60 ] delta = 0x5E2377FF for i in range (len (key)-1 ): key[i] ^= xor[i % 8 ] k = bytes_to_dwords_little_endian(key) dwordenc = bytes_to_dwords_little_endian(test) dwordresult = [] for i in range (len (dwordenc)//2 ): v0 = dwordenc[2 * i] v1 = dwordenc[2 * i + 1 ] ttl = 0 for j in range (8 ): ttl = (ttl + delta) & 0xFFFFFFFF v0 = (v0 + (((v1 << 4 ) + k[0 ]) ^ (v1 + ttl) ^ ((v1 >> 5 ) + k[1 ]))) & 0xFFFFFFFF v1 = (v1 + (((v0 << 4 ) + k[2 ]) ^ (v0 + ttl) ^ ((v0 >> 5 ) + k[3 ]))) & 0xFFFFFFFF dwordresult.append(v0 ^ 0xF ) dwordresult.append(v1 ^ 0xF ) result = dwords_to_bytes_little_endian(dwordresult) for i in range (len (dwordresult)): print (hex (dwordresult[i]),end=", " ) print ()for i in range (len (result)): print (hex (result[i]),end=", " ) print ()encflag = [0xDC , 0x45 , 0x1E , 0x03 , 0x89 , 0xE9 , 0x76 , 0x27 , 0x47 , 0x48 , 0x23 , 0x01 , 0x70 , 0xD2 , 0xCE , 0x64 , 0xDA , 0x7F , 0x46 , 0x33 , 0xB1 , 0x03 , 0x49 , 0xA3 , 0x27 , 0x00 , 0xD1 , 0x2C , 0x37 , 0xB3 , 0xBD , 0x75 ] dwordencflag = bytes_to_dwords_little_endian(encflag) for i in range (len (dwordencflag)): dwordencflag[i] ^= 0xF dwordrev = [] for i in range (len (dwordencflag)//2 ): v0 = dwordencflag[2 * i] v1 = dwordencflag[2 * i + 1 ] ttl = (8 * delta) & 0xFFFFFFFF for j in range (8 ): v1 = (v1 - (((v0 << 4 ) + k[2 ]) ^ (v0 + ttl) ^ ((v0 >> 5 ) + k[3 ]))) & 0xFFFFFFFF v0 = (v0 - (((v1 << 4 ) + k[0 ]) ^ (v1 + ttl) ^ ((v1 >> 5 ) + k[1 ]))) & 0xFFFFFFFF ttl = (ttl - delta) & 0xFFFFFFFF dwordrev.append(v0) dwordrev.append(v1) rev = dwords_to_bytes_little_endian(dwordrev) bflag = bytearray () for i in range (len (rev)): bflag.append(rev[i]) print (bflag)mapflag = bflag.decode() print ("OutpFlag:" , mapflag)
运行脚本得到flag:NSSCTF{!!!Y0u_g3t_th3_s3cr3t!!!} .
Mio?Ryo?Soyo?: 发现是python打包,解包后反编译program.pyc,发现程序结构很简单:
1 2 3 4 5 6 7 8 9 10 from Secret import *if __name__ == "__main__" : print ("输入:" , end="" ) aaaaaaaaaaaaa = input () wwwwwwwwwww = l(aaaaaaaaaaaaa) if sssssssssssss == wwwwwwwwwww.encode(): print ("哦,对的。" ) else : print ("哎,并非。" ) input ()
所以需要关注的是l函数和s密文,找到Secret库进行反编译:
1 2 3 4 5 6 7 8 from SecretEncrypt import *sssssssssssss = bytes ([57 , 118 , 33 , 114 , 68 , 56 , 117 , 115 , 34 , 52 , 52 , 95 , 78 , 40 , 49 , 59 , 95 , 85 , 63 , 122 , 54 , 33 , 77 , 110 , 49 , 54 , 34 , 109 , 106 , 122 , 60 , 92 , 108 , 91 , 61 , 51 , 42 , 62 , 35 , 38 , 52 , 67 , 62 , 122 , 116 , 48 , 76 , 50 , 67 , 51 , 59 , 41 , 122 , 45 , 45 , 51 , 90 ]) def l (_: str ): return SSSooooyyooo(MMMMiiiiiio.MMMMiiooooooo(SSSooooyyooo(RRRRyyooo.RRRRRRRyyyyooooo(_.encode()), 7 ).encode()), 9 )
反编译SecretEncrypt:
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 import mathclass MMMMiiiiiio : MMiiiiiiooo = "" .join([chr (Miiooooooooo) for Miiooooooooo in range (33 , 118 )]) @staticmethod def MMMMiiooooooo (MMMMMMMMMiiiooo: bytes ) -> str : MMMMiiiiioooooooooo = "" MMMMMMMiiiiioo = (4 - len (MMMMMMMMMiiiooo) % 4 ) % 4 MMMMMMMMMiiiooo += b'\x00' * MMMMMMMiiiiioo for MMMMMMiiiiiio in range (0 , len (MMMMMMMMMiiiooo), 4 ): MMMMiiiiiiooooo = MMMMMMMMMiiiooo[MMMMMMiiiiiio[:MMMMMMiiiiiio + 4 ]] MMMMMMiiioooooo = int .from_bytes(MMMMiiiiiiooooo, "big" ) MMMMMMMiiooooooooo = "" for _ in range (5 ): MMMMMMMiiooooooooo = MMMMiiiiiio.MMiiiiiiooo[MMMMMMiiioooooo % 85 ] + MMMMMMMiiooooooooo MMMMMMiiioooooo //= 85 else : MMMMiiiiioooooooooo += MMMMMMMiiooooooooo else : if MMMMMMMiiiiioo: MMMMiiiiioooooooooo = MMMMiiiiioooooooooo[None [:-MMMMMMMiiiiioo]] return MMMMiiiiioooooooooo class RRRRyyooo : RRRRyooooooo = "" .join([chr (RRRRRRRyyyyyoooo) for RRRRRRRyyyyyoooo in range (48 , 93 )]) @staticmethod def RRRRRRRyyyyooooo (RRRRRRyyyoooooo: bytes ) -> str : RRRRyyyyyooo = [] RRyyyyyyyyyoooooo = 0 while RRyyyyyyyyyoooooo < len (RRRRRRyyyoooooo): if RRyyyyyyyyyoooooo + 1 < len (RRRRRRyyyoooooo): RRRRRRRRRyyo = RRRRRRyyyoooooo[RRyyyyyyyyyoooooo] << 8 | RRRRRRyyyoooooo[RRyyyyyyyyyoooooo + 1 ] RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45 ]) RRRRRRRRRyyo //= 45 RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45 ]) RRRRRRRRRyyo //= 45 RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo]) RRyyyyyyyyyoooooo += 2 else : RRRRRRRRRyyo = RRRRRRyyyoooooo[RRyyyyyyyyyoooooo] RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo % 45 ]) RRRRRRRRRyyo //= 45 RRRRyyyyyooo.append(RRRRyyooo.RRRRyooooooo[RRRRRRRRRyyo]) RRyyyyyyyyyoooooo += 1 return "" .join(RRRRyyyyyooo) def SSSooooyyooo (SSSSooyoooooo, SSSSSoyyooooo ): SSoooooyyyyyyoo = [] for SSSSSSSSSoyooo in SSSSooyoooooo: if "a" <= SSSSSSSSSoyooo <= "z" : SSSSoooyooooooo = (ord (SSSSSSSSSoyooo) - ord ("a" ) + SSSSSoyyooooo) % 26 SSoooooyyyyyyoo.append(chr (ord ("a" ) + SSSSoooyooooooo)) elif "0" <= SSSSSSSSSoyooo <= "9" : SSSSoooyooooooo = (ord (SSSSSSSSSoyooo) - ord ("0" ) - SSSSSoyyooooo) % 10 SSoooooyyyyyyoo.append(chr (ord ("0" ) + SSSSoooyooooooo)) else : SSoooooyyyyyyoo.append(SSSSSSSSSoyooo) else : return "" .join(SSoooooyyyyyyoo)
去除混淆:
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 import mathclass M : M1 = "" .join([chr (idxM) for idxM in range (33 , 118 )]) @staticmethod def funM (inputM: bytes ) -> str : outputM = "" infM = (4 - len (inputM) % 4 ) % 4 inputM += b'\x00' * infM for iM in range (0 , len (inputM), 4 ): tempM = inputM[iM[:iM + 4 ]] intM = int .from_bytes(tempM, "big" ) strM = "" for _ in range (5 ): strM = M.M1[intM % 85 ] + strM intM //= 85 else : outputM += strM else : if infM: outputM = outputM[None [:-infM]] return outputM class R : R1 = "" .join([chr (idxR) for idxR in range (48 , 93 )]) @staticmethod def funR (inputR: bytes ) -> str : listR = [] iR = 0 while iR < len (inputR): if iR + 1 < len (inputR): tempR = inputR[iR] << 8 | inputR[iR + 1 ] listR.append(R.R1[tempR % 45 ]) tempR //= 45 listR.append(R.R1[tempR % 45 ]) tempR //= 45 listR.append(R.R1[tempR]) iR += 2 else : tempR = inputR[iR] listR.append(R.R1[tempR % 45 ]) tempR //= 45 listR.append(R.R1[tempR]) iR += 1 return "" .join(listR) def S (S1, S2 ): listS = [] for iS in S1: if "a" <= iS <= "z" : tempS = (ord (iS) - ord ("a" ) + S2) % 26 listS.append(chr (ord ("a" ) + tempS)) elif "0" <= iS <= "9" : tempS = (ord (iS) - ord ("0" ) - S2) % 10 listS.append(chr (ord ("0" ) + tempS)) else : listS.append(iS) else : return "" .join(listS) cipher = bytes ([57 , 118 , 33 , 114 , 68 , 56 , 117 , 115 , 34 , 52 , 52 , 95 , 78 , 40 , 49 , 59 , 95 , 85 , 63 , 122 , 54 , 33 , 77 , 110 , 49 , 54 , 34 , 109 , 106 , 122 , 60 , 92 , 108 , 91 , 61 , 51 , 42 , 62 , 35 , 38 , 52 , 67 , 62 , 122 , 116 , 48 , 76 , 50 , 67 , 51 , 59 , 41 , 122 , 45 , 45 , 51 , 90 ]) def l (_: str ): return S(M.funM(S(R.funR(_.encode()), 7 ).encode()), 9 ) if __name__ == "__main__" : print ("输入:" , end="" ) message = input () enc = l(message) if cipher == enc.encode(): print ("哦,对的。" ) else : print ("哎,并非。" ) input ()
发现S函数是一个凯撒加密,R函数是一个base45编码,M函数是一个base85编码,则加密方式为:
base45→偏移7→base85→偏移9
编写S函数的逆向脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def S_inverse (S1: str , S2: int ) -> str : listS = [] for iS in S1: if "a" <= iS <= "z" : tempS = (ord (iS) - ord ("a" ) - S2) % 26 listS.append(chr (ord ("a" ) + tempS)) elif "0" <= iS <= "9" : tempS = (ord (iS) - ord ("0" ) + S2) % 10 listS.append(chr (ord ("0" ) + tempS)) else : listS.append(iS) return "" .join(listS) enc = bytearray ([0x39 , 0x76 , 0x21 , 0x72 , 0x44 , 0x38 , 0x75 , 0x73 , 0x22 , 0x34 , 0x34 , 0x5f , 0x4e , 0x28 , 0x31 , 0x3b , 0x5f , 0x55 , 0x3f , 0x7a , 0x36 , 0x21 , 0x4d , 0x6e , 0x31 , 0x36 , 0x22 , 0x6d , 0x6a , 0x7a , 0x3c , 0x5c , 0x6c , 0x5b , 0x3d , 0x33 , 0x2a , 0x3e , 0x23 , 0x26 , 0x34 , 0x43 , 0x3e , 0x7a , 0x74 , 0x30 , 0x4c , 0x32 , 0x43 , 0x33 , 0x3b , 0x29 , 0x7a , 0x2d , 0x2d , 0x33 , 0x5a ]) encrypted_message = enc.decode() shift_value = 9 decrypted_message = S_inverse(encrypted_message, shift_value) print (f"{decrypted_message} " )
输出8m!iD7lj"33_N(0;_U?q5!Me05"daq<\c[=2*>#&3C>qk9L1C2;)q--2Z
,base85解码得到:JX2NG:CM:KJ?S0=:>?NC>K5<V29Z5<Y:9C=;LA1RQ9G:7
,再次偏移7得到:JX9NG:CM:KJ?S7=:>?NC>K2<V96Z2<Y:6C=;LA8RQ6G:4
,base45解码:
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 class R : R1 = "" .join(chr (i) for i in range (48 , 93 )) def decode_R (s ): result = bytearray () i = 0 while i < len (s): if i + 2 < len (s): c0, c1, c2 = [R.R1.index(s[i+j]) for j in range (3 )] tempR = c0 + c1 * 45 + c2 * 45 **2 result.extend([(tempR >> 8 ) & 0xFF , tempR & 0xFF ]) i += 3 elif i + 1 < len (s): c0, c1 = [R.R1.index(s[i+j]) for j in range (2 )] tempR = c0 + c1 * 45 result.append(tempR) i += 2 else : raise ValueError("Invalid length" ) return bytes (result) step4 = "JX9NG:CM:KJ?S7=:>?NC>K2<V96Z2<Y:6C=;LA8RQ6G:4" step5 = decode_R(step4) flag = step5.decode() print (flag)
得到flag:NSSCTF{Th3y’r3_a11_p1aY_Ba5e!} .
TimeSpaceRescue: 查看main:
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 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; int i; char Str[256 ]; int Src[4 ]; printf ("Before input the key, make sure you're on the same spacetime as Liv.\n" ); printf ("Try your key:" ); memset (Str, 0 , sizeof (Str)); scanf ("%s" , Str); memset (Src, 0 , sizeof (Src)); sub_401DD0((int )Src); v4 = strlen (Str); sub_401210(Src, 0x10u , (int )Str, v4); for ( i = 0 ; i < v4; ++i ) { if ( Str[i] != cipher[i] ) { printf ("Spacetime turbulence is detected, and rescue fails!\n" ); system("pause" ); exit (0 ); } } printf ("Congratulations on rescuing Liv and successfully protecting the world!\n" ); system("pause" ); return 0 ; }
查看src的初始化函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int __cdecl sub_151DD0 (int a1) { __time64_t v1; HANDLE CurrentProcess; int v4; int i; __time64_t Time; BOOL pbDebuggerPresent; int Src[3 ]; LODWORD(v1) = sub_402180(0 ); Time = v1; v4 = getlocaltime(&Time); memset (Src, 0 , sizeof (Src)); pbDebuggerPresent = 0 ; CheckRemoteDebuggerPresent(CurrentProcess, &pbDebuggerPresent); if ( !pbDebuggerPresent ) memcpy (Src, (const void *)(v4 + 12 ), sizeof (Src)); sub_1521A0(Src, 0xCu , a1); for ( i = 0 ; i < 16 ; ++i ) *(_BYTE *)(i + a1) ^= 0x14u ; return 39 ; }
发现有一个反调试,结尾还有一个诡异的return 39,查看汇编后发现藏了典型的call $+5花指令,抹掉后得到正确的函数:
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 unsigned int __cdecl sub_401DD0 (int a1) { __time64_t v1; HANDLE CurrentProcess; unsigned int result; struct tm *v4 ; int j; int i; __time64_t Time; BOOL pbDebuggerPresent; int Src[3 ]; Time = time64_(0 ); v3 = localtime64_(&Time); memset (Src, 0 , sizeof (Src)); pbDebuggerPresent = 0 ; CurrentProcess = GetCurrentProcess(); CheckRemoteDebuggerPresent(CurrentProcess, &pbDebuggerPresent); if ( !pbDebuggerPresent ) memcpy (Src, &v4->tm_mday, sizeof (Src)); result = sub_4021A0(Src, 0xCu , a1); for ( i = 0 ; i < 16 ; ++i ) { result = (*(unsigned __int8 *)(i + a1) ^ 0x114 ) & 0x800000FF ; *(_BYTE *)(i + a1) ^= 0x14u ; } for ( j = 0 ; j < 16 ; ++j ) { result = j + a1; *(_BYTE *)(j + a1) ^= 0x11u ; } return result; }
查看其中的sub函数,发现是16字节md5哈希,则这里的用意是选取2024年某月某日的系统时间表达中的日、月、年三个dword,对其求16字节md5哈希,并异或0x14和0x11之后返回(生成密钥),接着看加密函数,发现加密函数也有花指令,去掉之后发现是一个魔改的AES-ECB并在结尾多判定是否要额外异或一个0x11,这个值由之前对于Ntdll的是否处于调试模式的判断生成,非调试模式下为0,不会额外异或0x11,至于魔改,则是在加密前对密钥和明文进行了预处理,将密钥相邻字节交换并异或0x5(将刚才的异或0x14和0x11抵消),明文则逆序交换字节并异或0xF,加密完成后又对密文进行了相邻字节交换和异或0x5,编写同构加密脚本验证发现结果相符,编写脚本解密:
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 import hashlibfrom Crypto.Cipher import AESdef reverse_operations (cipher_text ): for i in range (len (cipher_text)//2 ): temp = cipher_text[2 *i] ^ 0x5 cipher_text[2 *i] = cipher_text[2 *i+1 ] ^ 0x5 cipher_text[2 *i+1 ] = temp return cipher_text def decrypt_and_reverse_aes_ecb (cipher_text, key ): cipher = AES.new(key, AES.MODE_ECB) plain_text = bytearray (cipher.decrypt(cipher_text)) for i in range (8 ): temp1 = plain_text[i] ^ 0xF temp2 = plain_text[16 +i] ^ 0xF plain_text[i] = plain_text[15 -i] ^ 0xF plain_text[16 +i] = plain_text[31 -i] ^ 0xF plain_text[15 -i] = temp1 plain_text[31 -i] = temp2 return plain_text def find_key_and_decrypt (cipher_text, tm_year ): for day in range (1 , 32 ): for tm_mon in range (1 , 13 ): src = (day.to_bytes(4 , 'little' ) + tm_mon.to_bytes(4 , 'little' ) + tm_year.to_bytes(4 , 'little' )) key = bytearray (hashlib.md5(src).digest()) for i in range (len (key)//2 ): temp = key[2 *i+1 ] key[2 *i+1 ] = key[2 *i] key[2 *i] = temp try : reversed_cipher_text = reverse_operations(bytearray (cipher_text)) decrypted = decrypt_and_reverse_aes_ecb(reversed_cipher_text, key) if decrypted.decode()[:6 ] == "NSSCTF" : print (f"Match time found: Day={day} , Month={tm_mon} " ) print ("Decrypted text:" , decrypted.decode()) exit(0 ) except Exception as e: continue cipher_text = bytearray (b'\xcd\x16\xdb\xb5\xd1\x02\xa4\x82\x8e\x59\x73\x9e\x96\x26\x56\xf2\x16\x8e\x46\xf2\x55\x7b\x92\x31\x30\xdc\xaa\x8a\xf3\x1c\xa0\xaa' ) tm_year = 124 find_key_and_decrypt(cipher_text, tm_year)
运行输出flag:NSSCTF{W0w_Y0u’re_@n_AE5_M@5t3r} .
Room 0: 题目提示了除以零的异常和SMC,发现程序字节码最上端有一个.enc字段,猜测是SMC,再往下还有一个无法反编译的区域,发现有花指令,抹除后发现是SMC的解密函数,先查看main:
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 int __cdecl main (int argc, const char **argv, const char **envp) { int i; char v5[256 ]; char Str[256 ]; CPPEH_RECORD ms_exc; ms_exc.registration.TryLevel = 0 ; printf ("Welcome to the hostel!\n" ); printf ("Please input your informatioin to enter your room.\n" ); printf ("Input your flag:" ); memset (Str, 0 , sizeof (Str)); scanf ("%s" , (char )Str); printf ("Input your Key:" ); memset (v5, 0 , sizeof (v5)); scanf ("%s" , (char )v5); if ( KeyGen (v5) != 0x11451419 ) { printf ("Not the key.\n" ); exit (0 ); } RC4encrypt (Str, 0x11451419 ); for ( i = 0 ; i < 32 ; ++i ) { if ( Str[i] != (unsigned __int8)cipher[i] ) { printf ("Your flag is incorrect.\n" ); exit (0 ); } } printf ("Welcome into your room\n" ); system("pause" ); return 0 ; }
发现要输入key和flag,查看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 int __cdecl KeyGen (char *Str) { int v2; int i; int v4; int v5; int v6; int v7; unsigned int v8; int v9; v2 = toInt(Str); if ( !v2 ) return 0 ; v6 = 0 ; v9 = v2; v8 = HIBYTE(v2); v5 = BYTE2(v2); v4 = BYTE1(v2); for ( i = 0 ; i < 32 ; ++i ) { v7 = v6 * (v8 + 0x5464A178 ) * (v9 - 0x57780FDF ) * ((v8 - v9) ^ (v8 >> 4 )); v5 = (v9 + v5) ^ (8 * v4); v4 = (v9 + v8) ^ (8 * v5); v8 = (v9 + v4) ^ (8 * v5); v9 -= v4 + v5 + v8; v6 = v7 + (v8 + 0x57780FDF ) * (((v8 - v9) ^ (unsigned __int64)(v8 >> 4 )) / (unsigned int )(v9 - 0x5464A178 )); } return v9 ^ v6; }
发现这貌似是一个单向函数,而且后文加密的时候并不需要用到key的值,所以直接在判定处进行patch,抹掉判定继续执行,发现下方的加密函数是对密文进行了密钥为0x11451419(32位int按小端序转4字节为[0x19, 0x14, 0x45, 0x11]),但对密文直接解密发现是fakeflag,说明刚才的SMC和异常处理应该也派上了用场,仔细查看main,发现有一个except分支,但是没有见到能触发的地方,调试过程中偶然发现特殊情况下跳转到了这个分支,仔细查看发现在KeyGen里面有一个div ecx,猜测若密钥正常,这一步应该是除以0 (对应上文v9 - 0x5464A178 = 0),动态调试到这一步直接修改ecx的值,触发除零异常,进入except分支,首先又调用了toint,然后进入了SMC函数,将SMC后的enc区段进行修改,观察发现enc区段末尾有大量重复的0xD3 0x75 0x5F 0xF0(真正的key是这四个字节的其中一种小端序顺序组合:755ff0d3),推测这里要循环异或这四个字节(也对应之前的32位int),编写idapython脚本让地址00401017开始的1513字节循环异或:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import ida_bytesdef cyclic_xor (start_addr, data_len, xor_key ): data = ida_bytes.get_bytes(start_addr, data_len) if data is None : print (f"错误:无法读取地址 0x{start_addr:X} 处的 {data_len} 字节" ) return False data_list = bytearray (data) key_length = len (xor_key) for i in range (data_len): key_index = i % key_length data_list[i] ^= xor_key[key_index] success = ida_bytes.patch_bytes(start_addr, bytes (data_list)) if not success: print ("错误:写入数据失败" ) return False idc.analyze_area(start_addr, start_addr + data_len) print ("异或操作完成" ) return True start_addr = 0x00401017 data_len = 1513 xor_key = [0xD3 , 0x75 , 0x5F , 0xF0 ] cyclic_xor(start_addr, data_len, xor_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 void __cdecl __noreturn sub_401000 (char *Str, unsigned int a2) { char v2; size_t v3; char v4; int v5; int ii; size_t n; int k; int j; int m; int i; char v12; char v13; unsigned __int8 v14; unsigned __int8 v15; char v16[256 ]; char v17[256 ]; int v18[2 ]; v18[0 ] = 0 ; v18[1 ] = 0 ; memset (v17, 0 , sizeof (v17)); for ( i = 0 ; i < 8 ; ++i ) { if ( i >= 4 ) v4 = 7 - i; else v4 = i; *((_BYTE *)v18 + i) = a2 >> (8 * v4); } for ( j = 0 ; j < 256 ; ++j ) v17[j] = j; memset (v16, 0 , sizeof (v16)); for ( k = 0 ; k < 256 ; ++k ) v16[k] = *((_BYTE *)v18 + k % 8 ); v5 = 0 ; for ( m = 0 ; m < 256 ; ++m ) { v5 = ((unsigned __int8)v16[m] + v5 + (unsigned __int8)v17[m]) % 256 ; v13 = v17[m]; v17[m] = v17[v5]; v17[v5] = v13; } v15 = 0 ; v14 = 0 ; v3 = strlen (Str); for ( n = 0 ; n < v3; ++n ) { v14 += v17[++v15]; v12 = v17[v15]; v17[v15] = v17[v14]; v17[v14] = v12; Str[n] ^= *((_BYTE *)v18 + (v15 & 7 )) ^ v17[((unsigned __int8)v17[v14] + (unsigned __int8)v17[v15]) % 256 ]; } for ( ii = 0 ; ii < 32 ; ++ii ) { if ( byte_405020[ii] != Str[ii] ) { sub_4028C0("You shouldn't come here, get out of that room!\n" , v2); exit (0 ); } } sub_4028C0("Welcome to Room-0.\n" , v2); system("pause" ); exit (0 ); }
发现是一个魔改RC4,在PRGA阶段不仅使用了S盒生成的密钥流,还额外使用了 v18 中的一个字节(通过 (v15 & 7) 来索引),密钥是刚才的4字节的正反(8字节),动态调试得到密钥[0xD4, 0x35, 0x6D, 0xF8, 0xF8, 0x6D, 0x35, 0xD4] ,密文也变了,编写脚本解密:
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 def rc4_modified_encrypt (plaintext, key ): S = list (range (256 )) K = key * (256 // len (key)) j = 0 for i in range (256 ): j = (j + S[i] + K[i]) % 256 S[i], S[j] = S[j], S[i] i = 0 j = 0 output = bytearray () for byte in plaintext: i = (i + 1 ) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] t = (S[i] + S[j]) % 256 prga_output = S[t] modified_byte = byte ^ prga_output ^ key[i % len (key)] output.append(modified_byte) return bytes (output) key = [0xD4 , 0x35 , 0x6D , 0xF8 , 0xF8 , 0x6D , 0x35 , 0xD4 ] plaintext = b"\x22\xc4\xa0\x5a\xde\xed\x62\x5e\x25\xe2\x6d\xa6\x05\xa7\x20\x8d\x7d\x99\x52\x3e\x8c\xa7\x7f\xfa\x09\xd8\x62\xdb\x00\x80\xc2\xa9" ciphertext = rc4_modified_encrypt(plaintext, key) print (f"Ciphertext: {ciphertext} " )
运行得到flag:NSSCTF{Int3r3st1ng_5MC_Pr0gr@m?} .
Canon: 观察main:
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 int __fastcall main (int argc, const char **argv, const char **envp) { int j; int i; int v6; int v7[4 ]; __int64 v8; __int64 v9; __int64 v10; int v11[8 ]; int v12[4 ]; char Str[12 ]; char Source[12 ]; char v15[16 ]; char Destination[112 ]; char Str1[112 ]; char v18[112 ]; printf ("Enter the flag: " ); scanf ("%36s" , Str); if ( strlen (Str) == 36 ) { strncpy (Part1, Str, 0xCu i64); v8 = 12 i64; Part1[12 ] = 0 ; strncpy (Part2, Source, 0xCu i64); v9 = 12 i64; Part2[12 ] = 0 ; strncpy (Part3, v15, 0xCu i64); v10 = 12 i64; v18[12 ] = 0 ; v11[0 ] = 1 ; v11[1 ] = 5 ; v11[2 ] = 6 ; v11[3 ] = 3 ; v11[4 ] = 4 ; v11[5 ] = 1 ; v11[6 ] = 4 ; v11[7 ] = 5 ; v12[0 ] = 0 ; v12[1 ] = 1 ; v12[2 ] = 2 ; v7[0 ] = 0 ; v7[1 ] = 0 ; v7[2 ] = 0 ; for ( i = 0 ; i < 8 ; ++i ) { for ( j = 0 ; j < 3 ; ++j ) { if ( i >= v12[j] ) { v6 = v7[j]; if ( v6 < 8 ) { if ( j ) { if ( j == 1 ) { sub_1400015D0(Str1, v18, v11[v6]); } else if ( j == 2 ) { sub_1400015D0(v18, Destination, v11[v6]); } } else { sub_1400015D0(Destination, Str1, v11[v6]); } ++v7[j]; } } } } if ( !strcmp (Destination, "WgvDmssEvcY326bHo3nNro3vXvvfmgrz" ) && !strcmp (Str1, "gX+Ri9PG=bt5=00B6hscPQOL" ) && !strcmp (v18, "T6bHgUPL2gXUd=xT=FNHtPzV" ) ) { printf ("Congratulations! You have found the flag!\n" ); } else { printf ("Invalid flag!\n" ); } return 0 ; } else { printf ("Invalid flag!\n" ); return 0 ; } }
发现这里把输入拆成了三部分,且有一个嵌套循环,把循环改成print编写C++脚本跑一遍得到加密顺序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Part1, Part2, 1 Part1, Part2, 5 Part2, Part3, 1 Part1, Part2, 6 Part2, Part3, 5 Part3, Part1, 1 Part1, Part2, 3 Part2, Part3, 6 Part3, Part1, 5 Part1, Part2, 4 Part2, Part3, 3 Part3, Part1, 6 Part1, Part2, 1 Part2, Part3, 4 Part3, Part1, 3 Part1, Part2, 4 Part2, Part3, 1 Part3, Part1, 4 Part1, Part2, 5 Part2, Part3, 4 Part3, Part1, 1
查看这个加密函数:
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 void __fastcall sub_1400015D0 (char *a1, const char *a2, int a3) { v9 = strlen (a1); v21 = strlen (a2); switch ( a3 ) { case 1 : for ( i = 0 ; i < v9; ++i ) { v22 = a2[i % v21]; if ( a1[i] < 65 || a1[i] > 90 ) { if ( a1[i] < 97 || a1[i] > 122 ) { if ( a1[i] >= 48 && a1[i] <= 57 ) a1[i] = (a1[i] + v22 - 48 ) % 10 + 48 ; } else { a1[i] = (a1[i] + v22 - 97 ) % 26 + 97 ; } } else { a1[i] = (a1[i] + v22 - 65 ) % 26 + 65 ; } } break ; case 2 : v32[0 ] = 1 ; v32[1 ] = 3 ; v32[2 ] = 5 ; v32[3 ] = 7 ; v32[4 ] = 9 ; v32[5 ] = 11 ; v32[6 ] = 15 ; v32[7 ] = 17 ; v32[8 ] = 19 ; v32[9 ] = 21 ; v32[10 ] = 23 ; v32[11 ] = 25 ; for ( j = 0 ; j < v9; ++j ) { if ( a1[j] < 65 || a1[j] > 90 ) { if ( a1[j] >= 97 && a1[j] <= 122 ) a1[j] = (a2[j % v21] + v32[j % 12 ] * (a1[j] - 97 )) % 26 + 97 ; } else { a1[j] = (a2[j % v21] + v32[j % 12 ] * (a1[j] - 65 )) % 26 + 65 ; } } break ; case 3 : v10 = *a2 % 10 + 2 ; v33 = v10; Block = malloc (saturated_mul(v10, 8u i64)); for ( k = 0 ; k < v10; ++k ) { Block[k] = malloc (v9 + 1 ); memset ((void *)Block[k], 0 , v9 + 1 ); } for ( m = 0 ; v10 * m < v9; ++m ) { for ( n = 0 ; n < v10 && n + v10 * m < v9; ++n ) *(_BYTE *)(Block[n] + m) = a1[n + v10 * m]; } v18 = 0 ; for ( ii = 0 ; ii < v10; ++ii ) { for ( jj = 0 ; jj < m; ++jj ) { if ( *(_BYTE *)(Block[ii] + jj) && v18 < v9 ) a1[v18++] = *(_BYTE *)(Block[ii] + jj); } } a1[v18] = 0 ; for ( kk = 0 ; kk < v10; ++kk ) free ((void *)Block[kk]); free (Block); break ; case 4 : v25 = *a2 % 10 + 2 ; for ( mm = 0 ; mm < v25; ++mm ) { v11 = a1[v9 - 1 ]; for ( nn = v9 - 1 ; nn > 0 ; --nn ) a1[nn] = a1[nn - 1 ]; *a1 = v11; } break ; case 5 : v34 = v9; v26 = malloc (saturated_mul(v9, 4u i64)); for ( i1 = 0 ; i1 < v9; ++i1 ) v26[i1] = (a2[i1 % v21] + 57 ) ^ a1[i1]; Source = (char *)sub_140001470(v26, (unsigned int )v9); strcpy (a1, Source); free (v26); free (Source); break ; case 6 : v3 = saturated_mul(strlen (a1), 4u i64); v27 = malloc (v3); RC4((__int64)a1, a2, (__int64)v27); v4 = strlen (a1); v30 = (char *)sub_140001470(v27, v4); strcpy (a1, v30); free (v27); free (v30); break ; case 7 : v5 = saturated_mul(strlen (a1), 4u i64); v28 = malloc (v5); RC4_xor39(a1, a2, v28); v6 = strlen (a1); v31 = (char *)sub_140001470(v28, v6); strcpy (a1, v31); free (v28); free (v31); break ; } }
发现里面一共有7种加密逻辑,但是不是每种都用上了,编写同构加密脚本,运行发现结果相同,进行解密:
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 import base64def case1_enc (a1: str , a2: str ) -> str : encrypted = list (a1) len_a1 = len (a1) len_a2 = len (a2) for i in range (len_a1): key_char = ord (a2[i % len_a2]) current_char = ord (encrypted[i]) if 65 <= current_char <= 90 : encrypted[i] = chr ( (current_char + key_char - 65 ) % 26 + 65 ) elif 97 <= current_char <= 122 : encrypted[i] = chr ( (current_char + key_char - 97 ) % 26 + 97 ) elif 48 <= current_char <= 57 : encrypted[i] = chr ( (current_char + key_char - 48 ) % 10 + 48 ) return '' .join(encrypted) def case1_dec (encrypted_str: str , a2: str ) -> str : decrypted = list (encrypted_str) len_str = len (encrypted_str) len_a2 = len (a2) for i in range (len_str): key_char = ord (a2[i % len_a2]) current_char = ord (decrypted[i]) if 65 <= current_char <= 90 : decrypted[i] = chr ( (current_char - key_char - 65 ) % 26 + 65 ) elif 97 <= current_char <= 122 : decrypted[i] = chr ( (current_char - key_char - 97 ) % 26 + 97 ) elif 48 <= current_char <= 57 : decrypted[i] = chr ( (current_char - key_char - 48 ) % 10 + 48 ) return '' .join(decrypted) def case3_enc (plaintext: str , key: str ) -> str : cols = (ord (key[0 ]) % 10 ) + 2 length = len (plaintext) columns = [[] for _ in range (cols)] for m in range ((length + cols - 1 ) // cols): for n in range (cols): idx = n + cols * m if idx < length: columns[n].append(plaintext[idx]) return '' .join('' .join(col) for col in columns) def case3_dec (ciphertext: str , key: str ) -> str : cols = (ord (key[0 ]) % 10 ) + 2 length = len (ciphertext) rows_per_col = [(length - n + cols - 1 ) // cols for n in range (cols)] columns = [] start = 0 for n in range (cols): end = start + rows_per_col[n] columns.append(list (ciphertext[start:end])) start = end original = [] for i in range (length): col_idx = i % cols row_idx = i // cols original.append(columns[col_idx][row_idx]) return '' .join(original) def case4_enc (input_str, a2 ): v25 = (ord (a2[0 ]) % 10 ) + 2 v9 = len (input_str) input_list = list (input_str) for _ in range (v25): last_char = input_list[v9 - 1 ] for i in range (v9 - 1 , 0 , -1 ): input_list[i] = input_list[i - 1 ] input_list[0 ] = last_char return '' .join(input_list) def case4_dec (input_str, a2 ): v25 = (ord (a2[0 ]) % 10 ) + 2 v9 = len (input_str) input_list = list (input_str) for _ in range (v25): last_char = input_list[0 ] for i in range (v9 - 1 ): input_list[i] = input_list[i + 1 ] input_list[v9 - 1 ] = last_char return '' .join(input_list) CUSTOM_BASE64_TABLE = "stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr" STANDARD_BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" TRANSLATION_ENCODE = str .maketrans(STANDARD_BASE64_TABLE, CUSTOM_BASE64_TABLE) TRANSLATION_DECODE = str .maketrans(CUSTOM_BASE64_TABLE, STANDARD_BASE64_TABLE) def custom_base64_encode (data ): encoded = base64.b64encode(data).decode('utf-8' ) return encoded.translate(TRANSLATION_ENCODE) def custom_base64_decode (encoded_str ): standard_encoded = encoded_str.translate(TRANSLATION_DECODE) return base64.b64decode(standard_encoded) def case5_enc (input_str, key_str ): v9 = len (input_str) v21 = len (key_str) xor_result = bytearray () for i in range (v9): xor_char = ((ord (key_str[i % v21]) + 57 ) ^ ord (input_str[i])) xor_result.append(xor_char) encoded_result = custom_base64_encode(bytes (xor_result)) return encoded_result def case5_dec (encoded_str, key_str ): decoded_bytes = custom_base64_decode(encoded_str) v9 = len (decoded_bytes) v21 = len (key_str) original_str = '' for i in range (v9): original_char = chr (((ord (key_str[i % v21]) + 57 ) ^ decoded_bytes[i])) original_str += original_char return original_str def rc4_modified (data, key, output_buffer, decrypt=False ): S = list (range (256 )) j = 0 for i in range (256 ): j = (j + S[i] + ord (key[i % len (key)])) % 256 S[i], S[j] = S[j], S[i] i = j = 0 for idx, char in enumerate (data): i = (i + 1 ) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] k = S[(S[i] + S[j]) % 256 ] if decrypt: output_buffer[idx] = ((char - 57 ) % 256 ) ^ k else : output_buffer[idx] = ((k ^ char) + 57 ) % 256 def case6_enc (input_str, key_str ): input_bytes = input_str.encode() output_buffer = bytearray (len (input_bytes)) rc4_modified(input_bytes, key_str, output_buffer) encoded_result = custom_base64_encode(output_buffer) return encoded_result def case6_dec (encoded_str, key_str ): decoded_bytes = custom_base64_decode(encoded_str) output_buffer = bytearray (len (decoded_bytes)) rc4_modified(decoded_bytes, key_str, output_buffer, decrypt=True ) return output_buffer.decode() message = "abcdefghijklmnopqrstuvwxyz1234567890" part1 = message[:12 ] part2 = message[12 :24 ] part3 = message[24 :] print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = case1_enc(part1, part2) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = case5_enc(part1, part2) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part2 = case1_enc(part2, part3) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = case6_enc(part1, part2) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part2 = case5_enc(part2, part3) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part3 = case1_enc(part3, part1) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = case3_enc(part1, part2) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part2 = case6_enc(part2, part3) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part3 = case5_enc(part3, part1) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = case4_enc(part1, part2) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part2 = case3_enc(part2, part3) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part3 = case6_enc(part3, part1) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = case1_enc(part1, part2) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part2 = case4_enc(part2, part3) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part3 = case3_enc(part3, part1) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = case4_enc(part1, part2) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part2 = case1_enc(part2, part3) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part3 = case4_enc(part3, part1) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = case5_enc(part1, part2) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part2 = case4_enc(part2, part3) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part3 = case1_enc(part3, part1) print (f"part1: {part1} , part2: {part2} , part3: {part3} " )part1 = "WgvDmssEvcY326bHo3nNro3vXvvfmgrz" part2 = "gX+Ri9PG=bt5=00B6hscPQOL" part3 = "T6bHgUPL2gXUd=xT=FNHtPzV" part3 = case1_dec(part3, part1) part2 = case4_dec(part2, part3) part1 = case5_dec(part1, part2) part3 = case4_dec(part3, part1) part2 = case1_dec(part2, part3) part1 = case4_dec(part1, part2) part3 = case3_dec(part3, part1) part2 = case4_dec(part2, part3) part1 = case1_dec(part1, part2) part3 = case6_dec(part3, part1) part2 = case3_dec(part2, part3) part1 = case4_dec(part1, part2) part3 = case5_dec(part3, part1) part2 = case6_dec(part2, part3) part1 = case3_dec(part1, part2) part3 = case1_dec(part3, part1) part2 = case5_dec(part2, part3) part1 = case6_dec(part1, part2) part2 = case1_dec(part2, part3) part1 = case5_dec(part1, part2) part1 = case1_dec(part1, part2) print (f"{part1} {part2} {part3} " )
运行脚本得到flag:NSSCTF{P4ch3Lbel’s_C@n0n_1n_D_mAjOr} .
Crypto方向: MIMT_RSA: 编写脚本爆破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 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 #include <gmp.h> #include <iostream> #include <thread> #include <atomic> #include <vector> #include <chrono> #include <mutex> #include <iomanip> #include <cmath> #pragma warning (push) #pragma warning (disable: 4146 4244) #include <gmpxx.h> #pragma warning (pop) std::atomic<bool > found (false ) ;std::atomic<uint64_t > total_processed (0 ) ;std::mutex cout_mutex; const uint64_t START_KEY = 0b100000000000000000000000000000000000 ;const uint64_t END_KEY = 0xFFFFFFFFF ;const uint64_t TOTAL_KEYS = END_KEY - START_KEY + 1 ;mpz_class n ("26563847822899403123579768059987758748518109506340688366937229057385768563897579939399589878779201509595131302887212371556759550226965583832707699167542469352676806103999861576255689028708092007726895892953065618536676788020023461249303717579266840903337614272894749021562443472322941868357046500507962652585875038973455411548683247853955371839865042918531636085668780924020410159272977805762814306445393524647460775620243065858710021030314398928537847762167177417552351157872682037902372485985979513934517709478252552309280270916202653365726591219198063597536812483568301622917160509027075508471349507817295226801011" ) ;mpz_class target ("8371316287078036479056771367631991220353236851470185127168826270131149168993253524332451231708758763231051593801540258044681874144589595532078353953294719353350061853623495168005196486200144643168051115479293775329183635187974365652867387949378467702492757863040766745765841802577850659614528558282832995416523310220159445712674390202765601817050315773584214422244200409445854102170875265289152628311393710624256106528871400593480435083264403949059237446948467480548680533474642869718029551240453665446328781616706968352290100705279838871524562305806920722372815812982124238074246044446213460443693473663239594932076" ) ;const uint32_t e = 0x10001 ;class ProgressTracker {private : std::chrono::steady_clock::time_point start_time; uint64_t last_count = 0 ; double avg_speed = 0.0 ; const double alpha = 0.2 ; public : ProgressTracker () : start_time (std::chrono::steady_clock::now ()) {} void update (uint64_t current) { auto now = std::chrono::steady_clock::now (); double elapsed = std::chrono::duration <double >(now - start_time).count (); if (elapsed > 0.1 ) { uint64_t delta = current - last_count; double instant_speed = delta / elapsed; avg_speed = alpha * instant_speed + (1 - alpha) * avg_speed; last_count = current; start_time = now; } } std::pair<double , std::string> get_progress (uint64_t done) { double progress = std::min (100.0 , done * 100.0 / TOTAL_KEYS); double remaining = (avg_speed > 0 ) ? (TOTAL_KEYS - done) / avg_speed : 0 ; return {progress, format_duration(remaining)}; } private : std::string format_duration (double seconds) { if (seconds < 60 ) return fmt_duration (seconds, "s" ); if (seconds < 3600 ) return fmt_duration (seconds/60 , "m" ) + " " + fmt_duration (fmod (seconds,60 ), "s" ); return fmt_duration (seconds/3600 , "h" ) + " " + fmt_duration (fmod (seconds,3600 )/60 , "m" ); } std::string fmt_duration (double value, const char * unit) { std::stringstream ss; ss << std::fixed << std::setprecision (1 ) << value << unit; return ss.str (); } }; void check_range (uint64_t start, uint64_t end) { mpz_class result, key; const uint64_t batch_size = 0x1000 ; for (uint64_t k = start; k <= end && !found; ) { uint64_t batch_end = std::min (k + batch_size, end); for (; k <= batch_end && !found; ++k) { key = k; mpz_powm_ui (result.get_mpz_t (), key.get_mpz_t (), e, n.get_mpz_t ()); if (result == target) { found = true ; std::lock_guard<std::mutex> lock (cout_mutex) ; std::cout << "\n\nFound key: " << k << std::endl; break ; } } total_processed.fetch_add (k - start, std::memory_order_relaxed); start = k; } } void progress_monitor () { ProgressTracker tracker; const int bar_width = 40 ; while (!found && total_processed < TOTAL_KEYS) { uint64_t done = total_processed.load (); auto [progress, eta] = tracker.get_progress (done); std::lock_guard<std::mutex> lock (cout_mutex) ; std::cout << "\r\033[K" ; int pos = bar_width * progress / 100.0 ; std::cout << "[" ; for (int i = 0 ; i < bar_width; ++i) { if (i < pos) std::cout << "=" ; else if (i == pos) std::cout << ">" ; else std::cout << " " ; } std::cout << "] " ; std::cout << std::fixed << std::setprecision (2 ) << std::setw (6 ) << progress << "% " << "ETA:" << std::setw (8 ) << eta << " Keys:" << std::setw (10 ) << done << "/" << TOTAL_KEYS << std::flush; std::this_thread::sleep_for (std::chrono::milliseconds (200 )); } std::cout << "\r\033[K" << std::flush; } int main () { const unsigned num_threads = std::thread::hardware_concurrency (); std::vector<std::thread> threads; uint64_t range = (END_KEY - START_KEY) / num_threads; for (unsigned i = 0 ; i < num_threads; ++i) { uint64_t start = START_KEY + i * range; uint64_t end = (i == num_threads - 1 ) ? END_KEY : start + range - 1 ; threads.emplace_back (check_range, start, end); } std::thread monitor (progress_monitor) ; for (auto & t : threads) { t.join (); } monitor.join (); if (!found) { std::cout << "\nKey not found in the specified range!" << std::endl; } return 0 ; }
得到key = 62495925932,flag为NSSCTF{14369380f677abec84ed8b6d0e3a0ba9} .
Misc方向: mybrave: 发现压缩包没密码,但里面只有一个png文件,由于png文件头是89504e470d0a1a0a0000000d494844520000
,并且加密算法是zipcrypto store
,则可以利用明文攻击,用bkcrack破解得到密钥97d30dcc 173b15a8 6e0e7455
,解密得到一张png,发现文件尾有东西:
1 2 3 4 5 6 7 8 9 10 54 00 6C 00 4E 00 54 00 51 00 31 00 52 00 47 00 65 00 30 00 6B 00 6E 00 62 00 56 00 39 00 58 00 61 00 44 00 46 00 7A 00 63 00 44 00 4E 00 79 00 61 00 55 00 35 00 6E 00 58 00 30 00 39 00 31 00 55 00 6C 00 39 00 4D 00 64 00 54 00 45 00 78 00 59 00 57 00 4A 00 5A 00 58 00 32 00 59 00 77 00 63 00 6C 00 39 00 5A 00 4D 00 48 00 56 00 66 00 64 00 47 00 39 00 66 00 51 00 32 00 39 00 4E 00 5A 00 56 00 39 00 43 00 4E 00 47 00 4E 00 72 00 58 00 30 00 68 00 76 00 62 00 57 00 56 00 39 00
去除其中的00,base64解密得到flag:NSSCTF{I’m_Wh1sp3riNg_OuR_Lu11abY_f0r_Y0u_to_CoMe_B4ck_Home} .
mywav: 发现该音频全是正弦波,0.1毫秒为一个小节,共有2种音高,一个300多Hz,一个700多Hz,当做二进制的0和1提取音频内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import numpy as npfrom scipy.io import wavfilesamplerate, data = wavfile.read('E:/CTF/attachment.wav' ) samples_per_bit = int (samplerate * 0.010 ) fft_size = samples_per_bit bit_sequence = [] freq_resolution = samplerate / fft_size threshold_index = int (450 / freq_resolution) for i in range (0 , len (data), samples_per_bit): segment = data[i:i+samples_per_bit] windowed_segment = segment * np.hanning(len (segment)) freqs = np.fft.rfft(windowed_segment) power_spectrum = np.abs (freqs)**2 dominant_freq_index = np.argmax(power_spectrum[1 :]) + 1 if dominant_freq_index > threshold_index: bit_sequence.append(1 ) else : bit_sequence.append(0 ) for i in range (len (bit_sequence)): print (bit_sequence[i],end="" ) print ()
输出的内容转换为文本为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 YbAgLvWkQbFp qk k 2021 Gfgbyue gxriobzqgx hpyau utnkxmgz Jpwx Dfcmocn ngj Bn Flvytmc. Gh cu bnlkh hg apw Mlglsmg nbokp Lxjzwdw gl hbg Lnmzmvx. Apw cipgsm ciexj sg Fhvyy XT dfio Ahzawm 10 nubav Eseimv 26, 2021. Tux yikbla zkw qsfjcsfxj e vntcdkxgts pkejxxwabw wx 1.3 lmjjwip apkuwl ohzayyq nzuvfbksw, bgjtmnmle Aupgb MB'w whtmkdma ydjniptzmhg, FwmDyzc, ohf Tutoptgk BjeiGB. Gspoclow Mal awbmcq hynlf mni lmvzq yj y qijgrvhx egw bvjepw aogruf fgpx zvl Rrss Qwswe (Pakr Salgmkr) ylr u jenwyxkhuo, kdyzzclp, aaw rsoxsg Usrbcfynln zove, Wpvy Hmyl (Lo Oeazpmx). Yywe liglu gwthtrpr xekdewgts ncbyxsemxz il cgfmcf vo oxisfbuo wkgf mhbgr'f eojxevvy msknohkoal or malqj tsspbya tukuyza fwmdl. Yjhbquta zlxr jwmvhl'r gncnq xgga hapwb er dwlut, gakc vhtm ly ennfyeinmk itvo wlrip'q gnteazzll bu bzomp bos-vo-qte mgmlzsmxgmbm. Oavg Ilxg Gpwiyyl om Bhbn Ymrnl I qyyle, vupdfhsi lvowdkv ufc yzuqxy eg tpz gp ejmczpefl grw bubwvpgeshee. Qxytbml pac gmjr upd qbyxtga mpdipgcl, je chywxlzmk k kclhfg aaw jimxyuaxib lonwrr mnem wyqnow fga nq phkyyx apa axrcpaiut qxymkxz. Qf dlc dowg os ygqbef kzkpjcbags, ux xiftpvk biqmzove vg nml ibzkemr mt bks qkkefl. Ec Eorehwy cs Qbtk Qbhv Sx yluopgrvgm egw kmlovkgbyf ybntk phtif glm lspgr gnxrl tdiq pvmk qbclyxtkxl bvlsp qfs zccrl gr bgzcjwsslhudlr hhwmtjtw. Rip jwzg uawkvpxub s nvykonkc gkgrlyvzekxgmb qjea lni kxswukxcb tlqm n lseee awox xm Qvyphnb Immr. Pvxvyclqyf bl akv rhbbzpyj gajwlfbbigxza kri qfcqeafx nik ypmjmi bchytmvggxbhu ifn yluopgrvgm tnkzcad sd fsl ioney. Whflbzsre Nomuwbkj WhecmA7DsE3rTf4i
发现是维尼吉亚密码,解密后得到keyword youcantgetthiskey
和明文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 AnGeLiDeMiMi is a 2021 Chinese television drama starring Chen Zheyuan and Xu Mengjie. It is based on the Chinese novel Secrets in the Lattice. The series aired on Mango TV from August 10 until August 26, 2021. The series has surpassed a cumulative viewership of 1.3 billion across various platforms, including Mango TV's domestic application, YouTube, and Thailand TrueID. Synopsis The series tells the story of a superior and unruly campus male god Zhou Siyue (Chen Zheyuan) and a headstrong, stubborn, and lovely Cinderella girl, Ding Xian (Xu Mengjie). From being mutually exclusive tablemates at school to becoming each other's lifelong companions in their journey through youth. Although they couldn't stand each other at first, they come to appreciate each other's strengths in their day-to-day interactions. Main Chen Zheyuan as Zhou Siyue A young, handsome scholar who exudes an air of aloofness and intelligence. Despite his cold and distant exterior, he possesses a gentle and determined nature that drives him to pursue his innermost desires. In the face of family challenges, he remains resolute in his pursuit of his dreams. Xu Mengjie as Ding Xian An unwavering and determined young woman who never turns away from challenges until she faces an insurmountable obstacle. Her life underwent a dramatic transformation when she relocated from a small town to Shenhai City. Influenced by her youthful impulsiveness she showcase her fierce determination and unwavering pursuit of her goals. Something Password SolveI7ToG3tFl4g
再次观察wav文件,在010editor运行wav模板,发现尾部有一个252字节的数据块无法识别出wav特征,并且去除后不影响总时长,提取这252字节:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 9E 97 BA 2A 00 80 88 C9 A3 70 97 5B A2 E4 99 B8 C1 78 72 0F 88 DD DC 34 2B 4E 7D 31 7F B5 E8 70 39 A8 B8 42 75 68 71 91 D4 EE E8 AC A8 99 2D EF 49 AC B9 DD 32 7A 2E 23 2D FE 00 96 BD B0 76 7D BA D5 7B 14 75 C4 94 8C 0A B4 AB 3C 51 28 F9 52 58 C8 ED 7C C1 94 6F D8 A3 BA 08 6D 81 15 B2 21 DC 47 E7 DE D6 42 CE BF 82 82 18 A4 2A 2C F0 AD 62 DD 99 B5 3D 15 59 88 A8 4A EB 91 67 24 DB A1 0A 66 55 E1 B2 2E 05 C9 3B B8 55 41 BB E5 B0 40 75 C3 58 C8 58 BE E7 42 2F 06 D1 5C 2A 9E BD 50 2E 95 AF CC 40 7B AB FA F2 6F D7 67 8A 66 62 98 F3 9E 21 C2 52 2E A4 E7 30 66 BE E3 EC 83 13 4F F7 CA 41 FA 97 79 78 26 86 AB 29 00 D9 7B 74 00 CE F8 22 0A 17 D4 0C EE 74 3E C5 3D CF F0 9F 01 48 49 00 00 E0 00 00 00 3E 6A 3A 3D 6A 3B 38 3E 69 30 39 6C 69 3D 3A 3F 00 7E 74 05
发现是OurSecret的文件头,oursecret解密提取得到flag.txt,获得flag:NSSCTF{9c99897d-5ea3-481c-b0a5-029fec9eaf42} .