Let's Learn: Dissecting Operation ShadowHammer Shellcode Internals in crt_ExitProcess

Goal: Reverse engineer and document the Operation ShadowHammer malware and its shellcode in-depth as it was originally discovered and reported by Kaspersky Labs.
Source:

Exclusive: Kaspersky researchers uncover operation #ShadowHammer - a supplychain attack targeting high-profile users. #KLResearch https://t.co/uyjK3lHP95
— Securelist (@Securelist) March 25, 2019


Sample:
Original Sample -> Setup.exe (MD5: 55a7aa5f0e52ba4d78c145811c830107)
Outline:
I. Background & Summary 
II. Main Shellcode Decoder & Execution Function
III. GetAdaptersInfo Media Access Control (MAC) Parser
IV. Compare MAC Computed MD5 Hash Against Hardcoded MD5 Set
V. Call Command-and-Control Server Function
VI. INI File Logger Function
Appendix: Hardcoded Targeted MAC Addresses
I. Background & Summary:Operation ShadowHammer is a newly discovered supply chain attack that leveraged ASUS Live Update software, originally discovered and reported by Kaspersky Labs. This case is interesting as it involves a lengthy targeting via the compromised supply chain attack vector.The attackers deploy an interesting shellcode within the backdoored ASUS “Setup.exe,” which is stored within “crt_ExitProcess” function.The main malware function is a simple add/decode blob function that is responsible for decoding and executing its core shellcode.By and large, the shellcode performs MAC lookup logic for victims of interest via parsing GetAdaptersInfo API return and struct AdapterAddresses for physical devices media access control (MAC) numbers, which are oftentimes unique enough to identify PCs.Moreover, the malware shellcode deployed MD5Init, MD5Update, and MD5Final API calls to create an MD5 hash of the MAC address treated as byte object.The shellcode leverages the WININET library to call the server appending “?”+ unique identifier if there is a match with the identified hardcoded MD5 value and the victim machine MAC MD5.Otherwise, the shellcode is responsible for writing the file to the local user directory while altering the timestamps in the written file leveraging FileTimeToSystemTime and adding 0x93A80 => 604800 seconds => 7 days. It is, currently, one of the oddities of this malware. The purpose of this date alteration ahead is unclear at this time.The original MISP JSON, CSV, and STIX indicators of compromise (IOCs) as well as the raw extracted and decoded shellcode is available here on my GitHub.You can simply check if you are targeted by the MD5 MAC match by generating an MD5 value of physical device MAC addresses and comparing against the hardcoded MD5 ones from this malware sample as provided below.
II. Main Shellcode Decoder & Execution Function

The attackers deploy the shellcode decoder and execution routine within the ASUS “Setup.exe” as seen below.

The “shelldecoder” function simply allocates the memory for the obfuscated shellcode, decodes it, and executes it.The decoding algorithm employed is as follows:
///////////////////////////////////////////////////////
/////////// OP ShadowHammer Decoder ///////////////////
///////////////////////////////////////////////////////

len = 16;
do
{
blob = *enc_blob++;
*allocated++ = blob;
--len;
}

Next, the shellcode resolves a plethora of API calls via process environment block (PEB) traversal technique.
///////////////////////////////////////////////////////
/////////// OP ShadowHammer Resolved APIs ///////////////////
///////////////////////////////////////////////////////
0028FC48 76E250C1 kernel32.LoadLibraryExW
0028FC4C 76E2C43A kernel32.VirtualAlloc
0028FC50 76E2EF35 kernel32.GetModuleFileNameW
0028FC54 76E18649 kernel32.WritePrivateProfileStringW
0028FC58 76E2D816 kernel32.GetSystemTimeAsFileTime
0028FC5C 76E2BE0D kernel32.FileTimeToSystemTime
0028FC60 76E36B15 kernel32.VirtualFree
0028FC64 770A4CC0 ntdll.memcpy
0028FC68 770A3B2F ntdll.memcmp
0028FC6C 770A5340 ntdll.memset
0028FC70 7713A569 ntdll.swprintf
0028FC74 7713A41F ntdll.sprintf
0028FC78 770A5640 ntdll.strncat
0028FC7C 77079DDC ntdll.MD5Init
0028FC80 77079EA4 ntdll.MD5Update
0028FC84 77079E16 ntdll.MD5Final
0028FC88 72B36A4D IPHLPAPI.GetAdaptersAddresses
0028FC8C 7618B880 WININET.InternetOpenA
0028FC90 76246000 WININET.InternetOpenUrlA
0028FC94 76187540 WININET.InternetQueryDataAvailable
0028FC98 76181C80 WININET.InternetReadFile
Then, the shellcode enters its main function responsible for obtaining and comparing MAC addresses and calling the server, among other functionality:
///////////////////////////////////////////////////////
/////////// OP ShadowHammer Main Function ///////////////////
///////////////////////////////////////////////////////

