Section 1: Analysis of the flaw
The SUNBURST vulnerability is a supply chain attack that created an evasive backdoor into the systems of numerous global entities. An infamous group of hackers known as ATP29 or ‘Cozy Bear’, members of the Russian GRU, are believed to be behind the attack but organizations like FireEye have not yet attributed the attack to any particular ATP. By a currently undetermined method, the hackers were able to gain a foothold into internal systems owned by the company Orion – the creators of the popular set of IT management tools called ‘Solarwinds’. After gaining this foothold, the hackers were suspected to have injected malicious code into Solarwinds tools during a routine software update.
There is some evidence that the initial foothold of the Solarwinds codebase was taken sometime in 2019 and then tested in October of 2019 by slightly modifying the code to contain a .NET class that was later used for the actual backdoor [1]. This proof of concept would provide the confidence needed to later implement the remaining exploits using the SUNBURST exploit that we will discuss here.
The SUNBURST backdoor, discovered and discussed thoroughly by FireEye [2], was injected into the codebase and is what kicked off the future exploits. This code sits idly on the machine for a random period of up to 2 weeks and then executes. Once it executes it checks against a hashed blocklist full of forensic and anti-virus processes running on the machine. If none are present, “the malware will attempt to resolve the subdomain of avsvmcloud.com and the DNS response will return a CNAME record that points to a Command and Control (c2) domain “[2]. Once this c2 domain is accessed the SUNBURST execution passes the reigns to other exploits injected into the codebase.
Section 2: Concrete exploits and breaches
The first bit of the SUNBURST code is a sleeping algorithm [1A]. Kaspersky labs has analyzed this code and believes that it has similarities to another malware, Kazuar by the Russian hacking group Turla [3]. This code generates a random number of minutes somewhere between 12 and 14 days and puts the exploit to sleep until that time. During this sleep time no communication will happen between the software and the C2 server. This code also contains the ability to adjust that period if necessary.
The next relevant segment of code will use the FNV-1a hash to obfuscate some of the activity happening. First it will check if the hash of the full Solarwinds process matches the expected hash. If it does not, it will not execute. Next the code will search a list of hashes of known antivirus programs, Microsoft SysInternals tools, and other software that the hacking group was potentially worried would set off the alarms of their backdoor. Each of these hashes obfuscated what the program was doing and quick discreet comparisons while hiding the actions in seemingly innocuous names such as ‘OrionImprovementBusinessLayer.assemblyTimeStamps’.
Upon finding that hash it attempts to edit the registry key for that file to disable the process, that way on the next restart it will run the check again and see it is disabled and can safely continue.
Now that the malicious code has finished the sleep timer and determined that none of the important processes are running, the code will attempt to communicate with the C2 server. By making a DNS request to a concatenated string of the server region and avsvmcloud.com [3A], a CNAME response will tell the code what the current C2 server is. This allows the attacker to be flexible with which C2 server they use in case they need to move the server to a different domain. This C2 server will then execute commands that are interpreted by the host. This is the backdoor! Appendix item [4A] is the list of items that FireEye was able to enumerate of commands that can be executed remotely. The interesting thing about the SUNBURST code is how it seems to be espionage focused and not destructive. The commands present in the FireEye investigation show that the code can adjust wait times in other calls, kill tasks, collect and report on system information, write files, delete files, and run tasks. It is likely that, depending on the instance, the hackers were using this backdoor to leverage future attacks instead of relying entirely upon SUNBURST to damage systems. It is entirely possible to inject ransomware or disk destroying software using SUNBURST, but as far as we’ve seen in public reporting there doesn’t seem to be any destructive code executed. Unfortunately, once the SUNBURST code has gotten this far, it is nearly impossible to know if another bit of backdoor malware was inserted.
Section 3: Remediation
The remediation for SUNBURST is unfortunately very straight forward. Because this backdoor is incredibly invasive and operates with SYSTEM or admin level privileges by nature of it being injected into a trusted software and because it can lead to full remote code execution, the only way to truly remediate SUNBURST is to isolate infected machines and replace them. If your organization intends to continue to use Solarwinds products, they have released an updated version that removes the malicious code.
The nuke and pave approach to remediation is the worst-case scenario when it comes to fixing vulnerabilities. To avoid this unfortunate scenario, the remediation actually needed to happen well before the vulnerability. There is a reason that the hackers wrote their code to avoid running when tools from Crowdstrike, Carbon Black, or SysInternal. These tools would have detected the suspicious behavior. Further, effective server isolation and monitoring would have also alerted to this suspicious activity. Looking for unexpected registry changes would have alerted to some of the early stages of this code and could have allowed system administrators the ability to isolate those machines and further investigate. Further, tiered server infrastructure with important or ‘crown jewel’ servers having very strict rules on external communication could have stopped this software in its tracts.
Section 4: References
[1]https://blog.reversinglabs.com/blog/sunburst-the-next-level-of-stealth
[3]https://securelist.com/sunburst-backdoor-kazuar/99981/
[4]https://www.crowdstrike.com/blog/sunspot-malware-technical-analysis/
[5]https://blog.cloudflare.com/solarwinds-orion-compromise-trend-data/
[6]http://blog.eckelberry.com/a-preliminary-look-into-who-was-hacked-in-the-sunburst-attack/
[7]https://www.mimecast.com/blog/important-update-from-mimecast/
[8]https://nvd.nist.gov/vuln/detail/CVE-2020-14005
[9]https://securityboulevard.com/2021/02/more-solarwinds-news/
[10]https://www.youtube.com/watch?v=JoMwrkijTZ8
[11]https://github.com/ITAYC0HEN/SUNBURST-Cracked/blob/main/OrionImprovementBusinessLayer_modified.cs
Section 5: Appendix
[1A] Sleeping Algorithm:
private static void DelayMs(double minMs, double maxMs) { if ((int)maxMs == 0) { minMs = 1000.0; maxMs = 2000.0; } double num; for (num = minMs + new Random().NextDouble() * (maxMs - minMs); num >= 2147483647.0; num -= 2147483647.0) { Thread.Sleep(int.MaxValue); } Thread.Sleep((int)num); } |
[2A] FNV-1a Hash
public static void Initialize() { try { if (OrionImprovementBusinessLayer.GetHash(Process.GetCurrentProcess().ProcessName.ToLower()) == 0xEFF8D627F39A2A9DUL) //"solarwinds.businesslayerhost" { // backdoor execution code } } } |
[2B] FNV-1a Hash for processes
private static bool SearchAssemblies(Process[] processes) { for (int i = 0; i < processes.Length; i++) { ulong hash = OrionImprovementBusinessLayer.GetHash(processes[i].ProcessName.ToLower()); if (Array.IndexOf<ulong>(OrionImprovementBusinessLayer.assemblyTimeStamps, hash) != -1) { return true; } } return false; } |
[3A] Domain concat
private string GetStatus() { |
[4A] C2 commands (From source [2])
| Command | Value | Operation |
| Idle | 0 | No operation |
| Exit | 1 | Terminate the current thread. |
| SetTime | 2 | Sets the delay time between main event loop executions Delay is in seconds, and varies random between [.9 * <delay>, 1.1 * <delay>]. If the delay is < 300 it is doubled on the next execution through the loop, this means it should settle onto an interval of around [5, 10] minutes. There is a second, unrelated delay routine that delays for a random interval between [16hrs, 83hrs] |
| CollectSystemDescription | 3 | Profile the local system including hostname, username, OS version, MAC addresses, IP address, DHCP configuration, and domain information. |
| UploadSystemDescription | 4 | Perform a HTTP request to the specified URL, parse the results and compare components against unknown hashed values. Format a report and send to the C2 server. |
| RunTask | 5 | Starts a new process with the given file path and arguments |
| GetProcessByDescription | 6 | Returns a process listing. If no arguments are provided returns just the PID and process name. If an argument is provided it also returns the parent PID and username and domain for the process owner. |
| KillTask | 7 | Terminate the given process, by PID. |
| GetFileSystemEntries | 8 | Given a path and an optional match pattern recursively list files and directories |
| WriteFile | 9 | Given a file path and a Base64 encoded string write the contents of the Base64 decoded string to the given file path. Write using append mode. Delay for [1s, 2s] after writing is done. |
| FileExists | 10 | Tests whether the given file path exists. |
| DeleteFile | 11 | Deletes the specified file path. |
| GetFileHash | 12 | Compute the MD5 of a file at a given path and return result as a HEX string. If an argument is provided, it is the expected MD5 hash of the file and returns an error if the calculated MD5 differs. |
| ReadRegistryValue | 13 | Arbitrary registry read from one of the supported hives |
| SetRegistryValue | 14 | Arbitrary registry write from one of the supported hives. |
| DeleteRegistryValue | 15 | Arbitrary registry delete from one of the supported hives |
| GetRegistrySubKeyAndValueNames | 16 | Returns listing of subkeys and value names beneath the given registry path |
| Reboot | 17 | Attempts to immediately trigger a system reboot. |