Executive Summary
LevelBlue Labs recently discovered a new highly evasive loader that is being delivered to specific targets through phishing attachments. A loader is a type of malware used to load second-stage payload malware onto a victim’s system. Due to the lack of previous samples observed in the wild, LevelBlue Labs has named this malware “SquidLoader,” given its clear efforts at decoy and evasion. After analysis of the sample LevelBlue Labs retrieved, we uncovered several techniques SquidLoader is using to avoid being statically or dynamically analyzed. LevelBlue Labs first observed SquidLoader in campaigns in late April 2024, and we predict it had been active for at least a month prior.
The second-stage payload malware that SquidLoader delivered in our sample is a Cobalt Strike sample, which had been modified to harden it against static analysis. Based on SquidLoader’s configuration, LevelBlue Labs has assessed that this same unknown actor has been observed delivering sporadic campaigns during the last two years, mainly targeting Chinese-speaking victims. Despite studying a threat actor who seems to focus on a specific country, their techniques and tactics may be replicated, possibly against non-Chinese speaking organizations in the near future by other actors or malware creators who try to avoid detections.
Loader Analysis
In late April 2024, LevelBlue Labs observed a few executables potentially attached to phishing emails. One of the samples observed was ‘914b1b3180e7ec1980d0bafe6fa36daade752bb26aec572399d2f59436eaa635’ with a Chinese filename translating to “Huawei industrial-grade router related product introduction and excellent customer cases.” All the samples LevelBlue Labs observed were named for Chinese companies, such as: China Mobile Group Shaanxi Co Ltd, Jiaqi Intelligent Technology, or Yellow River Conservancy Technical Institute (YRCTI). All the samples had descriptive filenames aimed at luring employees to open them, and they carried an icon corresponding to a Word Document, while in fact being executable binaries.
These samples are loaders that download and execute a shellcode payload via a GET HTTPS request to the /flag.jpg URI. These loaders feature heavy evasion and decoy mechanisms which help them remain undetected while also hindering analysis. The shellcode that is delivered is also loaded in the same loader process, likely to avoid writing the payload to disk and thus risk being detected.
Due to all the decoy and evasion techniques observed in this loader, and the absence of previous similar samples, LevelBlue Labs has named this malware “SquidLoader”.
Most of the samples LevelBlue Labs observed use a legitimate expired certificate to make the file look less suspicious. The invalid certificate (which expired on July 15, 2021) was issued to Hangzhou Infogo Tech Co., Ltd. It has the thumbprint “3F984B8706702DB13F26AE73BD4C591C5936344F” and serial number “02 0E B5 27 BA C0 10 99 59 3E 2E A9 02 E3 97 CB.” However, it is not the only invalid certificate used to sign the malicious samples.
The command and control (C&C) servers SquidLoader uses employ a self-signed certificate. In the course of this investigation all the discovered C&C servers use a certificate with the following fields for both the issuer and the subject:
- Common Name: localhost
- Organizational Unit: group
- Organization: Company
- Locality: Nanjing
- State/Province: Jiangsu
- Country: CN
When first executed, the SquidLoader duplicates to a predefined location (unless the loader is already present) and then restarts from the new location. In this case the target location was C:\BakFiles\install.exe. This action appears to be an intentional decoy, executing the loader with a non-suspicious name, since it does not pursue any persistence method. Even though SquidLoader does not feature any persistence mechanisms, the observed second-stage payload being delivered (Cobalt Strike) has the capability of creating services and modifying registry keys, which enables the C&C operators to achieve persistence on demand.
This shellcode is delivered in the HTTPS body of the response, and it is encrypted with a 5-byte XOR key. For the sample LevelBlue analyzed, the key was hardcoded with a value of "DE FF CC 8F 9A" after accounting for little endian storage.
Figure 1: XOR decoding of the shellcode.
Despite having a filename and icon claiming to be a Word Document to deceive the victim, the samples include vast amounts of code that reference popular software products like WeChat or mingw-gcc in an attempt to mislead security researchers inspecting the file. In addition, the file and PE metadata carry references to these companies. This is done to decoy as a legitimate component of said products. However, this code will never be executed - as the execution flow will be transferred to the loaded payload before the execution reaches that point. As an example, the code below referencing WeChat was found in the WinMain function of one of the discovered samples.
Figure 2: WeChat code never executed.
Other samples reference other software products like mingw-gcc. Even though this decoy code is included, all observed executables have icons that resemble the Microsoft Office icon for Word documents, making this decoy not very credible. The malicious code even generates an alert stating “File format error cannot be opened” in simplified Chinese.
Figure 3: Alert generated by malicious code.
Defense Evasion Techniques
SquidLoader caught our attention not only because of how few detections there were for it, but how many defensive evasion and obfuscation techniques it uses. Some of these observed techniques are:
Usage of pointless or obscure instructions: Some of the functions in the binaries include obscure and otherwise pointless x86 instructions, for example: “pause”, “mfence” or “lfence”. As can be seen in the sections below, some functions also include filler instructions, like random arithmetic calculations whose results are left unused. This is potentially an attempt to break or bypass antivirus emulators as they might have not implemented less-common instructions or likely operate on a maximum of emulated instructions.
Encrypted code sections: Immediately after starting execution the malware loads a bundled encrypted shellcode. The malware decrypts it in a dynamically allocated memory section, gives said section execution privileges and finally invokes it. The encryption algorithm is a single byte XOR with a fixed displacement, as can be observed in Figure 4 - the decryption loop also includes decoy instructions to further obfuscate the code’s purpose but that are actually pointless.
Figure 4: Shellcode XOR decryption among useless instructions.
In-stack encrypted strings: Keywords that can be easily associated with malicious activity or sensitive strings in the encrypted shellcode are embedded in each function body as XOR encrypted local variables. The strings are decrypted when they are needed with a multibyte XOR key. Storing strings in the stack makes it easier to conceal sensitive information as their content will be removed from memory when the stack-frame they reside in gets overwritten by a newer stack-frame. In the below example you can see the malware decrypting the string “NtWriteVirtualMemory” to later resolve the API.
Figure 5: Encrypted sensitive strings embedded in the function body as local variables
Jumping to the middle of instructions: Some functions include a “call” or a “jmp” instruction to an address within another function. The jumps are crafted in such a way that linear disassemblers consider them to be the middle of another instruction, thus producing incorrect assembly for the function body.
As an example, in Figure 6a we can see one of such calls made by the malware.
If we explore the target location 14000770E + 2 (Figure 6b), IDA will generate incorrect assembly output because the address is in the middle of what IDA considers a different function and 140007710 won’t even show up (Figure 6b). If we were to manually mark the beginning of a function in that address, IDA would identify a different set of operations - one that allows us to properly disassemble the malicious actions taken by the loader (Figure 6c).
Figure 6a: Call function to new function Figure 6b: Wrong function parsing by IDA Figure 6c:Fixed function parsing by IDA
It is worth noting that the hidden function that we have disassembled in Figure 6c is located within the “__scrt_common_main_seh” function and the called target is the routine that decrypts and executes the bundled loader shellcode. This function is a routine generated by the standard Microsoft C compiler and is responsible for starting WinMain / main - in other words a place where custom code is not supposed to be. Therefore, the normal and expected program flow starting at WinMain would be altered, generating yet another way of obfuscating the malicious code in unexpected places. Summarizing, this technique can:
- Hide code in areas reserved for Windows default functions.
- Conceal code leveraging IDA automated disassembly processes
Return address obfuscation: The routine responsible for loading and executing the shellcode mentioned in the previous section also performs return address obfuscation via stack manipulation. At the beginning of the routine in Figure 7a we can observe how the return address points to __scrt_common_main_seh+14. The stack is then manipulated via improper stack cleanup after the last function call. This results in a stack that points to the decrypted shellcode address as its return address when the function reaches the retn instruction. The main purpose of this technique is to hinder any person or tool analyzing this code.
Figure 7a: Original return address Figure 7b: Actual return address when executing retn highlighted in blue
Control Flow Graph (CFG) obfuscation: One of the most easily identifiable obfuscation features of this family is the CFG obfuscation of the shellcode functions. The CFG is flattened into one or several infinite loops with a vast switch statement. The switch is controlled by a variable that gets assigned seemingly random values to pick the next branch to be executed. This obfuscation makes it almost impossible to know what order the switch blocks would be executed or if they would be executed at all without dynamic analysis. An example of the CFG obfuscation found in the malware can be seen below.
Figure 8: CFG technique with infinite loops and manifold switches
Debugger detection: The loader searches for the presence of debuggers at several points during its execution with three different detection methods and will crash itself by executing illegal instructions if detected.
1. The first of these methods is to check the list of running processes against a list of known debugger process names. The running process list is obtained via calling NtQuerySystemInformation with the SystemProcessInformation (0x5) information class. The full list of checked processes is:
- Ida64.exe
- Ida.exe
- DbgX.Shell
- Windbg.exe
- X32dbg.exe
- X64dbg.exe
- Olldbg.exe
Figure 9: Checking a running process against a list of blacklisted process names (XOR encrypted)
2. Later in the execution flow, the loader performs another check, looking for a debugger attached to the running process by calling NtQueryInformationProcess with the undocumented 0x1e value for the ProcessInformationClass parameter. This instructs the API to return the “debug object” of the process.
NtQueryInformationProcess (in: ProcessHandle=0xffffffffffffffff, ProcessInformationClass=0x1e, ProcessInformation=0x26ce8ff788, ProcessInformationLength=0x8, ReturnLength=0x26ce8ff788 | out: ProcessInformation=0x26ce8ff788, ReturnLength=0x26ce8ff788) returned 0xc0000353
3. The loader also looks for the presence of a kernel debugger by calling NtQuerySystemInformation with SystemKernelDebuggerInformation (0x23) system information class.
NtQuerySystemInformation (in: SystemInformationClass=0x23, SystemInformation=0x26ce8ff388, Length=0x2, ResultLength=0x0 | out: SystemInformation=0x26ce8ff388, ResultLength=0x0) returned 0x0
Quirkily enough, if the loader detects the presence of a debugger, besides crashing itself, it will also replace the prologue of WinHttpConnect with a jump to his own entrypoint. This causes the loader to not properly load the library and avoid outputting network traffic to the Command and Control (C&C) server when it reaches the payload download section. Figure 10 displays a debugger with the replaced WinHttpConnect prologue on the left versus the actual prologue in IDA on the right.
Figure 10: Code modifications after a debugger is detected
File checking: The loader also checks for the existence of the following three files and exits if it finds any of the three, but the purpose of this check is unconfirmed:
- C:\temp\diskpartScript.txt
- C:\Users\Admin\My Pictures\My Wallpaper.jpg
- C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
Performing direct syscalls: Whenever possible, the malware avoids calling Windows NT APIs and opts instead to perform their own syscalls. The malware author created several NT API wrappers, one for each NT API they wanted to wrap with different count of parameters. As an example, the wrapper for an NT API with 4 parameters can be seen in Figure 11. Note that IDA wrongfully shows a function signature that accepts only 1 parameter, the actual function accepts 4 parameters as it would be expected.
Figure 11: NT API wrapper parsed by IDA with 1 parameter instead of 4.
In this case the wrapper is resolving NtQuerySystemInformation, as it can be seen from the returned value in RAX. The +12 offset from the function start corresponds to the “syscall” x86 instruction within NtQuerySystemInformation’s function body. The function below the current one (highlighted in blue) will prepare the stack and register for the “syscall” instruction. Finally, “jump_to_syscall” moves the given syscall number to EAX and performs the jump to “NtQuerySystemInformation+12”. This avoids calling NT APIs entirely, bypassing potential hooks and thus prevents them from showing in execution logs.
Figure 12: jump_to_syscall function body.
Figure 13: the jmp instruction jumps directly to the syscall instruction.
Delivered Payload
During the time LevelBlue Labs has been analyzing this sample and the C&C server has been online we have observed only one unique payload being loaded - Cobalt Strike. The adversary simulation sample contains the same type of CFG obfuscation found in the loader, so it was probably modified by the same authors who made the loader. However, it does not contain anti-debug or anti-VM mechanisms, which are expected to be already avoided by the loader.
When executed, the payload performs an HTTPS GET request to the /api/v1/pods URI
con: Initial connection request / call home snd: Exfiltrating system information to the C&C rcv: Pinging the C&C to receive tasks
HTTP response code should be 200. An X-Session HTTP header should be present.
Evasion
Win32 API obfuscation
Conclusion
The SquidLoader sample LevelBlue Labs analyzed clearly makes an effort to avoid detection and both static and dynamic analysis. Additionally, the threat actor has been using the same Cobalt Strike beacon configuration to target Chinese-speaking victims for more than two years. Analysis in this report may not include enough data to classify this threat actor as an APT, however, the TTPs observed from this threat actor resemble those of an APT.
Additionally, given the success SquidLoader has shown in evading detection, it is likely that threat actors targeting demographics beyond China will start to mimic the techniques used by the threat actor responsible for SquidLoader, helping them to to elude detection and analysis on their unique malware samples. LevelBlue Labs will continue to track this threat actor, together with the techniques observed in this blog, to keep our clients protected from the latest trends in malware development.
Detection Methods
SURICATA IDS SIGNATURES | alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"AV TROJAN SquidLoader CobaltStrike CnC Checkin"; flow:to_server,established; content:"GET"; http_method; content:"X-Method|3a 20|"; http_header; pcre:/X-Method\x3A\x20(con|rcv)\x0d\x0a/H; reference:md5,60bec57db4f367e60c6961029d952fa6; classtype:trojan-activity; sid:4002768; rev:1; metadata:created_at 2024_06_07, updated_at 2024_06_07;) | alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"AV TROJAN SquidLoader CobaltStrike CnC Request"; flow:established,to_server; content:"POST"; http_method; content:"X-Method|3a 20|snd|0d 0A|"; http_header; content:"X-Session|3a 20|"; http_header; reference:md5,60bec57db4f367e60c6961029d952fa6; classtype:trojan-activity; sid:4002769; rev:1; metadata:created_at 2024_06_07, updated_at 2024_06_07;) |
Associated Indicators (IOCs)