Flare-on 10 WriteUp
Flare-on 10 2023 vừa mới kết thúc, mình cũng đã giải được một số challenges và dưới đây là một số bài writeup của mình. (Flare-on là một cuộc thi CTF hàng năm do FLARE tổ chức, cuộc thi này về mảng RE và kéo dài trong 6 tuần).
Challenge 01
Sử dụng dnSpy mở X.dll, ta dễ dàng tìm thấy flag ở hàm _lockButton_Click

Challenge 02
Sử dụng JADX để đọc file .apk đề bài cho


Nhìn vào nội dung file strings.xml, mình chú ý đến một số string name như ag, alg, c2, file, iv. Ta có thể đoán chương trình có thể xử lý dữ liệu liên quan đến file .png bằng thuật toán AES và giao tiếp với C2 https://flare-on[.]com/evilc2server/report_token/report_token.php?token=
Tiếp tục kiểm tra AndroidManifest, mình xác định được nơi chương trình bắt đầu và chương trình có đăng ký Firebase Cloud Messaging.
Kiểm tra class xử lý game MalwareInvadersActivity, ta sẽ không tìm thấy bất cứ điều gì để tìm ra flag. Kiểm tra class MessageWorker, có 2 method được ghi đè là onMessageRecieved() và onNewToken(). Để ý hàm onMessageRecieved() mỗi khi nhận message, nó sẽ xử lý message và gọi setFullScreenIntent để cài đặt thông báo với tham số pending intent là c.f362a.a(this, str)

Tùy thuộc vào message được nhận từ onRecieveMessage(), hàm c.f362a.a() sẽ gán các giá trị i2, i3 tương ứng và gọi vào bvar.f(context, i3), i3 ở đây có thể là tên file iv.png hoặc ps.png được đặt trong resource.





Challenge 03
Chương trình yêu cầu tham số đầu vào. Tham số chứa nhiều dữ liệu/điều kiện kiểm tra cho mỗi hàm hoặc shellcode, chúng được cách nhau bởi ký tự ‘/’.- Ngay đầu chương trình, ta dễ thấy tham số arvg1 được kiểm tra với các điều kiện. Đối với những điều kiện đơn giản như so sánh thì mình sẽ không đề cập. Lần kiểm tra đầu 4*argv1[1] + argv1[2] = !295. Ở đây mình chọn 2 ký tự là “=, 3”.

Tham số argv1[7:12] được xor với key “ten” và so sánh với 0x16, 0x17, 0x3b, 0x17, 0x56 trong hàm sub_1EA00(), sử dụng python ta dễ dàng tìm được giá trị đúng của argv1[7:12]

Kết quả argv1[7:12] = “brUc3”. Kết hợp với một số điều kiện so sánh khác, ta có được dữ liệu tham số đầu tiên của tham số Argv1[] = “0=300R@brUc3E/input2/...”
Đối với input2: Dữ liệu được chia làm 2 phần, phần số được gán vào đầu file html như một signature, phần sau là tên của file html. Nội dung của file .html đã bị mã hóa được hardcode sẵn, dữ liệu của file được biến đổi dựa theo ngày hiện tại của hệ thống.
Input3 được kiểm tra ở hàm sub_1EAE0(), input3 cũng được chia làm 2 phần, phần đầu là một số hệ 10 sẽ được chuyển đổi sang hệ 4 ở sub_71A0() và là kích thước của chuỗi phần sau.

Phần đầu của input3 sau khi chuyển sang hệ 4 sẽ được làm tham số cho hàm sleep(). Tuy nhiên vì chương tình có kiểm tra thời gian thực thi bằng cách 2 lần gọi GetTickCount(), lần gọi 2 ở sub_18B0(). Nếu khoảng thời gian giữa 2 lần gọi bé hơn hoặc bằng 8s thì chương trình sẽ exit. Vì vậy, mình chọn tham số input3 = “20bcadbcda” (20 chuyển sang hệ 4 là 8, chuỗi ‘bcadbcda’ được chọn bất kỳ, miễn kích nó bằng phần trước)

Input5 yêu cầu chúng ta cần phải sửa một số byte của 2 shellcode ở hàm sub_2170(). Nếu bạn quen với việc phân tích mã độc thì không khó để nhận ra chức năng của 2 shellcode, shellcode thứ nhất dùng để lấy base address của kernel32.dll và shellcode thứ 2 lấy địa chỉ của hàm API. Ở đây, chúng ta chỉ cần sửa các byte để shellcode có thể thực thi làm đúng chức năng của nó là được. Mình chọn input5 = “AMu$E`0R.0AZe”. Chúng ta có tham số argv1[] = “0=300R@brUc3E/1337pr.ost /20bcadbcda/5pizza/ AMu$E`0R.0AZe/input6/...”
Input6 sau khi biến đổi sẽ được so sánh với “RUECKWAERTSINGENIEURWESEN” trong hàm sub_2910().

Dữ liệu file .html sau khi được giải mã bằng thuật toán RC4 với key = “REVERSEENGINEER” sẽ được checksum bởi hash crc32 ở hàm sub_2670(). Bây giờ, nếu bạn muốn chạy chương trình với tham số tìm được mà ra flag luôn thì buộc phải tìm đúng dữ liệu file .html. Tuy nhiên file html được gen từ một buffer hardcode sẵn và encode liên quan đến ngày hiện tại trên hệ thống nên cần phải thử từ ngày 1->31 để tìm được ngày đúng.


