A Look in the SUNBURST Backdoor C2 Communication Protocol

By Igor Lahav, Security Researcher, Cynet, Inc.

A lot has been written following the supply chain attack which targeted SolarWinds Orion software, yet the backdoor is not yet fully understood. Spanning almost 3500 lines of code, “obfuscated” with casual naming, trying to evade shallow review, it has quite of subtleties yet to uncover.

Our contribution in this article is a better understanding of the command-and-control communication channel, its’ various stages and conditions required for execution. Our findings explain many of the observations seen so far regarding the SUNBURST backdoor. The main goal of this article is to understand how we can find infected beaconing machines. We will be focusing mostly on anything that is passed on the wire, while touching upon a few of the evasion techniques employed by the SUNBURST backdoor.


Following recent news regarding a supply chain attack targeting SolarWinds Orion software in order to infiltrate its customers, with emphasis on high-profile targets, such as multiple US companies and government networks, it was discovered that the threat actor behind this attack has maliciously infected a legitimate SolarWinds Orion DLL, basically transforming it into an extremely sophisticated, digitally-signed backdoor.

The backdoor begins it execution in the legitimate SolarWinds.Orion.Core.BusinessLayer.BackgroundInventory.InventoryManager.RefreshInternal function.

Cleverly masqueraded as OrionImprovementBusinessLayer this function call does not draw any attention at first glance.

Furthermore, as it runs, it checks first if it is executed as part of the legitimate “solarwinds.businesslayerhost” process. Following by staying dormant between 12 to 14 days, after first arriving or an update. Afterwards it confirms it is the only instance of the backdoor currently running on the machine using named pipe existence as a synchronization mechanism.

Diving deeper to the communication of the backdoor, we make a distinction between 3 stages in the communication:

• Stage 0: Ends with the internet connectivity check, resolving

• Stage 1: DNS communicating with *.appsync-api.*

• Stage 2: HTTPS communicating with CNAME from Stage 1

Stage 0

The main function of this stage is OrionImprovementBusinessLayer.DnsHelper.CheckServerConnection. The function receives “” as an input.

All classification of IP is done inside OrionImprovementBusinessLayer.IPAddressesHelper.GetAddressFamily and the most detailed classification is based on a list of ranges called “nList”. The actual logic of this stage can be summarized with the following diagram:

Figure 1: Actual logic behind of stage 0

This logic allows it to stop execution and evade detection on many sandboxes and hostile environments where there is some sort of a “catch-all” domain resolver.

Stage 1 – DNS-Based Communication

The code for this stage resides in the OrionImprovementBusinessLayer.Update function. While we consider the call to UpdateNotification to be part of Stage 0, all the following code, up to the call to HttpHelper.Initialize,is in Stage 1.

Stage 1, Part 1 – FQDN Generation and resolving

In the first part of this stage, a hostname is generated using one of the following 4 functions:

• GetCurrentString

• GetPreviousString

• GetNextStringEx

• GetNextString

At the end of this part, hostName will hold the FQDN to contact and resolve. No matter which method is chosen, the subdomain will be appended with one of the following 4 suffixes generated from the GetStatus function:





addressFamilyEx, in turn, will hold the resolved IP’s classification mostly based on the mentioned “nList” of ranges.

Stage 1, Part 2 – Acting based on DNS answer for the subdomain

In the second part of this stage, actions will be taken according to the classification of returned IP for this subdomain.

Any IP classification besides AddressFamilyEx.NetBios will ultimately cause failure to move to the next stage and a local config ReportWatcherRetry to be updated, but only if it were set beforehand to any value between 3 and 5.

A classification of AddressFamilyEx.Atm (anything that would have caused stage 0 to fail – meaning IP is a loopback device or in the local network or multicast IP) will cause the backdoor to call the OrionImprovementBusinessLayer.ProcessTracker.SetAutomaticMode function which will try to turn back on services in the OrionImprovementBusinessLayer.svcList list, which were previously disabled by the backdoor in OrionImprovementBusinessLayer.ProcessTracker.SetManualMode. These attempts are to shut down software which might be interfering with clear communication with the C2 DNS server. A list of products attempted to be manipulated can be found in Appendix A.

