---
# System prepended metadata

title: Reversing task 2 - wannagame championship 2024
tags: [Write-up]

---

# 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!!!}