Challenge 04
Chương trình ban đầu tạo một cửa sổ có tên BananaAimBot có hàm callback 402AF0().
Mỗi khi có sự hiện WM_COMMAND hay chính là sự kiện click vào BUTTON “Launch Sauerbraten with Aimbot”, hàm callback sẽ gọi hàm 402150(). Ngoài ra, khi thấy tác giả viết chương trình nhắc đến sauerbraten by Potassium Crew như là một sự giới thiệu hay quảng cáo, mình nghĩ đến việc nó sẽ khởi chạy 1 chương trình/trò chơi khác.
Tiếp tục kiểm tra hàm 402150 (), ban đầu hàm này kiểm tra file “%PROGRAMFILES(X86)%\\Sauerbraten\\bin64\\sauerbraten.exe” và tính hash md5, sau đó so sánh với 1 giá md5 hardcode. Nếu bằng nhau chương trình tiếp tục thực thi, ngược lại thì nó sẽ exit.

Sau khi lên mạng tìm kiếm Sauerbraten, mình thấy nó là một trò chơi và thử tải phiên bản 2020_12_21 về cài đặt, sau khi kiểm tra hash thì mình thấy md5 đã bằng giá trị được hardcode nên tiếp tục phân tích luồng của chương trình.

Chương trình sau đó trích xuất 3 file miner.exe, config.json, aimbot.dll vào thư mục %APPDATA%\\BananaBot bằng cách giải mã resource sử dụng AES với key là “yummyvitamincjoy”


- Windows api: IsDebuggerPresent, CheckRemoteDebuggerPresent, VirtualProtect, DbgBreakPoint
- Checksum hash tên các tiến trình như: ida64.exe, x64dbg.exe
- Kiểm tra parrent process có phải là aimbot.exe
- Kiểm tra aimbot.dll có chạy trong sauerbraten.exe
- http://127.0.0.1:57328/2/summary
- bananabot 5000
- "version": "
- the decryption of this blob was successful
Sau đó chương trình giải mã shellcode bằng cách đọc key từ XMRig http://127.0.0.1:57328/2/summary. Shellcode được giải mã AES ECB với key "version": "6.20
Sau khi giải mã, ta thu được 1 file chứa shellcode:
Xóa phần header, lấy dữ liệu từ offset 0x2A, ta tiếp tục phân tích với ida. Shellcode sẽ giải mã những shellcode khác và thực hiện lần lượt các hành vi đánh cắp dữ liệu của discord, steams, sparraw và lưu vào C:\depot. Sau đó sẽ gửi những dữ liệu này tới https://bighackies.flare-on.com/stolen.
Shellcode cuối cùng, đọc dữ liệu từ file .cfg của map trong trò chơi, và so sánh 4 byte đầu với “spcr”. Nếu đúng nó sẽ tiếp tục thực thi.

Tuy nhiên trò chơi có 2 map có chứa “spcr”. Chỉ cần debug shellcode và sau 2 lần thử chơi game với map spcr và spcr2 và thực thi bỏ qua các điều kiện IF đến cuối shellcode, thì với map spcr2 ta có được flag.

Challenge 05: Where am i
Sử dụng API monitor, ta thấy chương trình load 2 resource và đều cấp phát vùng nhớ PAGE_EXCECUTE_READWRITE. Khả năng chương trình load và thực thi shellcode từ resource.

Ngoài, ta cũng có thể thấy chương trình tạo tiến trình Explorer.exe và sử dụng kỹ thuật APC injection để inject shellcode/PE file và sau đó tạo pipe name whereami để giao tiếp với tiến trình vừa tạo.

Như vậy, cơ bản chúng ta đã hiểu được luồng thực thi của chương trình. Tiến hành đặt breakpoint ở API VirtualAllocEx để xác định vùng bộ nhớ được tạo trong explorer.exe và WriteProcessMemory để theo dõi dữ liệu được ghi vào vùng nhớ đó, ta thấy dữ liệu được inject là shellcode.
Tiến hành dump shellcode. Vì shellcode được làm rối khá nặng bằng các kỹ thuật multi jump kết hợp junkcode. Tùy nhiên ghidra có thể đánh bại các kỹ thuật này nên mình đã chuyển shellcode thành file exe bằng công cụ shell2exe và phân tích nó bằng ghidra.

Ta thấy, shellcode tiếp tục giải mã một số phần dữ liệu của nó. Đặt breakpoint tại RVA 0x1df của shellcode và trace qua hàm decrypt, sau khi decrypt ta thấy dữ liệu sau giải mã là một pe file bị xóa một số phần header.


- Đường dẫn file thực thi có chứ “C\\Users\\Public\\”
- Username có phải là “flare”
- baseImage+0x3FC có bằng 0xBAADBEEF
Nếu thỏa mãn các điều kiện, nó sẽ hiện thị messagebox với nội dung gợi ý có mã hóa RC6 ở đâu đó và dĩ nhiên là chúng ta vẫn chưa có flag mặc dù đã đi hết luồng chương trình.
Vì các shellcode được làm rối khá nhiều nên mình nghĩ có gì đó được dấu trong shellcode. Mình quyết định dump 2 shellcode sau giải mã từ 2 resource ban đầu và chuyển chúng sang file exe và sử dụng ghidra để phân tích.
Thật may là mình đã phát hiện hàm genkey của thuật toán mã hóa RC6 với các signature 0xb7e15163 và 0x9e3779b9 được dùng để giải mã resource2.

Đặc biệt key dùng giải mã chính là 0xBAADBEEF ở offset 0x3fc, giá trị này cũng được kiểm tra trong điều kiện ở file PE được dump


Tiến hành thay dữ liệu resource2 bằng vùng dữ liệu ở giữa 2 0xBAADBEEF, sau khi chương trình gọi hàm rc6decrypt ở RVA 0x69df từ vùng nhớ của shellcode1, ta có được flag.