Decoded nList is shown below for reference:

To move to Stage 2 (HTTPS communication channel) we need 2 conditions:

• appsync-api subdomain IP resolution with ext=true (“ext”/“dnssec”)

 Also implies NetBios classification

 Seen as true at the end of line in the screenshot above. Also present in the table below.

 Without this condition, we will not get NetBios classification when we get CNAME later.

• CNAME response for the appsync-api subdomain (“cname”)

 Without this condition, the backdoor will not be started

Classification of AddressFamilyEx.NetBios is given to the following ranges:

Furthermore, the last 2 bytes of the IP resolved will have implications on the communication structure and will be discussed at the next stage.

Upon closer inspection of public partial list of passive DNS resolution for subdomains of *.appsync-api.* among 1460 subdomain, we found only 1 resolution that would truly activate the next stage of the backdoor. This is a testament to the pinpoint precision and OPSEC which the malicious actor was practicing.

Stage 2 – HTTPS-Based Communication

After getting through all the conditions of Stage 0 and Stage 1, we finally arrive to the last stage, Stage 2. This stage starts with the execution of OrionImprovementBusinessLayer.HttpHelper.Initialize function. Here we start to work with a Job-related state machine, in a loop.

Each round in the loop begins with some random delay (IsSynchronized). Then, if a minute passed since last time, some defense evasion checks are implemented (TrackEvent and subsequent ProcessTracker.TrackProcesses). Any intimidating process (from assemblyTimeStamps list or svcList[*].timeStamps lists) will cause this stage to abort and exit.

CreateUploadRequest is responsible for delivering results and errors of previous execution, as well as fetching the next command to execute.

ParseServiceResponse is responsible for parsing the received command from CreateUploadRequest to a Job and arguments.

Finally, ExecuteEngine executes received commands with arguments and prepares any errors and results to be sent back later.

Stage 2 – HTTPs C2 URL

The analysis of this section is based of GetBaseUriImpl as well as relevant contextual information. The URL of the 2nd Stage is largely dependent on the resolved IP of the subdomain at Stage 1. As a rule, the backdoor tries to make these URLs unique, but will give up eventually. Although HTTPS URLs are used, any certificate errors are discarded and connection continues as usual. The URL begins with https:/// and afterwards there are 3 distinct cases (“pki”, “fonts” and “swip”):

swip/Upload.ashx – last command had big output ( >10,000  bytes after compression )

swip/Events           – last command had small  output ( <= 10,000 bytes after compression )

swip/upd/              – see options in figure below.

Figure 2:  URL Structure for “swip” case, percentage represent probability of each part to appear in the final URL.

Figure  3: URL structure for “fonts” case, no matter if response exists or not.

Figure  4: URL structure for “pki” case, no matter if response exists or not.

Stage 2 – Sending Back Previous Errors and Results

If there is a result (“response”) to be sent back from previous command, it will be concatenated with the error (“err”) and the generated GUID (“customerId”). Afterwards it is compressed and XORed with the checksum. If the response is short, it is further encoded as a JSON object.

Either way, regarding the HTTP method, when there is a result (inData in CreateUploadRequestImpl), a POST method will be used, otherwise a GET method will be used.

Figure  5: First transformation to the result of previous command

Figure  6: Model of result of last command encoded as JSON object

Stage 2 – Command Parsing

The analysis in this part is based on ParseServiceResponse function. First of all, to extract anything meaningful 200 OK status code for the HTTP response required. Parsing of the HTTP response to the above-described HTTP request depends on the case we are working in, which in turn depends on the last number in the IP returned for the subdomain (Stage 1).

