---
# System prepended metadata

title: 'Digital Dragons CTF 2023 '
tags: [RE, CTF]

---

### Đôi Lời Muốn Nói
:::success
* Hôm nay mình có tham gia giải Digital Dragons CTF 2023. Mình cũng giải được hết tất cả các bài re và 1 bài crypto khá dễ :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. 
* Thì mình muốn viết writeup để chia sẻ cách làm cho mọi người :smiley:.
:::
### Baby Rev 1
:::info
* Đầu tiên, mình chạy thử chương trình thì nó hiển thị thông báo failed :thinking_face:. 
:::
![](https://hackmd.io/_uploads/rJmr87m23.png)
:::info
* Mình dùng IDA để mở disassembly thử thì thấy nó đang mở một file có tên là **RZHJw** bằng API **open()** và cấp phát bộ nhớ để lưu dữ liệu từ file này bằng API **mmap()**. Địa chỉ của bộ nhớ lưu dữ liệu được lưu trong biến **v8**.
:::
![](https://hackmd.io/_uploads/rJLaGXm2h.png)
:::info
* Dựa vào nhận xét trên, mình thử tạo một file có tên là RZHJw và cho nội dung của nó là chuỗi **"we are legion"**.
* Tiếp tục mình thử chạy lại chương trình thì thấy rằng thông báo failed không còn được hiển thị nữa nhưng mà lúc này nó lại hiển thị thông báo **"Wrong! No flag for you"**. Điều này có nghĩa là nội dung trong file RZHJw không đúng so với yêu cầu của chương trình.
:::
![](https://hackmd.io/_uploads/Sk7lI7Qnn.png)
:::info
* Mình tiếp tục xem thử đoạn mã còn lại thì thấy chương trình sẽ hiển thị thông báo **"Correct! Here is your flag:"** nếu như dest giống với **EXPECTED_RESULT**.
* Đầu tiên, ta thấy rằng dest sẽ sao chép 12 kí tự đầu từ kí tự thứ 7 của v8 mà nội dung của v8 lại là nội dung của file RZHJw.
* Tiếp theo, chương trình tiếp tục hoán đổi giá trị của dest[4] và dest[8] cho nhau.
* Cuối cùng chương trình tiếp tục thực hiện vòng for để thay đổi giá trị của kí tự **dest[i] và dest[11 - i]** cho nhau.
:::
![](https://hackmd.io/_uploads/Hk9EnQXh2.png)
:::info
* Đây là giá trị của EXPECTED_RESULT.
:::
![](https://hackmd.io/_uploads/r18wKm73h.png)
:::info
* Oce, dựa vào những nhận xét trên thì mình đã code một đoạn code nhỏ để giải quyết challenge này. Flag sẽ là **BoDR)[6`tFLm**
:::
```cpp=
#include<stdio.h>

int main() {
	unsigned char flag[] = "mLFtR6[)`DoB";
	unsigned char temp = '\0'; 
	temp = flag[4];
	flag[4] = flag[8];
	flag[8] = temp;
	for (int i = 0; i < 6; i++) {
		temp = flag[i];
		flag[i] = flag[11 - i];
		flag[11 - i] = temp;
	}
	printf("flag: %s\n", flag);
	return 0;
}
```
### Baby Rev 2
:::info
* Đầu tiên mình cũng chạy thử chương trình, nó bắt mình nhập offset gì đó và một byte :thinking_face:.
* Và cuối cùng ta bị lỗi segmentation fault. Ủa zậy là sao :smile: ?
:::
![](https://hackmd.io/_uploads/S1jj14m3n.png)
:::info
* Mình mở IDA lên xem thử mã assembly thì thấy đầu tiên nó tính toán v9 bằng cách thực hiện phép and giữa bin_padding với một giá trị hexa 8 byte.
* Sau đó, nó tiếp tục thực hiện API mprotect(), API này được dùng để thay đổi protection của bộ nhớ tiến trình. Nhìn vào các tham số được truyền vào trong API này, ta có thể thấy rằng nó đang thay đổi protection RWX cho các page liền kề nhau trên bộ nhớ dựa vào v9 :smile:. 
* Tiếp theo, chương trình yêu cầu mình nhập 2 byte offset v6 và 1 byte value v5 để thực hiện thay đổi giá trị một byte tại địa chỉ v9 + v6 cho v5. Điều này cũng giải thích vì sao lỗi segmentation fault được hiển thị lên lúc đầu. Lí do là vì địa chỉ v9 + v6 của ta thuộc về page chỉ có read và execute chứ không có write, mà ta lại đi thay đổi giá trị của byte tại vị trí này nên lỗi nảy sinh :smiley:.
:::
![](https://hackmd.io/_uploads/SJWrlV7hh.png)
:::info
* Tiếp theo, chương trình tiếp tục sao chép 0x18 byte bắt đầu từ byte thứ 16 của argv[1] cho dest và đồng thời tính hash cho dest bằng thuật toán MD5. 
* Sau khi có hash rồi thì so sánh nó với EXPECTED_RESULT, nếu giá trị hash này bằng EXPECTED_RESULT thì flag sẽ được in ra màn hình.
:::
![](https://hackmd.io/_uploads/BkO-mEQnh.png)
:::info
* Đây chính là giá trị của EXPECTED_RESULT và nó được biểu diễn bằng **75734e3168416c675846777a684e3864** ở dưới dạng hexa:smiling_face_with_smiling_eyes_and_hand_covering_mouth:.
:::
![](https://hackmd.io/_uploads/SkpaVEXhn.png)
:::info
* Mình đã cố gắng truy cập các web MD5 trên mạng để tìm kiếm giá trị trước khi hash của chuỗi 75734e3168416c675846777a684e3864 nhưng kết quả không khả quan lắm :cry:. 
:::
![](https://hackmd.io/_uploads/B17II4m3n.png)
![](https://hackmd.io/_uploads/BkgZuNQnh.png)

:::info
* Sau một lúc suy nghĩ thì mình đã nghĩ ra hướng giải quyết cho bài này. Chẳng phải một số page của chương trình đã bị thay đổi protection thành RWX bằng API mprotect() luôn hay sao :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. Vậy thì sẽ ra sao nếu các page bị thay đổi protection đó là code của chương trình ?
* Câu trả lời là lúc này ta có thể thay đổi code của chương trình luôn chứ sao :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. Nếu như suy luận của mình đúng thì ta có thể thay đổi 1 byte giá trị code của chương trình sao cho flag sẽ được in ra. Vậy thì ta nên thay đổi byte nào trong rất nhiều câu lệnh của chương trình :thinking_face: ?
:::
![](https://hackmd.io/_uploads/r1Sru4Qh2.png)
:::info
* Câu trả lời là lệnh **jnz short loc_1CC0**, mình muốn rằng mặc dù giá trị hash của dest không bằng EXPECTED_RESULT thì flag vẫn được in ra. Để làm được điều này thì mình thay đổi byte offset của lệnh nhảy từ giá trị 0x47 thành 0x0 là được. 
:::
![](https://hackmd.io/_uploads/H1o2sE72h.png)
:::info
* Vậy là vấn đề đã gần được giải quyết xong rồi, việc mình cần làm tiếp theo là tính toán v6 sao cho **v9 + v6** phải bằng địa chỉ của lệnh **jnz short loc_1CC0 + 1**. Cùng dùng GDB để tính toán thôi nào :smiling_face_with_smiling_eyes_and_hand_covering_mouth:.
* Tại vị trí **main+197** là vị trí ta đi tính toán giá trị cho v9, giá trị này sẽ được lưu trữ tại thanh ghi rax.
:::
![](https://hackmd.io/_uploads/BJPZ6Vm3n.png)
:::info
* Giá trị v9 sẽ là 0x555555555000 như trong hình.
:::
![](https://hackmd.io/_uploads/B1IDT4mnn.png)
:::info
* Còn địa chỉ của lệnh jnz short loc_1CC0 sẽ là 0x0000555555555c77.
:::
![](https://hackmd.io/_uploads/SyPATNmn2.png)
:::info
* Vậy từ đây ta suy ra offset sẽ là 0x0000555555555c77 + 1 - 0x555555555000 = 0xc78 và value là 0x0.
* Kết quả là flag được in ra, vậy là suy luận của mình đã đúng :nerd_face:.
:::
![](https://hackmd.io/_uploads/rk5VCEQhh.png)
### Moonshot
:::info
* Đầu tiên mình cũng thử chạy chương trình thôi, nó cũng cho mình nhập đầu vào. Mình thử nhập chuỗi x thì nó hiện ra thông báo gì đó mình cũng không hiểu lắm :thinking_face:. 
:::
![](https://hackmd.io/_uploads/ryoPgBXh3.png)
:::info
* Tiếp theo, mình mở IDA lên để xem thử và đập vào mắt mình là rất nhiều biến được hiển thị ra :tired_face:.
:::
![](https://hackmd.io/_uploads/HJSo1Smnh.png)
:::info
* Tiếp theo, ta thấy nó gán giá trị check = 0, input = argc, và các giá trị từ v19 đến v26 đều được gán giá trị cụ thể.
:::
![](https://hackmd.io/_uploads/BkJ6-rmhh.png)
:::info
* Tiếp theo nó thực hiện rất nhiều vòng for lồng nhau, mình cũng không biết mục đích của nó là gì nữa. Nhưng mình để ý là nó sẽ gọi đến hàm **FUN_00101160()** với các tham số là biến v20 đến v26. 
:::
![](https://hackmd.io/_uploads/BJ7zMSmn2.png)
![](https://hackmd.io/_uploads/ry58zB7hn.png)
:::info
* Trong FUN_00101160() thực hiện in ra rất nhiều kí tự bằng hàm putchar() và mình nghĩ rằng nó sẽ in ra flag. Vậy thì bây giờ việc ta cần làm là làm sao để hàm FUN_00101160() sẽ được gọi :thinking_face:. Nếu như ta dựa vào input để khiến cho FUN_00101160() được gọi thì điều này sẽ khá khó bởi vì code thật sự rất rối và nếu dịch ngược lại thì mất khá nhiều thời gian.
* Sau một lúc kiểm tra code của chương trình thì mình phát hiện ra rằng giá trị các biến từ v19 đến v26 đều không bị thay đổi trong quá trình thực thi. Điều này có nghĩa là các tham số được truyền vào FUN_00101160() luôn được giữ nguyên. 
* Tới đây thì dễ rồi, mình chỉ cần lấy code của hàm FUN_00101160() đi thực thi là flag sẽ được in ra rồi :smiling_face_with_smiling_eyes_and_hand_covering_mouth:.
:::
![](https://hackmd.io/_uploads/Bys2frX3h.png)
:::info
* Đây chính là định nghĩa hàm main() của mình.
:::
![](https://hackmd.io/_uploads/H18c_Bmnh.png)
:::info
* Còn đây là định nghĩa hàm FUN_00101160().
:::
![](https://hackmd.io/_uploads/Byba_BX2n.png)
:::info
* Sau khi biên dịch chương trình và chạy mình thu được flag là **"what_a_mess"**.
:::
![](https://hackmd.io/_uploads/Sk3MtS7n2.png)

### Xory
:::info
* Bài này là một bài crypto, thì đề bài cho mình một file python và một câu lệnh **nc 114.119.185.67 1338**.
:::
![](https://hackmd.io/_uploads/Hyzd5rQnh.png)
:::info
* Sau khi mình dùng lệnh nc 114.119.185.67 1338 thì nó trả về cho mình một chuỗi hexa như trong hình.
:::
![](https://hackmd.io/_uploads/SJCT9rQ3n.png)
:::info
* Còn đây chính là source code tạo ra cipher mà đề bài cung cấp cho mình. Về chức năng của nó thì cũng khá đơn giản thôi. Nó tạo random một key có 5 byte và sau đó lấy key này xor lần lượt cho flag để in ra cipher.
* Hmm, vậy làm thế nào để tìm ra flag khi key là random. Nếu ta brute force key thì sẽ rất mất thời gian, bởi vì số lượng phép tính sẽ lên đến cả tỉ phép tính :thinking_face:.
* Thì tới đây mình nghĩ đến việc rằng flag rất có thể sẽ bắt đầu bằng chuỗi **"flag{"** mà chuỗi này lại gồm có 5 kí tự nên mình nghĩ tới việc lấy 5 kí tự này xor cho 5 giá trị hexa đầu tiên của cipher thì sẽ ra key. 
* Sau khi có key rồi thì mình sẽ decrypt các byte hexa còn lại của cipher để tìm ra flag :smiling_face_with_smiling_eyes_and_hand_covering_mouth:.
:::
![](https://hackmd.io/_uploads/SJ1l3B733.png)
:::info
* Và đây là code do mình chuẩn bị để thực hiện lấy flag :smiling_face_with_smiling_eyes_and_hand_covering_mouth:.
:::
```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned char cipher[] = {
        0xe3, 0x6d, 0x90, 0xe9, 0xd8, 0xdd, 0x31, 0xa3,
        0xd1, 0x92, 0xb0, 0x5e, 0x86, 0xbd, 0x97, 0xee,
        0x5e, 0xc0, 0xe8, 0xfc, 0xfc, 0x31, 0x84, 0xd1,
        0xc8, 0xeb, 0x31, 0x86, 0xd1, 0xe8, 0xd5, 0x40,
        0x8c, 0x84
};
unsigned char plain_text[100];
unsigned char key[5];
unsigned char message[] = "flag{";

void xory_decrypt(unsigned char* cipher, unsigned char* key, 
                  int cipher_len, unsigned char* plaintext) {
    for (int i = 0; i < cipher_len; i++) {
        plaintext[i] = cipher[i] ^ key[i % 5];
    }
}

int main() {
    for (int i = 0; i < 5; i++) {
        key[i] = cipher[i] ^ message[i];
    }
    int cipher_len = strlen((const char*)cipher);
    xory_decrypt(cipher, key, cipher_len, plain_text);
    plain_text[cipher_len] = '\0';
    printf("%s\n", plain_text);
    return 0;
}
```
:::info
* Sau khi mình thực hiện đoạn mã trên thì flag thu được là **X0R_15_w34k_1f_y0u_kn0w_KPA**.
:::
![](https://hackmd.io/_uploads/B1IoTrm22.png)



















