# NSSCTF Round#18 Basic Writeup
# 前言
唉,好菜,赛时只做出来一题,看 ezcrypto 看了一下午结束后才做出来,还是太菜了😭
# Rev
# ezcrypt
解包,pycdc 反编译 NSSCTF.pyc
,可以看出来是 Blowfish 加密,缺个 key,在 crypto.pyc
发现了加密逻辑,一个魔改的 tea,还有字符串每 4 位一组逆序
#include <stdio.h> | |
#include <stdint.h> | |
void decrypt(uint32_t *v, uint32_t *k) | |
{ | |
uint32_t v0 = v[0], v1 = v[1], i; | |
uint32_t delta = 0x9e3779b9; | |
uint32_t sum = delta * 32; | |
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; | |
for (i = 0; i < 32; i++) | |
{ | |
v1 -= ((v0 << 3) + k2) ^ (v0 + sum) ^ ((v0 >> 4) + k3) ^ 2310; | |
v0 -= ((v1 << 3) + k0) ^ (v1 + sum) ^ ((v1 >> 4) + k1) ^ 596; | |
sum -= delta; | |
} | |
v[0] = v0; v[1] = v1; | |
} | |
void reverseGroups(char *str, int groupSize) { | |
int len = strlen(str); | |
for (int i = 0; i < len; i += groupSize) { | |
int start = i; | |
int end = i + groupSize - 1; | |
while (start < end) { | |
char temp = str[start]; | |
str[start] = str[end]; | |
str[end] = temp; | |
start++; | |
end--; | |
} | |
} | |
} | |
int main() | |
{ | |
uint32_t data[] = {1396533857, 0xCC8AE275L, 0x89FB8A63L, 940694833}; | |
uint32_t key[] = {17, 34, 51, 68}; | |
decrypt(data, key); | |
decrypt(data + 2, key); | |
reverseGroups(data, 4); | |
printf("%s", data); | |
} |
解密得到 key: EzNssRevProjects
,转为十六进制字符串,然后去解 blowfish
from Crypto.Cipher import Blowfish | |
def decrypt_file(input_path, output_path, key): | |
cipher = Blowfish.new(key, Blowfish.MODE_CBC) | |
with open(input_path, 'rb') as file: | |
encrypted_data = file.read() | |
plaintext = cipher.decrypt(encrypted_data) | |
print(cipher.iv.hex()) | |
with open(output_path, 'wb') as file: | |
file.write(plaintext) | |
decrypt_file(r"\bin\NSSCTF.exe_extracted\output", r"bin\output_dec", bytearray.fromhex("457a4e737352657650726f6a65637473")) |
解密后的文件去掉头部 8 个字节,可以发现是一个 elf 文件,ida 打开发现一个 vm
int __fastcall main(int argc, const char **argv, const char **envp) | |
{ | |
int a2[8]; // [rsp+10h] [rbp-50h] BYREF | |
unsigned __int64 a1[3]; // [rsp+30h] [rbp-30h] BYREF | |
int v6; // [rsp+48h] [rbp-18h] | |
__int16 v7; // [rsp+4Ch] [rbp-14h] | |
unsigned __int64 v8; // [rsp+58h] [rbp-8h] | |
v8 = __readfsqword(0x28u); | |
a2[0] = 0x23; | |
a2[1] = 0x12; | |
a2[2] = 0x45; | |
a2[3] = 0x56; | |
a2[4] = 0x67; | |
a1[0] = 0xB537982DAB0F8D37LL; | |
a1[1] = 0xB81BED0CDA30A648LL; | |
a1[2] = 0xB6ED81179824E900LL; | |
v6 = 0xDE218C26; | |
v7 = 0xDE04; | |
vm((unsigned __int8 *)a1, (BYTE *)a2, 30); | |
return 0; | |
} | |
void __fastcall vm(unsigned __int8 *a1, BYTE *a2, int a3) | |
{ | |
int i; // [rsp+1Ch] [rbp-8h] | |
int v4; // [rsp+20h] [rbp-4h] | |
for ( i = 0; i < a3; ++i ) | |
{ | |
v4 = i % 4; | |
if ( i % 4 == 3 ) | |
{ | |
a1[i] += a2[8] + *a2; | |
} | |
else if ( v4 <= 3 ) | |
{ | |
if ( v4 == 2 ) | |
{ | |
a1[i] -= a2[4] ^ a2[12]; | |
} | |
else if ( v4 <= 2 ) | |
{ | |
if ( v4 ) | |
{ | |
if ( v4 == 1 ) | |
a1[i] ^= *a2 - (unsigned __int8)*((_DWORD *)a2 + 2); | |
} | |
else | |
{ | |
a1[i] ^= a2[16] - (unsigned __int8)*((_DWORD *)a2 + 1); | |
} | |
} | |
} | |
} | |
} |
对着逻辑解就好了
data = bytearray.fromhex( | |
"378D0FAB2D9837B548A630DA0CED1BB800E924981781EDB6268C21DE04DE") | |
key = [0x23, 0x12, 0x45, 0x56, 0x67] | |
for i in range(30): | |
n = i % 4 | |
if (n == 0): | |
data[i] = (data[i] ^ (key[4] + key[1])) & 0xff | |
if (n == 1): | |
data[i] = (data[i] ^ (key[0] - key[2])) & 0xff | |
if (n == 2): | |
data[i] = (data[i] + (key[3] ^ key[1])) & 0xff | |
if (n == 3): | |
data[i] = (data[i] - (key[2] + key[0])) & 0xff | |
for i in data: | |
print(chr(i), end='') |
NSSCTF{M1xtru3_Py7h0n_1N_Rev}
# EzHook
分析程序流程,在 main 函数执行之前 Hook 了 MessageBoxA
__int64 InstallHook() | |
{ | |
__int64 result; // rax | |
result = HOOK(MyHook); | |
Hook_MessageBoxA = result; | |
return result; | |
} |
然后走到 main 函数里输入,然后调用了一个全是异或的函数,然后外面又异或了一下,最后 check 那管你对不对都会进入 MyHook
中
int __fastcall main(int argc, const char **argv, const char **envp) | |
{ | |
char fake[40]; // [rsp+20h] [rbp+0h] BYREF | |
char xor_key[8]; // [rsp+48h] [rbp+28h] | |
char input[40]; // [rsp+50h] [rbp+30h] BYREF | |
int v7; // [rsp+78h] [rbp+58h] | |
int i; // [rsp+7Ch] [rbp+5Ch] | |
__int64 v9; // [rsp+C0h] [rbp+A0h] | |
fake[0] = 4; | |
fake[1] = 90; | |
fake[2] = 6; | |
fake[3] = 13; | |
fake[4] = 17; | |
fake[5] = -73; | |
fake[6] = 15; | |
fake[7] = 91; | |
fake[8] = 10; | |
fake[9] = 68; | |
fake[10] = 68; | |
fake[11] = -36; | |
fake[12] = 49; | |
fake[13] = 6; | |
fake[14] = 21; | |
fake[15] = 24; | |
fake[16] = 19; | |
fake[17] = -83; | |
fake[18] = 43; | |
fake[19] = 66; | |
fake[20] = 56; | |
fake[21] = 91; | |
fake[22] = 25; | |
fake[23] = -83; | |
fake[24] = 44; | |
fake[25] = 89; | |
fake[26] = 19; | |
fake[27] = 53; | |
fake[28] = 56; | |
fake[29] = -101; | |
fake[30] = 5; | |
fake[31] = 94; | |
fake[32] = 19; | |
fake[33] = 53; | |
fake[34] = 12; | |
fake[35] = -98; | |
fake[36] = 35; | |
fake[37] = 81; | |
fake[38] = 70; | |
fake[39] = 23; | |
xor_key[0] = 0x11; | |
xor_key[1] = 0x45; | |
xor_key[2] = 0x14; | |
xor_key[3] = 0x19; | |
xor_key[4] = 0x19; | |
xor_key[5] = 0x81; | |
memset(input, 0, sizeof(input)); | |
v7 = 0; | |
printf("Plz input your flag:\n", argv, envp); | |
scanf("%s", input); | |
xorrrrrr(input); | |
for ( i = 0; i < 40; ++i ) | |
{ | |
v9 = i; | |
input[i] ^= xor_key[i % 6]; | |
} | |
if ( !memcmp(input, fake, v7) ) | |
MessageBoxA(0i64, input, "Right?", 0); | |
else | |
MessageBoxA(0i64, input, "Wrong?", 0); | |
return 0; | |
} |
这里又是一个异或,最后一个 xxtea 加密
__int64 __fastcall MyHook(__int64 handle, char *input, __int64 title, unsigned int a4) | |
{ | |
char v6[5]; // [rsp+28h] [rbp+8h] BYREF | |
char v7[8]; // [rsp+2Dh] [rbp+Dh] BYREF | |
char key[16]; // [rsp+38h] [rbp+18h] BYREF | |
char enc[40]; // [rsp+48h] [rbp+28h] BYREF | |
char xor_key[8]; // [rsp+70h] [rbp+50h] | |
int i; // [rsp+78h] [rbp+58h] | |
int j; // [rsp+7Ch] [rbp+5Ch] | |
__int64 v13; // [rsp+C0h] [rbp+A0h] | |
v6[0] = 52; | |
v6[1] = 15; | |
v6[2] = 1; | |
v6[3] = 14; | |
v6[4] = 18; | |
strcpy(v7, "GGG"); | |
qmemcpy(key, "He1l0NsS!", 9); | |
key[9] = 17; | |
key[10] = 69; | |
key[11] = 20; | |
key[12] = 25; | |
key[13] = 25; | |
key[14] = -127; | |
key[15] = 0; | |
enc[0] = -28; | |
enc[1] = -25; | |
enc[2] = -2; | |
enc[3] = -29; | |
enc[4] = 23; | |
enc[5] = 28; | |
enc[6] = -34; | |
enc[7] = 50; | |
enc[8] = -26; | |
enc[9] = -72; | |
enc[10] = 104; | |
enc[11] = 64; | |
enc[12] = 64; | |
enc[13] = -40; | |
enc[14] = 114; | |
enc[15] = -6; | |
enc[16] = -120; | |
enc[17] = 20; | |
enc[18] = -31; | |
enc[19] = -123; | |
enc[20] = -51; | |
enc[21] = -127; | |
enc[22] = -86; | |
enc[23] = -34; | |
enc[24] = 29; | |
enc[25] = -24; | |
enc[26] = -110; | |
enc[27] = 65; | |
enc[28] = -72; | |
enc[29] = 30; | |
enc[30] = 94; | |
enc[31] = -49; | |
enc[32] = -50; | |
enc[33] = 73; | |
enc[34] = 39; | |
enc[35] = 34; | |
enc[36] = 57; | |
enc[37] = 125; | |
enc[38] = 80; | |
enc[39] = -38; | |
xor_key[0] = 17; | |
xor_key[1] = 69; | |
xor_key[2] = 20; | |
xor_key[3] = 25; | |
xor_key[4] = 25; | |
xor_key[5] = 0x81; | |
for ( i = 0; i < 40; ++i ) | |
{ | |
v13 = i; | |
input[i] ^= xor_key[i % 6]; | |
input[i] ^= 0x73u; | |
} | |
xxtea_encrypt(input, 40, key); | |
if ( !strncmp(input, enc, 0x28ui64) ) | |
{ | |
for ( j = 0; j < 8; ++j ) | |
v6[j] ^= 0x66u; | |
xxtea_decrypt(input, 40, key); | |
Hook_MessageBoxA(handle, input, v6, a4); // Right!!! | |
} | |
else | |
{ | |
xxtea_decrypt(input, 40, key); | |
Hook_MessageBoxA(handle, input, title, a4); | |
} | |
return 0i64; | |
} |
总结一下流程就是 input->一坨异或->异或->再异或->xxtea加密
,但是通过调试可以发现,前面那一堆异或最后又还原了,所以最后就只剩下一个 xxtea
然而善良的出题人直接把 xxtea_decrypt
给我们了,直接改 rip 飞过去,修改 input
为 enc
,然后运行就得到 flag 了哈哈
NSSCTF{C0ngr@tulat1ons!H0Ok_bY_1t_s3lf!}