If the response is shorter than 4 bytes, no command is attempted to be recovered. For the first part of the parsing (“body”) can be as simple as skipping 48 bytes (in the “fonts” case) or 12 bytes (in the “pki” case). In the remaining “swip” case a more elaborate scheme employed:

• First the following regex is matched, which looks for hex encoded bytes

 “{[0-9a-f-]{36}}”|”[0-9a-f]{32}”|”[0-9a-f]{16}”

• Further cleaned, and hex decoded

After these steps, for all cases the following steps follow (which is almost the opposite of how the previous’ command result was encoded):

Figure 7: Transformation of incoming command

The rest of the command parsing is left as an exercise to the reader.

Stage 2 – Supported Command Execution

The analysis in this part is based on the ExecuteEngine function. Ultimately the back supported the following command:

Closing Notes

We have covered many aspects of the backdoor such as defense evasion mechanisms, but most notably the various stages the backdoor communicates in and tries to blend in. Future work can be directed towards figuring all the different subdomain encoding schemes, which in turn will hopefully allow to decode many of the C2 subdomains. We hope the information provided will be helpful to the Cyber Defense Community in their efforts with identifying and remediating any active beacons of this backdoor.

Cynet Protection against SUNBURST backdoor

Rest assured: Cynet 360 customers are fully protected against any attempts of abusing the SUNBURST backdoor. Customers who utilize the SolarWinds Orion software have been notified by our CyOps team.

Cynet XDR has released relevant detections for the compromised software and the
malicious backdoor, based on multiple detection vectors. We will continue to add additional
IOCs as new ones emerge.

Additional security endpoint protection recommendations:
– Isolate the vulnerable endpoint using the Cynet Dashboard, and ready for triage.
– Reset any passwords used in the SolarWinds platform, treat them as compromised
– Enable all detection and remediation mechanisms at Cynet 360 UI.

Appendix A – List of services attempted to be shut down and reset later

Thanks to the Hashcat Team for cracking the hashes of the malware.

Below are listed service names which are also keys that can be found in the registry at HKLMSYSTEMCurrentControlSetservices and the corresponding vendor.

• Windows Defender

 917638920165491138 – windefend

• Windows Defender ATP

 16335643316870329598 – sense

• Carbon Black

 11385275378891906608 – carbonblack

 13693525876560827283 – carbonblackk

 17849680105131524334 – cbcomms

 18246404330670877335 – cbstream

• Crowdstrike

 11771945869106552231 – csagent

 9234894663364701749 – csdevicecontrol

 8698326794961817906 – csfalconservice

• FireEye

 15695338751700748390 – xagt

 9384605490088500348 – fe_avk

 6274014997237900919 – fekern

 15092207615430402812 – feelam

 3320767229281015341 – fewscservice


 15587050164583443069 – eamonm

 9559632696372799208 – eelam

 4931721628717906635 – ehdrv

 3200333496547938354 – ekrn

 2589926981877829912 – ekrnepfw

 17997967489723066537 – epfwwfp

 14079676299181301772 – ekbdflt

 17939405613729073960 – epfw

• F-Secure

 17624147599670377042 – f-secure gatekeeper handler starter

 16066651430762394116 – f-secure network request broker

 13655261125244647696 – f-secure webui daemon

 12445177985737237804 – fsaua

 3421213182954201407 – fsma

 14243671177281069512 – fsorspclient

 16112751343173365533 – f-secure gatekeeper

 3425260965299690882 – f-secure hips

 9333057603143916814 – fsbts

 3413886037471417852 – fsni

 7315838824213522000 – fsvista

 13783346438774742614 – f-secure filter

 2380224015317016190 – f-secure recognizer

 3413052607651207697 – fses

 3407972863931386250 – fsfw

 10393903804869831898 – fsdfw

 12445232961318634374 – fsaus

 3421197789791424393 – fsms

 541172992193764396 – fsdevcon