result = GetAdaptersInfo_Alloc(a1, 0, 1);
if ( result > 0 )
{
v3 = 20 * (result + 5);
hash_mac_alloc = VirtualAlloc(
0,
20 * (result + 5),
12288,
4);
memset(hash_mac_alloc, 0, v3);
result = GetAdaptersInfo_Alloc(v1, hash_mac_alloc, 0);
v4 = result;
if ( result > 0 )
{
memset(&v5, 0, 44);
if ( qmempy_memcmp_for_computed_md5_mac(v1, &v6, hash_mac_alloc, v4, (int)&v5) )
result = call_c2(v1, (int)&v5);
else
result = file_creation_log(v1);
}
}
return result;
III. GetAdaptersInfo Media Access Control (MAC) Parser
One of the most notable functions of the malware simply parses GetAdaptersInfo API return and struct AdapterAddresses for physical devices media access control (MAC) numbers via EDI+2C return, which is used to generate an MD5 hash value of the length 16 as seen below.

The shortened pseudocoded function is as follows:

hashta< MISP JSON/CSV/#STIX hashtawith the extracted raw shellcode to https://lnkd.in/dMcZJ_g
///////////////////////////////////////////////////////
/////////// OP ShadowHammer Obtain & Hash MAC ///////////////////
///////////////////////////////////////////////////////
int __usercall GetAdaptersInfo_Alloc@<eax>(int a1@<esi>, int hash_match, int a3)
{


v9 = 0;
if ( GetAdaptersInfo (0, 0, 0, 0, &v9) == 0x6F )// 0x6F BUFFER_RUN_OVERFLOW
{
VirtualAlloc_ret = VirtualAlloc(0, v9, 4096, 4);
if ( !GetAdaptersInfo(0, 0, 0, VirtualAlloc_ret, &v9) )

v7 = 0;
if ( VirtualAlloc_ret )
{
dest = hash_match;
do
{
if ( *(_DWORD *)(VirtualAlloc_ret + 52) > 0u )
{
if ( !a3 )
{
MD5Init(&MD5_CTX_context);
MD5Update(&MD5_CTX_context, VirtualAlloc_ret + 44, 6);// len = 6
MD5Final(&MD5_CTX_context);
memcpy(dest, &source, 16);
}
++v7;
dest += 20;
}
VirtualAlloc_ret = *(_DWORD *)(VirtualAlloc_ret + 8);
}
while ( VirtualAlloc_ret );
}
}
result = v7;
}
else
{
result = 0;
}
return result;
IV. Compare MAC Computed MD5 Hash Against Hardcoded MD5 Set
The shellcode simply checks via memcmp API and parses the list of the hardcoded MD5 values against the machine generates MD5 MAC addresses via the following excerpt:
///////////////////////////////////////////////////////
///// OP ShadowHammer Compare MD5 Hash Excrept ////////
///////////////////////////////////////////////////////
while ( 1 )
{
v18 = *(_DWORD *)v20;
if ( v18 == 1 )
{
qmemcpy(&v13, v20, 0x2Cu);
v5 = 0;
v19 = 0;
if ( a4 )
{
mac_machine = a3;
while ( memcmp(&pre_computed, mac_machine, 16) )
{
++v19;
mac_machine += 20;
if ( v19 >= a4 )
goto LABEL_9;
}
v5 = 1;
}

The matched list of the hardcoded MD5 hashes is as follows:
///////////////////////////////////////////////////////
///// OP ShadowHammer Hardcoded MD5 List //////////////
///////////////////////////////////////////////////////

00B006C7DAB6ACE6C25C3799EB2B6E14
5977BAA3F8CE0CA1C96D6AC9A40C9A91
409D8EEBCE8546E56A0AD740667AADBD
7DA42DD34574D4E1A7EA0E708E7BC9A6
ADE62A257ADF118418C5B2913267543E
4268AED64AA5FFF2020D2447790D7D32
7B14C53FD3604CC1EBCA5AF4415AFED5
3A8EA62E32B4ECBE33DF500A28EBC873
CC16956C9506CD2BB389A7D7DA2433BD
FE4CCC64159253A6019304F17102886D
F241C3073A5777742C341472E2D43EEC
AB0CEF9E5957129E23FBA178120FA20B
F758024E734077C70532E90251C5DF02
F35A60617AB336DE4DAAC799676D07B6
6A62EAD801802A5C9EC828D0C1EDBB5B
600C7B52E7F80832E3CEE84FCEC88B9D
6E75B2D7470E9864D19E48CB360CAF64
FB559BCD103EE0FCB0CF4161B0FAFB19
690AD61EC7859A0964216B66B5D33B1A
09DA9DF3A050AFAD0DF0EF963B41B6E2
FAE3B06AB27F2B0F7C29BF7F2B03F83F
D4B958671F47BF5DCD08705D80DE9A53
V. Call Command-and-Control (C2) Server Function
If the malware is able to match successfully the hardcoded MD5 values and the machine MAC MD5 address, it proceeds to the C2 call function; otherwise, it writes a .ini file. The C2 server call function is rather trivial relying on the WININET DLL and the following API calls ANSI-version InternetOpenA, InternetOpenUrlA, InternetReadFile, InternetQueryDataAvailable.
The call appends and formats via sprintf API the URL to “?” and the unique identifier of the victim added to the URL string hxxps://asushotfix[.]com/logo2[.]jpg.
VI. INI File Logger Function When the malware fails to find a match with the hardcoded MD5 MAC addresses, it calls this interesting .ini logger function essentially creating a file called “idx.ini” in user directory with the timestamp set to +7 days from the current system timestamp. It is one of the odditiies of the malware. 
The pseudo-coded function is as follows:
///////////////////////////////////////////////////////
///// OP ShadowHammer Hardcoded MD5 List //////////////
///////////////////////////////////////////////////////

memset((__int16 *)&v10, 0, 16);
GetSystemTimeAsFileFileTime(&v67);// GetSystemTimeAsFileFileTime
v3 = time_proc(v67 - 0x19DB1DED53E8000i64, 0x989680u, 0);
if ( v3 > 32535244799i64 )
{
LODWORD(v3) = 0xFFFFFFFF;
HIDWORD(v67) = 0xFFFFFFFF;
}
v4 = (signed int)v3 + 0x93A80 - 0x49EF6F00i64;// 0x93A80 = 604800 seconds = 7 days
HIDWORD(v5) = HIDWORD(v4) + 2;
LODWORD(v5) = v4;
v68 = time_proc(v5, 0x989680i64);
FileTimeToSystemTime(&v68, (__int16 *)&v10);
swnprintf(&v9, &v13, v10, v11, v12);
WritePrivateProfileStringW(&v58, &v42, &v9, (__int16 *)&v8);
WritePrivateProfileStringW(&v58, &v26, &v9, &v8);
result = WritePrivateProfileStringW(&v58, &v50, &v9, (__int16 *)&v8);
}
return result;
}
Appendix: Hardcoded Targeted MAC Addresses
///////////////////////////////////////////////////////
///// OP ShadowHammer Hardcoded MD5 List //////////////
///////////////////////////////////////////////////////

