In an effort that has been attributed by many to actors working for or on behalf of a national government, an unknown adversary compromised the software supply chain of the enterprise IT management firm SolarWinds in order to distribute malicious code.
The success of that attack, dubbed Sunburst, gave the actors wide-ranging access to corporate and governmental information systems, and already has resulted in as-yet uncalculated volumes of data theft and concerns the attackers have used the foothold to insert other backdoors into enterprise networks yet to be discovered.
Because of the magnitude of the impact of Sunburst, there have already been many reports covering the attack details. We chose to focus on a specific part of the attack of particular interest to us: the techniques used by the attacker related to sensing and evading defenses. This report provides a walkthrough of the code used by the Sunburst attack that is intended help other researchers, defenders and IT specialists to better understand that portion of the attack chain.
Based on our analysis, Sunburst used a compromised software component to use SolarWinds’ Orion to detect and in some cases attempt to disable defensive software running on targeted systems. If any of an extensive list of processes was found to be running, the component shut down completely until called again. If none of those processes were found, it checked against a list of services—terminating if some were found, and attempting to disable others. And a similar automated check was made for drivers associated with security products, also resulting in the program shutting down.
Sunburst also uses a custom DGA algorithm for its initial command and control (C2). The attackers use the DNS response for the DGA lookup to control backdoor activity, including terminating it (essentially a killswitch).
“Upgraded” code
SophosLabs analyzed a specific component of the malicious modification of SolarWinds’ software: a dynamic link library named SolarWinds.Orion.Core.BusinessLayer.dll. This DLL was created by modifying the code of a legitimate component of SolarWinds Orion, and is activated by code patched into another Orion component, InventoryManager:
This code creates a new thread which runs the Sunburst backdoor code, the entry point being the Initialize() function.
Execution only proceeds if the DLL is running within a process of name solarwinds.businesslayerhost.exe.
Curiously, further digging by the security community has identified other versions of the DLL, that have been injected with just short skeleton code, not the complete backdoor. This is probably indicative of the attackers performing some kind of testing prior to delivering the full attack.
Looking through the Sunburst code, two subtle tricks are immediately obvious:
- String obfuscation. Interesting strings (Registry keys, filenames etc) are mildly obfuscated using a combination of compression and Base64. This is presumably to make it less likely the modified source code would be spotted.
- Certain process filenames are not directly referenced in the code. Instead, hashes of process names are used. This is to make analysis more cumbersome.
In order to evade targets’ defenses, the Sunburst DLL checks for a hard-coded list of processes, services and drivers. As noted above, the names of the processes and services Sunburst looks for are checked against pre-calculated hashes of their names, making it much more difficult to analyze the code’s intent.
Flipping the switch
Sunburst checks against the environment it is running in via the ProcessTracker.TrackProcesses() function. This function is called from three places, two of them in the main flow of the backdoor’s execution:
- UpdateNotification(), called prior to entering main execution loop
- Update(), within the main execution loop
The UpdateNotification() function also resolves the api.solarwinds.com hostname. If an internal IP address is returned, execution is termination. This illustrates the care taken by the attackers to avoid the backdoor running within networks belonging to SolarWinds.
There is an additional call to TrackProcesses() within the main loop of Update(), which breaks out of the main loop if a hit is found.
In either case, if a process is detected by the malware, Sunburst execution stops until next time the malicious DLL is loaded (when the application is next run).
Three steps of evasion
The TrackProcesses() function consists of three steps: checking processes (SearchAssemblies()), checking services (SearchServices()), and checking drivers (SearchConfigurations()):
The function first calls SearchAssemblies(), passing a simple, flat list containing hardcoded hashes of 137 process names. The extraction of the reversed names is still in progress. These processes include executables tied to security products (including Tanium and AVG antivirus software), as well as packet capture, forensic and malware analysis tools, including :
- Wireshark
- VirtualBox
- RetDec decompiler
- Process Hacker
- Registry Monitor
- PE Explorer
A longer partial list of matched hashes is posted in the IOC file for this report on SophosLabs’ GitHub page.
The hashes are checked against the hashes of the lower-cased names of processes.If SearchAssemblies() does not find a process name match, the TrackProcesses() function calls SearchServices().
SearchServices() compares running services against a short list of hardcoded services, again using pre-calcuated hashes for the corresponding process filenames and Registry service subkeys.
SearchServices() compares running services against a short list of hardcoded services, again using pre-calcuated hashes for the corresponding process filenames and Registry service subkeys.
The Sunburst code attempts to disable certain services in the list, modifying the “Start” value in the service Registry configuration.
Product | Service names |
Windows Defender | msmpeng |
Windows Defender Advanced Threat Protection | mssense |
Avast | avastsvc |
Carbon Black | cavp,cb |
Crowdstrike | csfalconservice,csfalconcontainer |
FireEye | xagt |
Eset | ekrn,egui,ekrnepfw |
F-Secure | fsgk32,fsma32,fssm32,fnrb32,fsaua,fsorsp,fsav32,fsdevcon,fsgk32st,fswebuid |
If any service is disabled in this process, ConfigManager.WriteServiceStatus() is called, which writes packed data into an application settings file ( SolarWinds.Orion.Core.BusinessLayer.dll.config), storing the matched service entry. For example:…<add key=”ReportWatcherPostpone” value=”255″/>…If no services are detected or disabled, and SearchAssemblies() found no processes from the blacklist, SearchConfigurations() is called—which checks installed drivers against 17 pre-calculated hashes.
The function retrieves driver filenames using WMI “Select * From Win32_SystemDriver,” grabbing lowercased filename for each driver and generates hash for checking against the list. If any of the targeted drivers are selected, the backdoor terminates. The list includes drivers for Cyberark Endpoint Privilege Manager, the Symantec Management Agent, and Sentinel Agent; a full list of the driver filenames and hashes is available in the IOCs file on SophosLabs’ Github.
We did not find hashes matching any Sophos product.
Remote kill
Assuming all the process, service and driver checks pass, Sunburst will proceed to the main execution loop. It uses a DGA to generate a hostname (sub-domain of avsvmcloud[.]com, in which the victim hostname is encoded).The IP returned in the DNS response for the generated hostname is then checked against a list – this is used to control backdoor execution flow. (Other researchers have delved into the DGA in detail.)
For example, addresses within the private subnet ranges will terminate backdoor execution. In this case, the status is set to “Truncate” (3), and this is written to the application settings file mentioned above, specifically the ReportWatcherRetry field:
<add key="ReportWatcherRetry" value="3"/>
Saving the status here ensures the backdoor will not execute in the future. This value is checked within the Initialize() function.
Conclusions
The selectivity of the execution of Sunburst, and the method it takes to disable defenses in the least aggressive manner possible, are indicative of a cautious actor seeking to trip as few alarms as possible in their intrusion.