[QuickNote] DarkGate — Make AutoIt Great Again
1. Context
In the first quarter of 2024, @AvastThreatLabs observed a DarkGate campaign distributed via malicious PDF files:
The technique of using PDF files to lure users into downloading and executing files containing malware is not new. I have previously discussed this technique in my presentation titled Unveiling Qakbot: Exploring one of the Most Active Threat Actors at the Security Bootcamp 2023 (SBC2023) conference. We can search for more information about the domain selectwendormo9tres[.]com
on VirusTotal reveal that multiple samples are used to access this domain for downloading files.
However, the domain is currently being used as a sinkhole, which helps users avoid downloading malicious content. Sinkholes are a valuable defense mechanism in cybersecurity. They redirect malicious traffic to a controlled server, preventing it from reaching its intended malicious destination.
Therefore, in this article, I will utilize the IOCs released by @AvastThreatLabs to conduct a thorough analysis:
2. Execution Flow Summary
Here’s the high-level illustration of the malware execution flow:
3. Technical Analysis
3.1. Analyze PDF file
The PDF file is quite small in size:
Normal users, when opening this file, will be prompted to click the “” button, which will then lead them to the following link to download a malicious file: hxxps[:]//adclick[.]g.doubleclick[.]net//pcs/click?f1587wub8-24-TzRtAOnedriveBskd&&adurl=//selectwendormo9tres[.]com?utm_content=AAhqplxaJo&session_id=3VHLBRuVfwDKTPWgylgR&id=b2WBu&filter=FSBMsIgzmQ-pIvZl&lang=zh&locale=US
Similar to the February_-2024_3398702636.pdf file, this file will lure users to access the following link: hxxps[:]//adclick[.]g.doubleclick[.]net//pcs/click?f6879wbk1-2024-CnPlUOnedriveFrkd&&adurl=//selectwendormo9tres[.]com?utm_content=orHchiCJYv&session_id=QLuKkySnvaDSwwY9mSwT&id=Uwgtv&filter=PBeVeNVqPB-dEdGa&lang=es&locale=DE
Quick comparison of the links above:
According to @AvastThreatLabs, these links will download an MSI file named “”. If users trust and execute this MSI file, it will infect their system with the DarkGate malware.
3.2. Analyze MSI file
The structure of the build-x64.msi
file is as follows:
It uses a valid digital signature:
[+] Included certificates:
- Subject: CN=GlobalSign GCC R45 EV CodeSigning CA 2020, O=GlobalSign nv-sa, C=BE
Issuer: CN=GlobalSign Code Signing Root R45, O=GlobalSign nv-sa, C=BE
Serial: 159159760011286741492753271723304908269
Valid from: 2020-07-28 00:00:00+00:00
Valid to: 2030-07-28 00:00:00+00:00
- Subject: CN=Inoellact EloubantTech Optimization Information Co.\, Ltd., O=Inoellact EloubantTech Optimization Information Co.\, Ltd., STREET=Room 502\, No.22\, Jiangbu Road\, Dali Town\, Nanhai District, L=Foshan, ST=Guangdong, C=CN, 1.3.6.1.4.1.311.60.2.1.1=Foshan, 1.3.6.1.4.1.311.60.2.1.2=Guangdong, 1.3.6.1.4.1.311.60.2.1.3=CN, 2.5.4.5=91440605MACRJLFMXL, 2.5.4.15=Private Organization
Issuer: CN=GlobalSign GCC R45 EV CodeSigning CA 2020, O=GlobalSign nv-sa, C=BE
Serial: 3383085930441128603247187467
Valid from: 2024-01-26 09:28:10+00:00
Valid to: 2025-01-26 09:28:10+00:00
[+] Signer:
Issuer: CN=GlobalSign GCC R45 EV CodeSigning CA 2020, O=GlobalSign nv-sa, C=BE
Serial: 3383085930441128603247187467
Program name: None
More info: None
[+] Countersigner (nested RFC3161):
Issuer: CN=GlobalSign Timestamping CA - SHA384 - G4, O=GlobalSign nv-sa, C=BE
Serial: 2256292952261721351877727342917337962
Signing time: 2024-02-12 15:54:27+00:00
Included certificates:
- Subject: CN=Globalsign TSA for CodeSign1 - R6, O=GlobalSign nv-sa, C=BE
Issuer: CN=GlobalSign Timestamping CA - SHA384 - G4, O=GlobalSign nv-sa, C=BE
Serial: 2256292952261721351877727342917337962
Valid from: 2022-04-06 07:45:38+00:00
Valid to: 2033-05-08 07:45:38+00:00
- Subject: CN=GlobalSign Timestamping CA - SHA384 - G4, O=GlobalSign nv-sa, C=BE
Issuer: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R6
Serial: 152301165417217153014605563764
Valid from: 2018-06-20 00:00:00+00:00
Valid to: 2034-12-10 00:00:00+00:00
- Subject: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R6
Issuer: CN=GlobalSign, O=GlobalSign, OU=GlobalSign Root CA - R6
Serial: 1417766617973444989252670301619537
Valid from: 2014-12-10 00:00:00+00:00
Valid to: 2034-12-10 00:00:00+00:00
[+] Digest algorithm: openssl_sha256
[+] Digest: 69d4769c1244a1dbd1222c91f7efd4d39bc3b46edb5c9a53061cee3843e33932
Further examination using the Orca tool reveals that the CustomAction
section executes the bz.CustomActionDll
file:
The Property
section has property field related to iTunesHelper.exe
file:
3.3. Analyze bz.CustomActionDll file
A quick review of the bz.CustomActionDll
code reveals that it will interact with the iTunesHelper.exe
file located in the bz.WrappedSetupProgram
cab file:
3.4. Analyze iTunesHelper.exe file
The iTunesHelper.exe
file is a genuine Apple file, with its Pdb path is: D:\BWA\6B22E293-2BF5-0\iTunesWin-1200.12.12.9.4\srcroot\iTunes\iPodSupport\(Win32)\BuildResults\Production64\bin\iTunesHelper.pdb
. Examining the Import Directory of this file reveals that it loads a DLL file called CoreFoundation.dll
.
The CoreFoundation.dll
file is included within the bz.WrappedSetupProgram
cab file. This indicates that the attacker employed the Dll Side Loading techniques to load a DLL file containing code responsible for executing the task of malware propagation.
3.5. Analyze CoreFoundation.dll file
This is a 64-bit DLL without a digital signature:
It has the following information:
FileDescription: Project1
FileVersion: 1.0.0.0
ProgramID: com.embarcadero.Project1
ProductName: Project1
ProductVersion: 1.0.0.0
Loading this DLL file into IDA, the CFAbsoluteTimeAddGregorianUnits()
function is called from DllEntryPoint
. The function’s task is to read encrypted data stored in the sqlite3.dll
file into a buffer, decrypt it using XOR with the decryption key “VzXLKSZE
”, and finally execute the decrypted payload:
The sqlite3.dll
file before and after the decryption process:
The decrypted file (sqlite3_xored_VzXLKSZE.dll
) contains a PE file with SizeOfImage = 0x4E000
.
3.6. Analyze sqlite3_xored_VzXLKSZE.dll file
The file has a header of “MZER
”, enabling it to execute as a shellcode. This shellcode maps the file into memory and then calls the OEP address of the file to execute the main code. Its code first reads the contents of the original “sqlite3.dll
” file into the memory:
Next, it performs a search for the string “delimitador
” and use it to separate the data sections corresponding to the files. Then, decrypt Autoit3.exe file using the same decryption key as above, which is “VzXLKSZE
”. Check if the directory c:\temp
exists, if it does not exist, create it and drop the following files into it: Autoit3.exe, script.a3x, test.txt
. Finally, use the CreateProcessA
API to execute Autoit3.exe
for loading AutoIt script is script.a3x
. The script will take the file test.txt
as a parameter.
3.7. Extract and analyze AutoIt script
Based on the analysis above, the compiled script’s content can be extracted from 0x1288BE
to 0x19F01E
(size: 0x76760
bytes):
By leveraging the AutoIt Extractor tool, we can effortlessly extract the original script:
Analyzing the script, it uses the FileRead function to read the contents from the test.txt
file. Then, it employs StringSplit function on the retrieved string to generate an array of characters. Based on this character array stored in the $a variable, corresponding strings are constructed. The contents of the test.txt
file is as follows:
However, since the script is 4557 lines long, manual replacement is not feasible. An automated script needs to be written to perform this de-obfuscation task and restore the corresponding strings. To achieve this, I utilize two regular expressions as follows:
.r"\$a\[0x[0-9a-fA-F]{1,2}].*"
: This regex extracts the entire encoded string after variable$bzxrgjfo =
r"0x[0-9a-fA-F]{1,2}"
: This regex extracts all hexadecimal codes within the string.
The entire Python code I wrote to recover the script is as follows:
from argparse import ArgumentParser
import sys
import re
# Regular expression pattern to match encoded string
pattern_1 = r"\$a\[0x[0-9a-fA-F]{1,2}].*"
# Regular expression pattern to match hex codes
pattern_2 = r"0x[0-9a-fA-F]{1,2}"
# Define a dictionary to map num to characters
char_map = {0: '(', 1: 'n', 2: 'q', 3: ']', 4: 'N', 5: '*', 6: '0', 7: 'C', 8: 'V', 9: '3', 10: '&',
11: 'R', 12: 'e', 13: 'M', 14: 'O', 15: 't', 16: 'J', 17: '}', 18: 'U', 19: 'a', 20: 'D',
21: '{', 22: 'W', 23: ' ', 24: 'Z', 25: 'x', 26: 'b', 27: '8', 28: 'u', 29: 'l', 30: 'd',
31: 'Y', 32: '"', 33: 'S', 34: 'K', 35: '2', 36: 'T', 37: '1', 38: 'r', 39: 's', 40: 'p',
41: 'j', 42: '$', 43: 'o', 44: 'I', 45: 'F', 46: 'f', 47: 'G', 48: 'B', 49: '=', 50: 'Q',
51: 'L', 52: '6', 53: '5', 54: '.', 55: 'H', 56: 'c', 57: 'i', 58: '9', 59: 'w', 60: 'z',
61: ')', 62: 'E', 63: 'm', 64: '4', 65: 'g', 66: 'h', 67: 'A', 68: 'y', 69: ',', 70: 'k',
71: '7', 72: '[', 73: 'X', 74: 'v', 75: 'P'}
def mapping_char(listCharCode):
message = ""
for num in listCharCode:
message += char_map.get(num)
# return the final message
return message
def extract_index_from_str(text):
# Find all matches of the regex pattern
matches = re.findall(pattern_2, text)
# Convert hex codes to integers
idxArr = [int(hex_code, 16) for hex_code in matches]
return idxArr
def main():
parser = ArgumentParser(description="Deobfuscating DarkGate AutoIt script!!")
parser.add_argument("-i", "--input_file", dest='input_file', metavar='INPUT_FILE', type=str, required=True, help="Please specify obfuscated script")
parser.add_argument("-o", "--output_file", dest='output_file', metavar='OUTPUT_FILE', type=str, required=True, help="Write deobfuscated script to output file")
args = parser.parse_args()
try:
fin = open(args.input_file, "r")
except IOError as e:
print("Could not open file %s - %s" % (args.input_file, e))
return 1
in_lines = fin.readlines()
fin.close()
out_lines = []
for line in in_lines:
matched_str = re.search(pattern_1, line)
if matched_str:
strEncoded = matched_str.group(0)
idxArray = extract_index_from_str(strEncoded)
strDecoded = mapping_char(idxArray)
line = line.replace(strEncoded,strDecoded)
out_lines.append(line)
if out_lines:
try:
fout = open(args.output_file, "w+")
except IOError as e:
print("Coult not write to file %s - %s" % (args.output_file, e))
fout = sys.stdout
for line in out_lines:
fout.write(line)
fout.close()
print("Deobfuscating done :)")
return 0 # 0 = EXIT_SUCESS
return 1 # 1 is EXIT_FAILURE
if __name__ == "__main__":
sys.exit(main())
Here is the final result:
As a result, the variable $bzxrGJFo
will store the shellcode, which is 45,988 bytes in size. The script copies this shellcode to the variable $pt
, and utilizes the EnumWindows
function to execute it.
3.8. Extract and analyze DarkGate shellcode loader
For extracting the shellcode content from the above script, I used the following regex: "\$bzxrgjfo.+= (.+)"
Utilizing CyberChef, the shellcode was successfully dumped as follows:
recipe=Regular_expression('User%20defined','%5C%5C$bzxrgjfo.%2B%3D%20(.%2B)',true,true,false,false,false,false,'List%20capture%20groups')Find_/_Replace(%7B'option':'Extended%20(%5C%5Cn,%20%5C%5Ct,%20%5C%5Cx...)','string':'%5C%5Cn'%7D,'',true,false,true,false)From_Hex('Auto')&ieol=CRLF&oeol=NEL
This shellcode will perform the task of loading and executing a PE file in memory:
This shellcode’s complete loader code is as follows:
unsigned int __stdcall drg_shellcode_loader(PE_BASE arg_dgDelphiLoaderBaseAddr)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
if ( !drg_resolve_LoadLibraryA_GetProcAddress(&mw_resolved_api) )
{
return -1u;
}
if ( arg_dgDelphiLoaderBaseAddr.dos_hdr->e_magic != IMAGE_DOS_SIGNATURE )
{
return -2u;
}
pNtHeaders = (arg_dgDelphiLoaderBaseAddr.baseAddress + arg_dgDelphiLoaderBaseAddr.dos_hdr->e_lfanew);
if ( pNtHeaders->Signature != IMAGE_NT_SIGNATURE )
{
return -2u;
}
v3 = arg_dgDelphiLoaderBaseAddr.dos_hdr->e_res[2];
switch ( v3 )
{
case 2:
return 0x4DF;
case 3:
if ( (pNtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0 )
{
return 0x4DF;
}
result = ((arg_dgDelphiLoaderBaseAddr.baseAddress + pNtHeaders->OptionalHeader.AddressOfEntryPoint))(
arg_dgDelphiLoaderBaseAddr.baseAddress,
0,
0);
if ( result )
{
LOBYTE(arg_dgDelphiLoaderBaseAddr.dos_hdr->e_res[2]) = 2;
}
return result;
case 0:
if ( !pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress )
{
return -3u;
}
if ( !drg_perform_base_relocate(
&pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,
arg_dgDelphiLoaderBaseAddr,
pNtHeaders->OptionalHeader.ImageBase) )
{
return -4u;
}
if ( pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
&& !drg_build_import_table(
mw_resolved_api.LoadLibraryA,
mw_resolved_api.GetProcAddress,
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY].VirtualAddress,
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY].Size,
arg_dgDelphiLoaderBaseAddr) )
{
return -5u;
}
if ( pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress )
{
drg_exec_tls_callbacks(
&pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS],
arg_dgDelphiLoaderBaseAddr);
}
break;
}
LOBYTE(arg_dgDelphiLoaderBaseAddr.dos_hdr->e_res[2]) = 1;
mw_EntryPointAddr = (arg_dgDelphiLoaderBaseAddr.baseAddress + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
LOBYTE(arg_dgDelphiLoaderBaseAddr.dos_hdr->e_res[2]) = 2;
if ( (pNtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0 )
{
return mw_EntryPointAddr();
}
result = (mw_EntryPointAddr)(arg_dgDelphiLoaderBaseAddr.baseAddress, 1, 0);
if ( result )
{
LOBYTE(arg_dgDelphiLoaderBaseAddr.dos_hdr->e_res[2]) = 3;
}
return result;
}
3.9. Analyze DarkGate Delphi loader
As analyzed above, the shellcode loader’s function is to load and execute a PE file in memory. For easier analysis, let’s extract this PE. This PE is coded in Delphi:
The primary function of this loader is to decrypt the final payload, which is the DarkGate payload, from the AutoIt script, map it into memory, and execute it. To accomplish this task, it first assigns “VzXLKSZE
” to the global variable. This string is then used as a marker to retrieve the encrypted final payload and is subsequently modified for use as an XOR key for decrypting the final payload. Next, it reads the contents of the AutoIt script is script.a3x
into a buffer.
Next, utilizing the aforementioned marker string, extract the encrypted payload from the data of the AutoIt script, decrypt the DarkGate payload, and execute the decrypted payload.
The decryption process of the DarkGate payload is carried out through the drg_xor_crypt
function. In this function, the initial string ” VzXLKSZE
” is converted into a new string using the drg_xor_key_modify
function.
Rewrite the above pseudo-code in Python as follows:
# modify marker
l = len(marker)
mod_key = ''
for c in marker:
k = c ^ l
l -= 1
mod_key += chr(k)
After obtaining the new key, use this key to decrypt the DarkGate payload:
The above pseudo-code is rewritten as a function as follows:
def xor_crypt(enc_data, key):
dec_bytes = list()
idx = 0
key_len = len(key)
for i in range(len(enc_data)):
dec_bytes.append(((enc_data[i] ^ ord(key[idx]))) & 0xFF)
idx = (idx + ord(key[idx])) % key_len
if idx == 0:
idx = key_len -1
dec_data = ''.join(chr(c) for c in dec_bytes).encode('latin-1')
return dec_data
After decoding the DarkGate payload, the loader calls the function drg_mapping_and_exec_final_payload
to execute this payload directly from memory. The entire code of the drg_mapping_and_exec_final_payload
function is as follows:
void __fastcall drg_mapping_and_exec_final_payload(char *arg_pFinalPayload)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
drg_perform_refCnt(arg_pFinalPayload);
v10[2] = &savedregs;
v10[1] = System::_16747;
v10[0] = NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, v10);
pFinalPayload = drg_get_pointer_to_buffer(&arg_pFinalPayload);
// copy dos header
qmemcpy(&pPayloadDosHdr, pFinalPayload, sizeof(pPayloadDosHdr));
// copy nt headers
qmemcpy(&pPayloadNtHdr, &pFinalPayload[pPayloadDosHdr.e_lfanew], sizeof(pPayloadNtHdr));
dwPayloadSize = System::__linkproc__ DynArrayLength(arg_pFinalPayload);
pMappedPayload = VirtualAlloc(0, 8 * dwPayloadSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// copy section data
dwNumberOfSections = pPayloadNtHdr.FileHeader.NumberOfSections;
dwCount = 0;
while ( 1 )
{
qmemcpy(
&pPayloadSectionHdr,
&pFinalPayload[0x28 * dwCount + 0xF8 + pPayloadDosHdr.e_lfanew],
sizeof(pPayloadSectionHdr));
if ( pPayloadSectionHdr.SizeOfRawData )
{
drg_qmemcpy_wrap(
&pMappedPayload[pPayloadSectionHdr.VirtualAddress],
&pFinalPayload[pPayloadSectionHdr.PointerToRawData],
pPayloadSectionHdr.SizeOfRawData);
}
++dwCount;
// build import table
if ( !--dwNumberOfSections )
{
for ( import_desc = &pMappedPayload[pPayloadNtHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress];
;
++import_desc )
{
dwNameRva = import_desc->Name;
if ( !dwNameRva )
{
break;
}
dll_handle = LoadLibraryA(&pMappedPayload[dwNameRva]);
if ( dll_handle != INVALID_HANDLE_VALUE )
{
if ( import_desc->anonymous_0.OriginalFirstThunk )
{
thunkRef = &pMappedPayload[import_desc->anonymous_0.OriginalFirstThunk];
}
else
{
thunkRef = &pMappedPayload[import_desc->FirstThunk];
}
for ( funcRef = &pMappedPayload[import_desc->FirstThunk]; ; ++funcRef )
{
thunkData = *thunkRef;
if ( !*thunkRef )
{
break;
}
if ( thunkData >= 0 )
{
apiAddr = GetProcAddress(dll_handle, &thunkData->Name[pMappedPayload]);
}
else
{
apiAddr = GetProcAddress(dll_handle, *thunkRef);
}
*funcRef = apiAddr;
++thunkRef;
}
}
}
// perform base relocation
pBase = &pMappedPayload[pPayloadNtHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress];
for ( relocation = &pMappedPayload[pPayloadNtHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress];
relocation - pBase < pPayloadNtHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
relocation = (relocation + relocation->SizeOfBlock) )
{
v11 = relocation->SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION;
dwRelocationCount = System::__linkproc__ TRUNC(v11 / 2.0);
v15 = relocation + 1;
do
{
if ( LOWORD(v15->VirtualAddress) >> 0xC == IMAGE_REL_BASED_HIGHLOW )
{
relocEntry = &pMappedPayload[relocation->VirtualAddress + (v15->VirtualAddress & 0xFFF)];
*relocEntry += &pMappedPayload[-pPayloadNtHdr.OptionalHeader.ImageBase];
}
v15 = (v15 + 2);
--dwRelocationCount;
}
while ( dwRelocationCount );
}
// Execute final DarkGate payload
__asm { jmp eax }
}
}
}
Based on everything analyzed above, we can completely rewrite the script to extract DarkGate payload from AutoIt script as follows:
from argparse import ArgumentParser
def xor_crypt(enc_data, key):
dec_bytes = list()
idx = 0
key_len = len(key)
for i in range(len(enc_data)):
dec_bytes.append(((enc_data[i] ^ ord(key[idx]))) & 0xFF)
idx = (idx + ord(key[idx])) % key_len
if idx == 0:
idx = key_len -1
dec_data = ''.join(chr(c) for c in dec_bytes).encode('latin-1')
return dec_data
def get_payload(enc_file, marker_file, out_file):
marker = open(marker_file, 'rb').read()
enc_data = open(enc_file, 'rb').read()
# modify marker
l = len(marker)
mod_key = ''
for c in marker:
k = c ^ l
l -= 1
mod_key += chr(k)
blobs = enc_data.split(marker)
darkgate_enc_final_payload = blobs[1]
darkgate_payload = xor_crypt(darkgate_enc_final_payload, mod_key)
open(out_file, 'wb').write(darkgate_payload)
print("\nSaved to {}".format(out_file))
def main():
# Add arguments
parser = ArgumentParser(description="Get final DarkGate payload!")
parser.add_argument("-i", "--input_file", dest='input_file', metavar='INPUT_FILE', type=str, required=True, help="Please specify extracted AutoIt binary file!")
parser.add_argument("-m", "--marker_file", dest='marker_file', metavar='MARKER_FILE', type=str, required=True, help="Please specify marker file!")
parser.add_argument("-o", "--output_file", dest='output_file', metavar='OUTPUT_FILE', type=str, required=True, help="Write decrypted payload to output file")
# Parse arguments
args = parser.parse_args()
get_payload(args.input_file, args.marker_file, args.output_file)
if __name__ == "__main__":
main()
Here is the final result:
3.10. Analyze DarkGate payload
I will not go into detailed analysis of the DarkGate payload but rather focus on extracting the configuration it uses. The payload I am analyzing is version 6.1.9
of DarkGate:
From the main
function, it will call the drg_decode_configuration (0x0042FB0C)
to perform configuration decoding. In this function, it reads the encrypted config stored at address 0045E524
in section .DATA and calls the function drg_decrypt_config (0x00432534)
to perform the main decryption task with the initial key “ckcilIcconnh
“:
Similar to the analysis with the loader, before performing decryption, the initial key will be changed using the drg_xor_key_modify (0x004324E4)
:
Then, use the modified key above to perform configuration decryption:
Thus, based on the code written for loader analysis, we can use it to decode the configuration of this campaign as follows:
Based on this configuration information, along with payload analysis, we can describe as follows:
0=prodomainnameeforappru.com| //c2 domain
8=No // show_fake_error
11=DarkGate //Fake error message caption
12=R0ijS0qCVITtS0e6xeZ //Fake error message. Custom Base64-encoded -> "Hello world!" (charsets: "zLAxuU0kQKf3sWE7ePRO2imyg9GSpVoYC6rhlX48ZHnvjJDBNFtMd1I5acwbqT+="
13=6
14=Yes
15=443 //c2 port number
1=Yes //setup persistence
3=Yes //anti_vm based on display devices
4=Yes //check_disk
18=50 //min_disk
6=Yes //anti_vm based on display devices
7=No //check_ram
19=7000 //min_ram
5=Yes //check_xeon
21=No
22=Yes
23=No
25=admin888 //campaign ID
26=No // perform process injection using Process Hollowing technique
27=VzXLKSZE //xor_key or marker
28=No
29=1
tabla=(nq]N*0CV3&ReMOtJ}UaD{W Zxb8uldY"SK2T1rspj$oIFfGB=QL65.Hci9wz)Em4ghAy,k7[XvP //test.txt data to decrypt AutoIt script