00B006C7DAB6ACE6C25C3799EB2B6E14
5977BAA3F8CE0CA1C96D6AC9A40C9A91
409D8EEBCE8546E56A0AD740667AADBD
7DA42DD34574D4E1A7EA0E708E7BC9A6
ADE62A257ADF118418C5B2913267543E
4268AED64AA5FFF2020D2447790D7D32
7B14C53FD3604CC1EBCA5AF4415AFED5
3A8EA62E32B4ECBE33DF500A28EBC873
CC16956C9506CD2BB389A7D7DA2433BD
FE4CCC64159253A6019304F17102886D
F241C3073A5777742C341472E2D43EEC
AB0CEF9E5957129E23FBA178120FA20B
F758024E734077C70532E90251C5DF02
F35A60617AB336DE4DAAC799676D07B6
6A62EAD801802A5C9EC828D0C1EDBB5B
600C7B52E7F80832E3CEE84FCEC88B9D
6E75B2D7470E9864D19E48CB360CAF64
FB559BCD103EE0FCB0CF4161B0FAFB19
690AD61EC7859A0964216B66B5D33B1A
09DA9DF3A050AFAD0DF0EF963B41B6E2
FAE3B06AB27F2B0F7C29BF7F2B03F83F
D4B958671F47BF5DCD08705D80DE9A53
V. Call Command-and-Control (C2)

Article Link: https://www.vkremez.com/2019/03/lets-learn-dissecting-operation.html