# Interactive Đề bài gồm 1 file release.exe ![image](https://hackmd.io/_uploads/HyyMKC6vJx.png) và 1 file output.txt ``` vYHAW7QX6DXGkNTRiDVlK8IGELPs/zyQ4124RObD6IqNCer8ipVSMipChgu/tVKcX/IB/Zz2XmS053UxuEiQLq== ``` Kiểm tra bằng DIE thì mình thấy nó đã bị packed bằng upx ![image](https://hackmd.io/_uploads/SJUD_J0Dyl.png) ![image](https://hackmd.io/_uploads/rJ6SACTvkx.png) Sau khi unpack thì mình chạy lại chương trình cho ra kết quả khác 2+2=5, có thể đây là một cảnh báo cho việc có thể một vài chỗ khác cũng sẽ bị unpack có vấn đề. ![image](https://hackmd.io/_uploads/S1lgJkCvJe.png) Load lại vào IDA và tìm tới hàm có đoạn in ra 2 + 2 = 5 thì có được đoạn code sau: ```c __int64 sub_1400D4BE0() { unsigned int v0; // eax _QWORD *v1; // rbx __int64 v2; // rax _BYTE *v3; // rsi __int64 v4; // rdx __int64 v5; // rax __int64 v6; // rax _BYTE *v7; // rbx __int64 v8; // r8 char *v9; // r12 size_t v10; // rax __int64 v11; // rbx void *v12; // rax const char *v13; // rsi size_t v14; // rax unsigned __int64 v15; // rdi unsigned __int64 v16; // rsi _QWORD *v17; // rbx __int64 v18; // rax _BYTE *v19; // rdi __int64 v20; // rdx __int64 v21; // rax __int64 (__fastcall *v22)(); // rax __int64 (__fastcall *v23)(); // rax __int64 (__fastcall *v25)(); // rax void *v26; // [rsp+40h] [rbp-3A8h] void *v27; // [rsp+48h] [rbp-3A0h] char v28[8]; // [rsp+78h] [rbp-370h] BYREF char Str[16]; // [rsp+80h] [rbp-368h] BYREF char v30; // [rsp+90h] [rbp-358h] BYREF __int64 v31[2]; // [rsp+A0h] [rbp-348h] BYREF char v32[16]; // [rsp+B0h] [rbp-338h] BYREF const __m128i *v33[4]; // [rsp+C0h] [rbp-328h] BYREF void *Src; // [rsp+E0h] [rbp-308h] BYREF size_t Size; // [rsp+E8h] [rbp-300h] char v36; // [rsp+F0h] [rbp-2F8h] BYREF void *v37; // [rsp+100h] [rbp-2E8h] BYREF unsigned __int64 v38; // [rsp+108h] [rbp-2E0h] __int64 v39[2]; // [rsp+110h] [rbp-2D8h] BYREF __int128 v40[2]; // [rsp+120h] [rbp-2C8h] BYREF void *v41; // [rsp+140h] [rbp-2A8h] BYREF unsigned __int64 v42; // [rsp+148h] [rbp-2A0h] __int64 v43[2]; // [rsp+150h] [rbp-298h] BYREF void *v44[2]; // [rsp+160h] [rbp-288h] BYREF __int64 v45; // [rsp+170h] [rbp-278h] BYREF void *Block; // [rsp+180h] [rbp-268h] BYREF __int64 v47; // [rsp+188h] [rbp-260h] _BYTE v48[16]; // [rsp+190h] [rbp-258h] BYREF char v49[584]; // [rsp+1A0h] [rbp-248h] BYREF sub_140010020(); v0 = time64(0i64); srand(v0); v31[0] = (__int64)v32; strcpy(v32, "2 + 2 = 5"); v31[1] = 9i64; v1 = (_QWORD *)sub_1400CD390(&unk_1400D9AC0, v32, 9i64); v2 = *(_QWORD *)(*v1 - 24i64); v3 = *(_BYTE **)((char *)v1 + v2 + 240); if ( !v3 ) sub_1400D5620(); if ( v3[56] ) { v4 = (unsigned int)(char)v3[67]; } else { sub_14002DE90(*(_QWORD *)((char *)v1 + v2 + 240)); v4 = 10i64; v25 = *(__int64 (__fastcall **)())(*(_QWORD *)v3 + 48i64); if ( v25 != sub_14002E6B0 ) v4 = (unsigned int)((char (__fastcall *)(_BYTE *, __int64))v25)(v3, 10i64); } v5 = sub_140081560(v1, v4); sub_140081780(v5); sub_140005350(v33); v36 = 0; Src = &v36; Size = 0i64; while ( 1 ) { sub_1400CD390(&unk_1400D9AC0, "> ", 2i64); v6 = *(_QWORD *)(qword_1400D9760[0] - 24i64); v7 = *(_BYTE **)((char *)&qword_1400D9760[30] + v6); if ( !v7 ) sub_1400D5620(); if ( v7[56] ) { v8 = (unsigned int)(char)v7[67]; } else { sub_14002DE90(*(_QWORD *)((char *)&qword_1400D9760[30] + v6)); v8 = 10i64; v22 = *(__int64 (__fastcall **)())(*(_QWORD *)v7 + 48i64); if ( v22 != sub_14002E6B0 ) v8 = (unsigned int)((char (__fastcall *)(_BYTE *, __int64, __int64))v22)(v7, 10i64, 10i64); } sub_1400CEF40(qword_1400D9760, &Src, v8); if ( Size > 0x64 ) { sub_140005110("Input too long!!!\n"); exit(0); } if ( Size == 4 && *(_DWORD *)Src == 1953069157 ) break; sub_1400020E0(v28, 2i64); v9 = Str; do *v9++ = rand(); while ( v9 != &v30 ); v30 = 0; v37 = v39; v10 = strlen(Str); sub_140005160(&v37, Str, &Str[v10]); v11 = sub_140003950(Src, Size); v12 = memcpy(v49, *(const void **)v11, *(_QWORD *)(v11 + 8)); v40[0] = (__int128)_mm_loadu_si128(v33[0]); v40[1] = (__int128)_mm_loadu_si128(v33[0] + 1); v13 = (const char *)sub_140002AD0(v28, v12, *(_QWORD *)(v11 + 8), v40, Str); v13[*(unsigned int *)(v11 + 8)] = 0; v41 = v43; if ( !v13 ) sub_1400D56A0("basic_string: construction from null is not valid"); v14 = strlen(v13); sub_140005160(&v41, v13, &v13[v14]); v15 = v38; v16 = v42; Block = v48; v26 = v37; v48[0] = 0; v27 = v41; v47 = 0i64; sub_1400B1E40(&Block, v38 + v42); if ( 0x3FFFFFFFFFFFFFFFi64 - v47 < v15 ) sub_1400D5790("basic_string::append"); sub_1400B1F80(&Block, v26, v15); if ( 0x3FFFFFFFFFFFFFFFi64 - v47 < v16 ) sub_1400D5790("basic_string::append"); sub_1400B1F80(&Block, v27, v16); sub_1400043C0(v44, &Block, 0i64); if ( Block != v48 ) j_j_free_3(Block); v17 = (_QWORD *)sub_1400CD390(&unk_1400D9AC0, v44[0], v44[1]); v18 = *(_QWORD *)(*v17 - 24i64); v19 = *(_BYTE **)((char *)v17 + v18 + 240); if ( !v19 ) sub_1400D5620(); if ( v19[56] ) { v20 = (unsigned int)(char)v19[67]; } else { sub_14002DE90(*(_QWORD *)((char *)v17 + v18 + 240)); v20 = 10i64; v23 = *(__int64 (__fastcall **)())(*(_QWORD *)v19 + 48i64); if ( v23 != sub_14002E6B0 ) v20 = (unsigned int)((char (__fastcall *)(_BYTE *, __int64))v23)(v19, 10i64); } v21 = sub_140081560(v17, v20); sub_140081780(v21); if ( v44[0] != &v45 ) j_j_free_3(v44[0]); if ( v41 != v43 ) j_j_free_3(v41); if ( v37 != v39 ) j_j_free_3(v37); } sub_1400AF470(&Src); sub_1400AF470(v33); sub_1400AF470(v31); return 0i64; } ``` sub_140005350(v33): hàm tạo ra key Vòng lặp vô tận liên tục yêu cầu nhập vào một xâu Str: * Nếu có độ dài > 0x64 (100) thì thoát * Nếu là exit thì thoát * sub_1400020E0(v28, 2i64): ... * sub_140005160(&v37, Str, &Str[v10]): ... * sub_140003950(Src, Size): thêm padding cho Src cho đủ độ dài với một block là Size. * v13 = (const char *)sub_140002AD0(v28, v12, *(_QWORD *)(v11 + 8), v40, Str): đây là hàm mã hóa chính của chương trình, có thể là AES CBC: * v28: * v12 là input * v11 + 8: độ dài + 8 * v40: key được sinh bởi hàm sub_140005350 * Str: IV ```c __int64 __fastcall sub_140002AD0(__int64 a1, __int64 a2, unsigned int a3, __int64 a4, const __m128i *a5) { __int64 v9; // r12 void *v10; // rbp unsigned int v11; // r15d __m128i v12; // xmm0 const __m128i *v13; // rbx __m128i v15[5]; // [rsp+20h] [rbp-58h] BYREF sub_140002120(a1, a3); v9 = sub_1400D41A0(a3); v10 = (void *)sub_1400D41A0((unsigned int)(16 * (*(_DWORD *)(a1 + 4) + 1))); sub_1400027C0(a1, a4, v10); v15[0] = _mm_loadu_si128(a5); if ( a3 ) { v11 = 0; do { v12 = _mm_loadu_si128((const __m128i *)(a2 + v11)); v13 = (const __m128i *)(v9 + v11); v11 += 16; v15[0] = _mm_xor_si128(v12, v15[0]); ((void (__fastcall *)(__int64, __m128i *, const __m128i *, void *))sub_1400023F0)(a1, v15, v13, v10); v15[0] = _mm_loadu_si128(v13); } while ( v11 < a3 ); } j_j_free_2(v10); return v9; } ``` ![image](https://hackmd.io/_uploads/SJVrEbUFke.png) 16 byte đầu tiên của kết quả chính là IV. Các đoạn tiếp theo Ciper[i] sinh bởi encrypt của phép xor Plain[i] với đoạn trước đó Cipher[i-1]. Trở lại với hàm sinh key thì có vẻ như nó cố định ```c __m128i **__fastcall sub_140005350(__m128i **a1) { __int16 v1; // dx unsigned int v2; // eax int v3; // r8d char v5; // cl char *v6; // r9 unsigned int v7; // eax int v8; // edx __int64 v9; // rcx __int16 v10; // cx unsigned int v11; // edx int v12; // r8d char v13; // r9 char *v14; // r9 int v15; // edx __int64 v16; // rcx __int16 v17; // cx unsigned int v18; // edx int v19; // r8d char v20; // r9 char *v21; // r9 int v22; // edx __int64 v23; // rcx __m128i *v24; // rax __int64 v25; // rdx __m128i v26; // xmm0 __m128i v27; // xmm1 __m128i *v28; // rax unsigned int v30; // [rsp+24h] [rbp-B4h] BYREF __int64 v31; // [rsp+28h] [rbp-B0h] BYREF __m128i v32; // [rsp+30h] [rbp-A8h] BYREF __m128i v33; // [rsp+40h] [rbp-98h] BYREF char v34[136]; // [rsp+50h] [rbp-88h] BYREF v1 = 32; v2 = 79764919; v3 = 0; do { v5 = v2; v2 >>= 1; v3 = v5 & 1 | (2 * v3); --v1; } while ( v1 ); v6 = aPractice; v7 = -1; do { v8 = (unsigned __int8)*v6++; v9 = 8i64; v7 ^= v8; do { v7 = v3 & -(v7 & 1) ^ (v7 >> 1); --v9; } while ( v9 ); } while ( v6 != &aPractice[8] ); v10 = 32; v11 = 79764919; v12 = 0; do { v13 = v11; v11 >>= 1; v12 = v13 & 1 | (2 * v12); --v10; } while ( v10 ); v14 = aMakes; do { v15 = (unsigned __int8)*v14++; v16 = 8i64; v7 ^= v15; do { v7 = v12 & -(v7 & 1) ^ (v7 >> 1); --v16; } while ( v16 ); } while ( v14 != &aMakes[5] ); v17 = 32; v18 = 79764919; v19 = 0; do { v20 = v18; v18 >>= 1; v19 = v20 & 1 | (2 * v19); --v17; } while ( v17 ); v21 = aPerfect; do { v22 = (unsigned __int8)*v21++; v23 = 8i64; v7 ^= v22; do { v7 = v19 & -(v7 & 1) ^ (v7 >> 1); --v23; } while ( v23 ); } while ( v21 != &aPerfect[7] ); v30 = ~v7; sub_140001450(v34); sub_140001940(v34, &v30, 4i64); sub_140001AC0(&v32, v34); *a1 = (__m128i *)(a1 + 2); a1[1] = 0i64; v31 = 32i64; v24 = (__m128i *)sub_1400B2100(a1, &v31, 0i64); v25 = v31; v26 = _mm_loadu_si128(&v32); v27 = _mm_loadu_si128(&v33); *a1 = v24; a1[2] = (__m128i *)v25; *v24 = v26; v24[1] = v27; v28 = *a1; a1[1] = (__m128i *)v25; v28->m128i_i8[v25] = 0; return a1; } ``` Hàm khá phức tạp nên chắc sẽ phải debug để xem giá trị của a1 sau cùng luôn ![image](https://hackmd.io/_uploads/B1D9kM8Ykl.png) Với key đó thì python đã có thể giải mãi AES CBC rồi ```python import base64 from Crypto.Cipher import AES key = [0xDF, 7, 0x38, 0x2F, 0x4A, 0xFC, 0x18, 3, 0x22, 0x89, 0x65, 0x90, 0xE0, 0xC4, 0x0E, 0x93, 0x10, 4, 0x31, 0xCE, 0x49, 0x57, 0x83, 0x79, 0xC4, 0x13, 0x29, 0x5A, 0x74, 0x26, 0x60, 0x99] key = bytes(key) with open('output.txt', 'rb') as f: s = f.read() s = base64.b64decode(s) VI = s[:16] cipher = AES.new(key, AES.MODE_CBC, VI) print(cipher.decrypt(s)) ``` ![image](https://hackmd.io/_uploads/HJ6mbzIt1e.png) Nhưng nó lại không ra flag ;-; Trong lúc mò mẫn IDA bên text view để xem còn unpack được cái gì không thì có thấy một chỗ này: ![image](https://hackmd.io/_uploads/S1lRBGLFJe.png) Trong giải NMLT cũng có bài custom base64 như này nên mình đã tìm xem có hàm nào thay đổi cái này hay không vì cũng có khá nhiều thứ mình không rõ... ![image](https://hackmd.io/_uploads/ryee3GUKyg.png) ![image](https://hackmd.io/_uploads/BkMb2M8Y1e.png) Những mà cũng chả hiểu gì cả ;-; Đọc lại hint thì mình nghĩ vấn đề nằm ở đoạn unpack, vì thực tế là vụ 2 + 2 = 4 đã bị đổi thành 2 + 2 = 5 mình cũng chưa giải thích được nên giờ sẽ phải tìm cách unpack khác xem thử có bị vậy không. ![image](https://hackmd.io/_uploads/S1cfTG8YJl.png) https://kienmanowar.wordpress.com/2019/05/24/reversing-with-ida-from-scratch-p14/ Đây là lần đầu tiên tự unpack file nên hơi lằng nhằng ;-; Thử đặt break point và làm theo các bước, dump file mới thì mình ra được như này: ![image](https://hackmd.io/_uploads/r1GetQ8Fyx.png) Vậy là vấn đề đúng là ở unpack, mảng để custom base64 đã bị sai, nên giờ chỉ cần chỉnh lại xâu s về như cũ trước khi decode base64 là được ```python import base64 from Crypto.Cipher import AES key = [0xDF, 7, 0x38, 0x2F, 0x4A, 0xFC, 0x18, 3, 0x22, 0x89, 0x65, 0x90, 0xE0, 0xC4, 0x0E, 0x93, 0x10, 4, 0x31, 0xCE, 0x49, 0x57, 0x83, 0x79, 0xC4, 0x13, 0x29, 0x5A, 0x74, 0x26, 0x60, 0x99] key = bytes(key) with open('output.txt', 'rb') as f: s = f.read() base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' custom = 'UbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/' for i in range(len(s)): for j in range(len(base)): if s[i] == custom[j].encode(): s = s[:i] + base[j].encode() + s[i+1:] break s = base64.b64decode(s) VI = s[:16] print(AES.new(key, AES.MODE_CBC, VI).decrypt(s)) ``` ![image](https://hackmd.io/_uploads/S1UWqfLY1l.png) Flag: W1{do_you_enjoy_it?_06f171f7399c} # Incident ![image](https://hackmd.io/_uploads/SJSBRVLYkl.png) Bài này viết bằng Golang nên mình mất khá nhiều thời gian setup vì đây là lần đầu tiên gặp ;-; Sau một hồi nổ lực thì IDA 7.7 của mình vẫn không nhận script python của gosym ;-; Vậy là mình chuyển sang ghidra thì may còn chạy được mà đọc cứ lú lú @.@ Đọc qua các hàm thì mình thấy chỗ tương tác chính ở đây vì nó có chứa các xâu giống như trong ảnh ![image](https://hackmd.io/_uploads/Hy1dcV8F1e.png) ![image](https://hackmd.io/_uploads/HkSf3ELKkx.png) ![image](https://hackmd.io/_uploads/Syq7hVUtyg.png) ... Tiếp theo là rất nhiều những block kiểu như này ![image](https://hackmd.io/_uploads/HyzFaNIYJl.png) ![image](https://hackmd.io/_uploads/SJ_va4It1e.png) Phân tích đến hàm HasPrefix đó thì có vẻ như là nó kiểm tra một đoạn tiền tố 4 kí tự của cái xâu dài kia có khớp với input không. (diss, slap) ![image](https://hackmd.io/_uploads/B1rJRVUtke.png) * Nếu khớp diss: ![image](https://hackmd.io/_uploads/HkX2yr8Kkg.png) ... ![image](https://hackmd.io/_uploads/SJ9ugSLFkl.png) Có vẻ như đây là chỗ nó sẽ đi decryptAES cái tin nhắn của mình, nếu ppuVar3 mà bằng 0 thì sẽ in ra đống output DAT_... Trong ảnh thì mình cần chú ý đến 2 trường hợp nó không in kiểu vậy mà là một cipher text. * Nếu khớp slap: ![image](https://hackmd.io/_uploads/S1b-1HLFyg.png) ... ![image](https://hackmd.io/_uploads/BJaf1BUKJx.png) Vậy thì mình đoán là chỗ slap sẽ là base64 của keyAES * Xem bên trong hàm EncryptAES với DecryptAES thì đoán là AES_CBC ![image](https://hackmd.io/_uploads/rJggNHIYyg.png) Mình thử cả 2 cái output đó thì có 1 cái ra flag: ``` python from Crypto.Cipher import AES import base64 # slap key = base64.b64decode(b'aIhFcSvcfhbElEGdOZvXllvxqTDnfNieUTjphBKYmjs=') print(key) # diss ra output cipher = [ b'qzUzmR4gEA5NMMgueKoylxgjKp+sh2Q0Bsu0/6jQZmGTzpjoRLQl4i4wG1xZrT5eRhTG367Nvj1tSibv98zpkelFbJOrnTY1h/Ur5PK9iHbpDlTctubVpXJstc6jG5olk70iEpP/KO6klBjJ2M7BePgjBFSXsr8BJGF6bUJO5nFznHRGCy0AYWpj9cnq8P8R', b'jGX2Rwz0vIv9jke+FnDZAN10341qgcRGmsAUPFMjTF3bmFAZIwCTwJk6YO4P9BlRUWccXXGNBAaIQ0JDKytj/w==' ] for s in cipher: print(AES.new(key, AES.MODE_CBC).decrypt(base64.b64decode(s))) ``` ![image](https://hackmd.io/_uploads/HJO6lVIFyg.png) Flag: W1{lEaKeD_SuPeR_SeCrEt_rAp_bAtTlE!!!}