Ramin Nafisi, Author at Microsoft Security Blog http://approjects.co.za/?big=en-us/security/blog Expert coverage of cybersecurity topics Wed, 07 Feb 2024 01:10:35 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.2 FoggyWeb: Targeted NOBELIUM malware leads to persistent backdoor http://approjects.co.za/?big=en-us/security/blog/2021/09/27/foggyweb-targeted-nobelium-malware-leads-to-persistent-backdoor/ Mon, 27 Sep 2021 19:00:34 +0000 In-depth analysis of newly detected NOBELIUM malware: a post-exploitation backdoor that Microsoft Threat Intelligence Center (MSTIC) refers to as FoggyWeb. NOBELIUM uses FoggyWeb to remotely exfiltrate the configuration database of compromised AD FS servers, decrypted token-signing certificate, and token-decryption certificate, as well as to download and execute additional components.

The post FoggyWeb: Targeted NOBELIUM malware leads to persistent backdoor appeared first on Microsoft Security Blog.

]]>
Microsoft continues to work with partners and customers to track and expand our knowledge of the threat actor we refer to as NOBELIUM, the actor behind the SUNBURST backdoor, TEARDROP malware, and related components. As we stated before, we suspect that NOBELIUM can draw from significant operational resources often showcased in their campaigns, including custom-built malware and tools. In March 2021, we profiled NOBELIUM’s GoldMax, GoldFinder, and Sibot malware, which it uses for layered persistence. We then followed that up with another post in May, when we analyzed the actor’s early-stage toolset comprising EnvyScout, BoomBox, NativeZone, and VaporRage.

This blog is another in-depth analysis of newly detected NOBELIUM malware: a post-exploitation backdoor that Microsoft Threat Intelligence Center (MSTIC) refers to as FoggyWeb. As mentioned in previous blogs, NOBELIUM employs multiple tactics to pursue credential theft with the objective of gaining admin-level access to Active Directory Federation Services (AD FS) servers. Once NOBELIUM obtains credentials and successfully compromises a server, the actor relies on that access to maintain persistence and deepen its infiltration using sophisticated malware and tools. NOBELIUM uses FoggyWeb to remotely exfiltrate the configuration database of compromised AD FS servers, decrypted token-signing certificate, and token-decryption certificate, as well as to download and execute additional components. Use of FoggyWeb has been observed in the wild as early as April 2021.

Microsoft has notified all customers observed being targeted or compromised by this activity. If you believe your organization has been compromised, we recommend that you

  • Audit your on-premises and cloud infrastructure, including configuration, per-user and per-app settings, forwarding rules, and other changes the actor might have made to maintain their access
  • Remove user and app access, review configurations for each, and re-issue new, strong credentials following documented industry best practices.
  • Use a hardware security module (HSM) as described in securing AD FS servers to prevent the exfiltration of secrets by FoggyWeb.

Microsoft security products have implemented detections and protections against this malware. Indicators of compromise (IOCs), mitigation guidance, detection details, and hunting queries for Azure Sentinel and Microsoft 365 Defender customers are provided at the end of this analysis and in the product portals. Active Directory Federation Services (AD FS) servers run on-premises and customers can also follow detailed guidance on securing AD FS servers against attacks.

FoggyWeb: Backdoor targeting AD FS

FoggyWeb is a passive and highly targeted backdoor capable of remotely exfiltrating sensitive information from a compromised AD FS server. It can also receive additional malicious components from a command-and-control (C2) server and execute them on the compromised server.

After compromising an AD FS server, NOBELIUM was observed dropping the following two files on the system (administrative privileges are required to write these files to the  folders listed below):

  • %WinDir%\ADFS\version.dll
  • %WinDir%\SystemResources\Windows.Data.TimeZones\pris\Windows.Data.TimeZones.zh-PH.pri

FoggyWeb is stored in the encrypted file Windows.Data.TimeZones.zh-PH.pri, while the malicious file version.dll can be described as its loader. The AD FS service executable Microsoft.IdentityServer.ServiceHost.exe loads the said DLL file via the DLL search order hijacking technique that involves the core Common Language Runtime (CLR) DLL files (described in detail in the FoggyWeb loader section). This loader is responsible for loading the encrypted FoggyWeb backdoor file and utilizing a custom Lightweight Encryption Algorithm (LEA) routine to decrypt the backdoor in memory.

After de-obfuscating the backdoor, the loader proceeds to load FoggyWeb in the execution context of the AD FS application. The loader, an unmanaged application, leverages the CLR hosting interfaces and APIs to load the backdoor, a managed DLL, in the same Application Domain within which the legitimate AD FS managed code is executed. This grants the backdoor access to the AD FS codebase and resources, including the AD FS configuration database (as it inherits the AD FS service account permissions required to access the configuration database).

Diagram showing structure of Microsoft.IdentityServer.ServiceHost.exe after loading version.dll

When loaded, the FoggyWeb backdoor (originally named Microsoft.IdentityServer.WebExtension.dll by its developer) functions as a passive and persistent backdoor that allows abuse of the Security Assertion Markup Language (SAML) token. The backdoor configures HTTP listeners for actor-defined URIs that mimic the structure of the legitimate URIs used by the target’s AD FS deployment. The custom listeners passively monitor all incoming HTTP GET and POST requests sent to the AD FS server from the intranet/internet and intercept HTTP requests that match the custom URI patterns defined by the actor. This version of FoggyWeb configures listeners for the following hardcoded URI patterns (which might vary per target):

  • HTTP GET URI pattern:
    • /adfs/portalhttps://www.microsoft.com/images/theme/light01/profile.webp
    • /adfs/portalhttps://www.microsoft.com/images/theme/light01/background.webp
    • /adfs/portalhttps://www.microsoft.com/images/theme/light01/logo.webp
  • HTTP POST URI pattern:
    • /adfs/services/trust/2005/samlmixed/upload

Each HTTP GET/POST URI pattern above corresponds to a C2 command:

  • When the AD FS server receives an HTTP GET request containing the URI pattern /adfs/portalhttps://www.microsoft.com/images/theme/light01/profile.webp, the backdoor retrieves the token signing certificate of the compromised AD FS server and then obfuscates and returns the certificate to the issuer of the request.
  • Similarly, when the AD FS server receives an HTTP GET request containing the URI pattern /adfs/portalhttps://www.microsoft.com/images/theme/light01/background.webp, the backdoor retrieves the token decryption certificate of the compromised AD FS server and then obfuscates and returns the certificate to the issuer of the request.
  • When the AD FS server receives an HTTP GET request containing the URI pattern /adfs/portalhttps://www.microsoft.com/images/theme/light01/logo.webp, the backdoor retrieves the AD FS configuration data of the compromised server, obfuscates the data, and returns the obfuscated data to the issuer of the request.
  • When the AD FS server receives an HTTP POST request containing the URI pattern /adfs/services/trust/2005/samlmixed/upload, the backdoor treats the obfuscated and compressed POST data as either .NET assembly or source code. If assembly, the backdoor executes the assembly in the execution context of the AD FS process. If source code, the backdoor dynamically compiles the source code and proceeds to execute the resulting memory-resident assembly in the execution context of the AD FS process.

The diagram below illustrates the methodology used by the actor to communicate with the FoggyWeb backdoor located on a compromised internet-facing AD FS server.

Diagram showing FoggyWeb attack chain

Since FoggyWeb runs in the context of the main AD FS process, it inherits the AD FS service account permissions required to access the AD FS configuration database. This contrasts with tools such as ADFSDump that must be executed under the user context of the AD FS service account. Also, because FoggyWeb is loaded into the same application domain as the AD FS managed code, it gains programmatical access to the legitimate AD FS classes, methods, properties, fields, objects, and components that are subsequently leveraged by FoggyWeb to facilitate its malicious operations. For example, this allows FoggyWeb to gain access to the AD FS configuration data without connecting to the WID named pipe or manually running SQL queries to retrieve configuration information (for example, to obtain the EncryptedPfx blob from the configuration data). FoggyWeb is also AD FS version-agnostic; it does not need to keep track of legacy versus modern configuration table names and schemas, named pipe names, and other version-dependent properties of AD FS.

FoggyWeb loader

The file version.dll is a malicious loader responsible for loading an encrypted backdoor file from the file system, decrypting the backdoor file, and loading it in memory. This malicious DLL, which shares a name with a legitimate Windows DLL located in the %WinDir%\System32\ folder, is meant to be placed in the main AD FS folder %WinDir%\ADFS\, where the AD FS service executable Microsoft.IdentityServer.ServiceHost.exe is located (for reasons described later in this section).

When the AD FS service (adfssrv) is started, the service executable Microsoft.IdentityServer.ServiceHost.exe gets executed. As a .NET-based managed application, Microsoft.IdentityServer.ServiceHost.exe imports an unmanaged Windows DLL named mscoree.dll.

Screenshot showing Microsoft.IdentityServer.ServiceHost.exe importing mscoree.dll.

The file mscoree.dll dynamically loads another unmanaged Windows/CLR DLL named mscoreei.dll. As shown below, mscoreei.dll has a delay load import (Delay Import) named version.dll.

Screenshot showing mscoreei.dll has a delay load import (Delay Import) named version.dll

NOBELIUM, with existing administrative permissions, was observed to drop a malicious loader named version.dll in the %WinDir%\ADFS\ folder where the AD FS service executable Microsoft.IdentityServer.ServiceHost.exe is located. Once the system or the AD FS service is restarted, Microsoft.IdentityServer.ServiceHost.exe loads mscoree.dll, which in turn loads mscoreei.dll. As mentioned above, mscoreei.dll has a delay load import named version.dll. Once loaded, instead of loading the legitimate version.dll from the %WinDir%\System32\ folder mscoreei.dll loads the malicious version.dll planted by the attacker in %WinDir%\ADFS\ folder (referred to as DLL search order hijacking), as shown in the call stack below.

Screenshot of call stack showing folder mscoreei.dll loads the malicious version.dll planted by the attacker in %WinDir%\ADFS\ folder

The malicious loader version.dll behaves as a proxy for all legitimate version.dll export function calls. As shown below, it exports the same 17 function names as the legitimate version of version.dll.

Screenshot of dump of the legitimate version.dll  Screenshot of the malicious version.dll

The export functions of the malicious version.dll are all short stubs that call a single trampoline function labeled TrampolineFunction, as seen in the screenshot below.

Screenshot of the export functions of the malicious version.dll

Below is a pseudocode for the trampoline function.

Screenshot of pseudocode for the trampoline function

This trampoline function is responsible for the following:

  • Calling a function (labeled as LoadDecryptExecuteBackdoor() by the analyst) to load a backdoor file from the file system, and then decrypting and executing the file in memory
  • Transferring execution to the initially called target function from the legitimate version of version.dll.

The trampoline function preserves the value of the arguments/registers intended for the function from the legitimate version of version.dll by saving the value of certain CPU registers. It first pushes them onto the stack before calling the LoadDecryptExecuteBackdoor() function above and then restoring them before transferring execution to the function from the legitimate version of version.dll.

Screenshot of code showing trampoline function preserves the value of the arguments/registers intended for the function from the legitimate version of version.dll by saving the value of certain CPU register

When called, LoadDecryptExecuteBackdoor() attempts to create a Windows event named {2783c149-77a7-5e51-0d83-ac0566daff96} to ensure that only one copy of the loader is actively running on the system. In a new thread, it then checks if the following file is present (hardcoded path string):

C:\Windows\SystemResources\Windows.Data.TimeZones\pris\Windows.Data.TimeZones.zh-PH.pri

Windows.Data.TimeZones.zh-PH.pri is an encrypted backdoor file that is placed in the folder above. MSTIC refers to this backdoor file as FoggyWeb, and our analysis is in the next section.

Microsoft.IdentityServer.ServiceHost.exe in and of itself is an unmanaged Windows executable that is generated when the high-level AD FS managed code is compiled. When executed, the unmanaged code inside Microsoft.IdentityServer.ServiceHost.exe leverages Common Language Runtime (CLR) to run the managed AD FS code within a virtual runtime environment. This virtual runtime environment is comprised of one or more application domains, which provide a unit of isolation for the runtime environment and allow different applications to run inside separate containers within a process. The managed AD FS code is executed within an application domain inside the virtual runtime environment.

The FoggyWeb backdoor (also a managed DLL) is intended to run alongside the legitimate AD FS code (that is, within the same application domain). This means that for the FoggyWeb loader to load the backdoor alongside the AD FS code, it needs to gain access to the same application domain that the AD FS code is executed within. Since the FoggyWeb loader version.dll is an unmanaged application, it cannot directly access the virtual runtime environment that the managed AD FS code is executed within. The loader overcomes this limitation and loads the backdoor alongside the AD FS code by leveraging the CLR hosting interfaces and APIs to access the virtual runtime environment within which the AD FS code is executed.

The loader performs the following high-level actions:

  • Enumerate all CLRs loaded in the AD FS process Microsoft.IdentityServer.ServiceHost.exe
  • For each CLR, enumerate all running application domains and perform the following actions for each domain:
    • Read the contents of the following encrypted FoggyWeb backdoor file into memory: C:\Windows\SystemResources\Windows.Data.TimeZones\pris\Windows.Data.TimeZones.zh-PH.pri
    • Decrypt the encrypted FoggyWeb backdoor file using the Lightweight Encryption Algorithm (LEA). The LEA-128 key schedule uses the following hardcoded master key to generate the round keys:

Screenshot of hardcoded master key to generate the round keys

After decrypting each 16-byte cipher block, the loader uses the following XOR key to decode each individual decrypted/plaintext block:

Screenshot of XOR key to decode each individual decrypted/plaintext block

This is equivalent to first LEA decrypting the entire file and then XOR decoding the decrypted data (instead of decrypting and XOR decoding each individual 16-byte block).

    • Create a Safe Array and copy the decrypted FoggyWeb backdoor bytes to the array. It then calls the Load() function for the current application domain to load the FoggyWeb DLL into the application domain. After the FoggyWeb DLL is loaded into the current application domain, the loader invokes the following method from the DLL: Microsoft.IdentityServer.WebExtension.WebHost.

At this point in the execution cycle, the FoggyWeb DLL is loaded into one or more application domains where the legitimate AD FS code is running. This means the backdoor code runs alongside the AD FS code with the same access and permissions as the AD FS application. Because the backdoor is loaded in the same application domain as the AD FS code, it gains programmatical access to the legitimate classes, methods, properties, fields, objects, and components used by various AD FS modules to carry out their legitimate functionality. Such access allows the FoggyWeb backdoor to directly interact with the AD FS codebase (that is, not an external disk-resident tool) and selectively invoke native AD FS methods needed to facilitate its malicious operations.

FoggyWeb backdoor

This malicious memory-resident DLL (originally named Microsoft.IdentityServer.WebExtension.dll by its developer) functions as a backdoor targeting AD FS. It is loaded by the main AD FS service process Microsoft.IdentityServer.ServiceHost.exe through a malicious loader component.

When loaded, the backdoor starts an HTTP listener that listens for HTTP GET and POST requests containing the following URI patterns:

  • HTTP GET URI pattern: /adfs/portalhttps://www.microsoft.com/images/theme/light01/
  • HTTP POST URI pattern: /adfs/services/trust/2005/samlmixed/upload

As shown below, the URI patterns are hardcoded in the backdoor and mimic the structure of the legitimate URIs used by the target’s AD FS deployment.

Screenshot of backdoor structure that mimic the structure of the legitimate URIs used by the target’s AD FS deployment.

Once the backdoor receives an HTTP request that contains one of the URI patterns above, the listener proceeds to handle the request using either an HTTP GET or HTTP POST callback/handler method (ProcessGetRequest() and ProcessGetRequest(), respectively).

Screenshot of code showing the listener handling the request using either an HTTP GET or HTTP POST callback/handler method

HTTP GET handler

The incoming HTTP GET requests that contain the URI pattern /adfs/portalhttps://www.microsoft.com/images/theme/light01/ are handled by backdoor’s ProcessGetRequest() method.

Screenshot of ProcessGetRequest() method

If an incoming HTTP GET request is issued for a file/resource with the file extension of .webp, the ProcessGetRequest() method proceeds to handle the request. Otherwise, the request is ignored by the backdoor. Also, if the requested file name matches one of the three hardcoded names below, the backdoor treats the request as a C2 command issued by the attacker.

Screenshot of code showing hardcoded names

The following URL patterns are treated as C2 commands:

  • /adfs/portalhttps://www.microsoft.com/images/theme/light01/profile.webp
  • /adfs/portalhttps://www.microsoft.com/images/theme/light01/background.webp
  • /adfs/portalhttps://www.microsoft.com/images/theme/light01/logo.webp

The first two C2 commands, profile.webp and background.webp (UrlGetFileNames[0] and UrlGetFileNames[1] in the screenshot above), are handled by calling the backdoor’s Service.GetCertificate() method.

Screenshot of code for Service.GetCertificate() method

As the name suggests, this method is responsible for retrieving an AD FS certificate (either the token- signing or the token encryption certificate, depending on the value of the certificateType parameter passed to the method) from the AD FS service configuration database.

Analyst note: Refer to the Appendix for an in-depth analysis of the Service.GetCertificate() method and how it obtains and decrypts either the token signing or encryption certificate.

As shown in the screenshot above, when the C2 command profile.webp (UrlGetFileNames[0]) is issued to the backdoor (by issuing an HTTP GET request for the URI /adfs/portalhttps://www.microsoft.com/images/theme/light01/profile.webp), the backdoor retrieves the token-signing certificate of the compromised AD FS server. Similarly, when the C2 command background.webp (UrlGetFileNames[1]) is issued to the backdoor (by issuing an HTTP GET request for the URI /adfs/portalhttps://www.microsoft.com/images/theme/light01/background.webp), the backdoor retrieves the token encryption certificate of the compromised AD FS server.

The third C2 command, logo.webp (UrlGetFileNames[2]), is triggered by sending an HTTP GET request to the following URI: /adfs/portalhttps://www.microsoft.com/images/theme/light01/logo.webp. The C2 command is handled by calling the backdoor’s GetInfo() method.

Screenshot of code for the backdoor’s GetInfo() method

The GetInfo() method is responsible for dumping the AD FS service configuration data of the compromised server.

Screenshot of GetInfo() method dumping the AD FS service configuration data

As shown above, the AD FS service configuration data is obtained via the ServiceSettingsData property, which retrieves the data from the AD FS service configuration database, Windows Internal Database (WID).

Before returning the output of the C2 commands (that is, the token-signing certificate, the token encryption certificate, or the AD FS service configuration data) to the C2 in an HTTP 200 response, the backdoor first obfuscates the output by calling its method named GetWebpImage().

Screenshot of code for GetWebpImage() method

The GetWebpImage() method is in charge of masquerading the output of the C2 commands as a legitimate WebP file (by adding appropriate RIFF/WebP file header magic/fields) and encoding the resulting WebP file.

Screenshot of code for GetWebpImage() method masquerading the output of the C2

GetWebpImage() uses the following helper methods to create and encode the fake WebP file that contains the C2 command output:

  • GetWebpImage() first invokes the Webp.GetFrame() method, which is responsible for compressing the output of the C2 command and copying the compressed version to a new array (0 padded to a multiple of 32 bytes). The length of the compressed data is added as the first four bytes of the new array.

Screenshot of code for GetWebpImage() first invoking the Webp.GetFrame() method

To compress the data, GetFrame() invokes the Common.Compress() method, which is used to compress the data by leveraging the C# GZipStream compression class.

Screenshot of code for GetFrame() invokes the Common.Compress() method

For demonstration purposes, assume the C2 command yields the following data (a 256-byte pseudo-randomly generated byte array).

Screenshot of a 256-byte pseudo-randomly generated byte array

Given the data above (that is, sample C2 command output), GetFrame() returns the following byte array.

Screenshot of byte array returend by GetFrame()

  • Next, GetWebpImage() invokes the Webp.GetWebpHeader() method, passing in the size of the byte array returned by GetFrame() in the step above. GetWebpHeader() is responsible  for creating and returning an array containing custom RIFF WebP file magic/header bytes.

Screenshot of code for GetWebpImage() invoking the Webp.GetWebpHeader() method

The array variable above contains the following 32-byte hardcoded RIFF/WebP header bytes.

Screenshot of 32-byte hardcoded RIFF/WebP header bytes

If the size of the array passed to GetWebpHeader() (returned by GetFrame()) exceeds 8,192 bytes, the bytes at index 26 and 28 of the header bytes (initially set to 0x00) are replaced with 0x80. Otherwise, the bytes at index 26 and 28 are replaced with 0x40, as shown below.

Screenshot showing the bytes at index 26 and 28 being replaced with 0x40

 GetWebpHeader() then returns the custom RIFF/WebP header above to GetWebpImage().

  • Next, GetWebpImage() creates a new array by appending the custom RIFF/WebP header bytes returned by GetWebpHeader() to the array returned by GetFrame() (the array containing the compressed version of the C2 command output).

Screenshot of new array creating GetWebpImage()  

GetWebpImage() calls the Common.ProtectData() method of the backdoor to encode the portion of the new array that contains the compressed bytes (that is, it does not encode the custom RIFF/WebP header). As the second argument, GetWebpImage() passes the offset of the first compressed byte to ProtectData() (as shown in the table above, 0x20 or 32 is the offset of the first compressed byte in this case). ProtectData() uses a dynamic XOR key and a custom XOR methodology to XOR encode the compressed data.

Screenshot of code for ProtectData() using a dynamic XOR key and a custom XOR methodology to XOR encode the compressed data 

Initially, the 12-byte hardcoded XOR key array contains the following (seed) bytes.

Screenshot of seed bytes

As shown in the screenshot above, each byte of compressed data is XOR’d with a byte from the XOR key array. The first byte of the compressed data (0x17) is XOR’d with the XOR key byte located at offset 8 of the key array (0x77).

Screenshot showing the first byte of the compressed data

After XOR’ing the first byte of the compressed data with the XOR key byte located at offset 8 of the key array, the XOR key byte itself gets overwritten with a new value.

Screenshot of the XOR key byte overwritten with a new value

For example, the XOR key byte located at offset 8 of the XOR key array (0x77) gets overwritten with 0xEE via the following operations.

Screenshot of XOR key byte located at offset 8 of the XOR key array (0x77) overwritten with 0xEE

The second byte of the compressed data (0x01) is XOR’d with the XOR key byte located at offset 9 of the key array (33 % 12 = 9) and so on until the key rolls to the first byte of the XOR array (as mentioned above, the XOR key bytes get overwritten after each encoding operation). Below is the XOR encoded version of the sample compressed array.

Screenshot of the second byte of the compressed data

After the steps outlined above, GetWebpImage() returns the following sample data to the method that invokes it to obfuscate and conceal the output of each C2 command (ProcessGetRequest()).

As previously mentioned, ProcessGetRequest() returns the fake RIFF/WebP file generated above (containing stolen token-signing certificate, token encryption certificate, or the AD FS service configuration data) to the C2 in an HTTP 200 response.

Screenshot of code for ProcessGetRequest() returning the fake RIFF/WebP file to the C2 in an HTTP 200 response

If the backdoor cannot execute a C2 command successfully, it returns an HTTP 404 response to the C2 instead.

HTTP POST handler

Incoming HTTP POST requests that match the URI pattern /adfs/services/trust/2005/samlmixed/upload are handled by the ProcessPostRequest() method.

Screenshot of code for the ProcessPostRequest() method

This method ensures that the ContentType value of an incoming HTTP POST request ends with “xml” (case-insensitive), and the HTTP POST data contains two XML elements named X509Certificate and SignatureValue (for example, a blob that starts with the string “<X509Certificate>” and ends with the string </X509Certificate>”).

Screenshot of ProcessPostRequest() method ensuring that the ContentType value of an incoming HTTP POST request ends with “xml”

If the XML data contains the two elements, the backdoor performs the following actions:

  • Decode the values of the SignatureValue and X509Certificate elements by first decoding the values using Base64 and then calling the Common.UprotectData() method on each decoded value.

Screenshot of code for decoding the values of the SignatureValue and X509Certificate elements

The UprotectData() method treats the first two bytes of the Base64 decoded value as a two-byte XOR key. It invokes the Common.ProtectData() method (covered in the previous section) on the rest of the data (that is, third byte on) and then uses the two-byte XOR key to XOR decode the data returned by Common.ProtectData(). In other words, UprotectData() leverages Common.ProtectData() to remove the first layer of XOR encoding and then another XOR routine to remove the second layer of XOR encoding applied to the data.

Screenshot of code for UprotectData() leveraging Common.ProtectData() to remove the first layer of XOR encoding and then another XOR routine

  • Invoke the Service.ExecuteAssembly() method to handle the decoded SignatureValue and X509Certificate values. As shown below, the decoded X509Certificate value is the first GZip decompressed/inflated by calling the Common.Decompress() method.

Screenshot of decoded X509Certificate value, the first GZip decompressed/inflated by calling the Common.Decompress() method

In a new thread, Service.ExecuteAssembly() calls Service.ExecuteAssemblyRoutine() method to handle the data.

Screenshot of Service.ExecuteAssembly() calling Service.ExecuteAssemblyRoutine() method

  • ExecuteAssemblyRoutine() checks if the decoded X509Certificate value starts with “MZ” (or the bytes 0x4D 0x5A, the hexadecimal representation of the decimal numbers 77 and 90, as seen in the screenshot below).

Screenshot showing decoded X509Certificate value that starts with “MZ” or the bytes 0x4D 0x5A, the hexadecimal representation of the decimal numbers 77 and 90

  • If the decoded X509Certificate value starts with “MZ,” the backdoor treats the decoded data as a .NET-based assembly/payload and proceeds to call its Service.ExecuteBinary() method to load and execute the DLL payload in memory. After loading the DLL in memory, ExecuteBinary() proceeds to invoke a specific method from the loaded DLL. The method name and parameters needed to invoke the method are supplied to the backdoor within the decoded SignatureValue data.

Screenshot of code for Service.ExecuteBinary() method, which loads and execute the DLL payload in memory

If the decoded X509Certificate value does not start with MZ, the backdoor treats the decoded X509Certificate value as source code for a C#-based payload and calls its Service.ExecuteSource() method to dynamically compile and execute the payload in memory.

Screenshot of code for Service.ExecuteSource() method, which dynamically compiles and executes the payload in memory

After handling the HTTP POST request containing the XML elements X509Certificate and SignatureValue, the backdoor responds to the request with an HTTP 204 response code. If the HTTP POST does not have the elements mentioned above, the backdoor responds to the request with an HTTP 404 response code.

Appendix: Obtaining and decrypting AD FS tokens

As the name suggests, the Service.GetCertificate() method is responsible for retrieving an AD FS certificate (either the token- signing or the token encryption certificate, depending on the value of the certificateType parameter passed to the method) from the AD FS service configuration database.

Screenshot of code for Service.GetCertificate() method, responsible for retrieving an AD FS certificate

The method performs the following actions to retrieve the desired certificate:

  • Invoke another one of its methods named GetServiceSettingsDataProvider() to create an instance of type Microsoft.IdentityServer.PolicyModel.Configuration.ServiceSettingsDataProvider from the already loaded assembly Microsoft.IdentityServer.

Screenshot of code for invoking the GetServiceSettingsDataProvider() method

Screenshot of code for GetServiceSettingsDataProvider() method

  • Invoke the GetServiceSettings() member/method of the above ServiceSettingsDataProvider instance to obtain the AD FS service configuration settings.

Screenshot of code for invoking the GetServiceSettings() method

  • Obtain the value of the AD FS service settings (from the SecurityTokenService property), extract the value of the EncryptedPfx blob from the service settings, and decode the blob using Base64.

Screenshot of code for extracting the value of the EncryptedPfx blob

  • Invoke another method named GetAssemblyByName() to enumerate all loaded assemblies by name and locate the already loaded assembly Microsoft.IdentityServer.Service. This method retrieves the value of two fields named _state and _certificateProtector from an object of type Microsoft.IdentityServer.Service.Configuration.AdministrationServiceState (from the Microsoft.IdentityServer.Service assembly).

The AdministrationServiceState class/object contains configuration information necessary for the execution and handling of client requests. The field _state is used to maintain the current state of the AdministrationServiceState class/object (screenshot from Microsoft.IdentityServer.Service.dll).

The AdministrationServiceState object (stored in the _state field) contains another field named _certificateProtector.

The field _certificateProtector stores an instance of the Data Protector class DkmDataProtector for Distributed Key Management (DKM). The DkmDataProtector class implements a method named Unprotect(), which ultimately calls the Unprotect() method of DKM/IDKM (screenshot from Microsoft.IdentityServer.dll).

The DKM Unprotect() method inherits a method named Unprotect() from Microsoft.IdentityServer.Dkm.DKMBase (screenshot from Microsoft.IdentityServer.Dkm.dll).

The Unprotect() method from Microsoft.IdentityServer.Dkm.DKMBase (shown above) provides the functionality to decrypt the encrypted certificate (a PKCS12 object) stored in the EncryptedPfx blob.

Armed with the knowledge about the availability of the Unprotect() method accessible via the _certificateProtector field, the backdoor invokes the Unprotect() method to decrypt the encrypted certificate stored in the EncryptedPfx blob of the desired certificate type (either the AD FS token signing or encryption certificate).

A variant of the technique described in this Appendix was publicly presented by Douglas Bienstock and Austin Baker at the TROOPERS conference in 2019 (I am AD FS and so can you: Attacking Active Directory Federated Services). However, the method used by FoggyWeb differs from the publicly presented method, in that FoggyWeb leverages the _state and _certificateProtector fields from the AdministrationServiceState class/object to facilitate access to the Data Protector class DkmDataProtector (used to gain access to and invoke the Unprotect() method).

Indicators of compromise (IOCs)

Type Threat Name Threat Type Indicator
MD5 FoggyWeb Loader 5d5a1b4fafaf0451151d552d8eeb73ec
SHA-1 FoggyWeb Loader c896ece073dd01191cbc1d462bc2f47161828a83
SHA-256 FoggyWeb Loader 231b5517b583de102cde59630c3bf938155d17037162f663874e4662af2481b1
MD5 FoggyWeb Backdoor (encrypted) 9ff9401315d0f7258a9fcde0cfdef02b
SHA-1 FoggyWeb Backdoor (encrypted) 4597431f26424cb814c917168fa8d74d01ab7cd1
SHA-256 FoggyWeb Backdoor (encrypted) da0be762bb785085d36aec80ef1697e25fb15414514768b3bcaf798dd9c9b169
MD5 FoggyWeb Backdoor (decrypted) e9671d294ce41fe6dbb9637dc0157a88
SHA-1 FoggyWeb Backdoor (decrypted) 85cfeccbb48fd9f498d24711c66e458e0a80cc90
SHA-256 FoggyWeb Backdoor (decrypted) 568392bd815de9b677788addfc4fa4b0a5847464b9208d2093a8623bbecd81e6

Mitigations

Customers should review their AD FS Server configuration and implement changes to secure these systems from attacks:

We strongly recommend for organizations to harden and secure AD FS deployments through the following best practices:

  • Ensure only Active Directory Admins and AD FS Admins have admin rights to the AD FS system.
  • Reduce local Administrators’ group membership on all AD FS servers.
  • Require all cloud admins to use multi-factor authentication (MFA).
  • Ensure minimal administration capability via agents.
  • Limit on-network access via host firewall.
  • Ensure AD FS Admins use Admin Workstations to protect their credentials.
  • Place AD FS server computer objects in a top-level OU that doesn’t also host other servers.
  • Ensure that all GPOs that apply to AD FS servers apply only to them and not to any other servers. This limits potential privilege escalation through GPO modification.
  • Ensure that the installed certificates are protected against theft. Don’t store these on a share on the network and set a calendar reminder to ensure they get renewed before expiring (expired certificate breaks federation auth). Additionally, we recommend protecting signing keys or certificates in a hardware security module (HSM) attached to AD FS.
  • Set logging to the highest level and send the AD FS (and security) logs to a SIEM to correlate with AD authentication as well as Azure AD (or similar).
  • Remove unnecessary protocols and Windows features.
  • Use a long (>25 characters) and complex password for the AD FS service account. We recommend using a Group Managed Service Account (gMSA) as the service account, as it removes the need for managing the service account password over time by managing it automatically.
  • Update to the latest AD FS version for security and logging improvements (as always, test first).
  • When federated with Azure AD follow the best practices for securing and monitoring the AD FS trust with Azure AD.

Detections

Protecting AD FS servers is key to mitigating NOBELIUM attacks. Detecting and blocking malware, attacker activity, and other malicious artifacts on AD FS servers can break critical steps in known NOBELIUM attack chains. Microsoft Defender Antivirus detects the new NOBELIUM components discussed in this blog as the following malware:

  • Loader: Trojan:Win32/FoggyWeb.A!dha
  • Backdoor: Trojan:MSIL/FoggyWeb.A!dha

Microsoft 365 Defender

Endpoint detection and response (EDR) capabilities in Microsoft Defender for Endpoint detect malicious behavior related to this malware which is surfaced as alerts with the following titles:

  • A suspicious DLL was loaded by the ADFS service
  • Suspicious service launched
  • Suspicious file dropped

Azure AD Identity Protection

This kind of attack can also be detected in the cloud using Azure AD Identity Protection. It is recommended that you monitor the Azure AD Identity Protection Risk detections report for the “Token Issuer Anomaly” detection. This detection looks for anomalies in the SAML token presented to the Azure AD tenant.

Advanced hunting queries

Microsoft Defender for Endpoint

To locate related activity, run the following advanced hunting queries in Microsoft 365 Defender:

DeviceImageLoadEvents
| where FolderPath has @"C:\Windows\ADFS"
| where FileName has @"version.dll"

Azure Sentinel

Azure Sentinel customers can use the following detection queries to look for this activity:

Detection query: https://github.com/Azure/Azure-Sentinel/tree/master/Detections/MultipleDataSources/Nobelium_FoggyWeb.yaml

Indicator file: https://github.com/Azure/Azure-Sentinel/tree/master/Sample%20Data/Feeds/FoggyWebIOC.csv

 

The post FoggyWeb: Targeted NOBELIUM malware leads to persistent backdoor appeared first on Microsoft Security Blog.

]]>
GoldMax, GoldFinder, and Sibot: Analyzing NOBELIUM’s layered persistence http://approjects.co.za/?big=en-us/security/blog/2021/03/04/goldmax-goldfinder-sibot-analyzing-nobelium-malware/ Thu, 04 Mar 2021 17:00:02 +0000 Microsoft has identified three new pieces of malware being used in late-stage activity by NOBELIUM – the actor behind the SolarWinds attacks, SUNBURST, and TEARDROP.

The post GoldMax, GoldFinder, and Sibot: Analyzing NOBELIUM’s layered persistence appeared first on Microsoft Security Blog.

]]>
April 2023 update – Microsoft Threat Intelligence has shifted to a new threat actor naming taxonomy aligned around the theme of weather. NOBELIUM is now tracked as Midnight Blizzard. April 15, 2021 update – We updated this blog with new indicators of compromise, including files, domains, and C2 decoy traffic, released by Cybersecurity & Infrastructure Security Agency (CISA) in Malware Analysis Report MAR-10327841-1.v1 – SUNSHUTTLE.   Microsoft continues to work with partners and customers to expand our knowledge of the threat actor behind the nation-state cyberattacks that compromised the supply chain of SolarWinds and impacted multiple other organizations. As we have shared previously, we have observed the threat actor using both backdoor and other malware implants to establish sustained access to affected networks. As part of our commitment to transparency and intelligence-sharing in the defender community, we continue to update analysis and investigative resources as we discover new tactics and techniques used by the threat actor.

Introducing NOBELIUM

Microsoft Threat Intelligence Center (MSTIC) is naming the actor behind the attacks against SolarWinds, the SUNBURST backdoor, TEARDROP malware, and related components as NOBELIUM. Recent investigations have identified three new pieces of malware being used in late-stage activity by NOBELIUM. This blog provides detailed analysis of these malware strains to help defenders detect, protect, and respond to this threat. We continue to partner with FireEye to understand these threats and protect our mutual customers. FireEye’s analysis of the malware used by NOBELIUM is here. Microsoft discovered these new attacker tools and capabilities in some compromised customer networks and observed them to be in use from August to September 2020. Further analysis has revealed these may have been on compromised systems as early as June 2020. These tools are new pieces of malware that are unique to this actor. They are tailor-made for specific networks and are assessed to be introduced after the actor has gained access through compromised credentials or the SolarWinds binary and after moving laterally with TEARDROP and other hands-on-keyboard actions. These capabilities differ from previously known NOBELIUM tools and attack patterns, and reiterate the actor’s sophistication. In all stages of the attack, the actor demonstrated a deep knowledge of software tools, deployments, security software and systems common in networks, and techniques frequently used by incident response teams. This knowledge is reflected in the actor’s operational decisions, from the choice of command-and-control (C2) infrastructure to the naming of scheduled tasks used to maintain persistence. With this actor’s established pattern of using unique infrastructure and tooling for each target, and the operational value of maintaining their persistence on compromised networks, it is likely that additional components will be discovered as our investigation into the actions of this threat actor continues.

New NOBELIUM malware

Maintaining persistence is critical for any threat actor after gaining access to a network. In addition to the backdoor in the SolarWinds software, NOBELIUM has been observed using stolen credentials to access cloud services like email and storage, as well as compromised identities to gain and maintain access to networks via virtual private networks (VPNs) and remote access tools. Microsoft assesses that the newly surfaced pieces of malware were used by the actor to maintain persistence and perform actions on very specific and targeted networks post-compromise, even evading initial detection during incident response.

GoldMax

The GoldMax malware was discovered persisting on networks as a scheduled task impersonating systems management software. In the instances it was encountered, the scheduled task was named after software that existed in the environment, and pointed to a subfolder in ProgramData named after that software, with a similar executable name. The executable, however, was the GoldMax implant. Written in Go, GoldMax acts as command-and-control backdoor for the actor. It uses several different techniques to obfuscate its actions and evade detection. The malware writes an encrypted configuration file to disk, where the file name and AES-256 cipher keys are unique per implant and based on environmental variables and information about the network where it is running. GoldMax establishes a secure session key with its C2 and uses that key to securely communicate with the C2, preventing non-GoldMax-initiated connections from receiving and identifying malicious traffic. The C2 can send commands to be launched for various operations, including native OS commands, via psuedo-randomly generated cookies. The hardcoded cookies are unique to each implant, appearing to be random strings but mapping to victims and operations on the actor side. Observed GoldMax C2 domains are high-reputation and high-prevalence, often acquired from domain resellers so that Whois records retain the creation date from their previous registration, or domains that may have been compromised. This tactic complements NOBELIUM’s operational security strategy as these domains are more likely to be overlooked by security products and analysts based on their perceived long-lived domain ownership. Put simply, several domains we have shared as GoldMax C2 domains are only associated with NOBELIUM after the time they were re-sold or compromised – and Microsoft has provided that indicator context where it is available to us. Upon execution, GoldMax retrieves a list of the system’s network interfaces; the malware terminates if it is unable to do so or no network interface is configured. It then attempts to determine if any of the network interfaces has the following hardcoded MAC address: c8:27:cc:c2:37:5a. If so, it terminates.

Figure 1. HardwareAddr.String() call, hardcoded MAC address, and os.Exit() call

Configuration file

GoldMax is designed to store its configuration data in an encrypted file named features.dat.tmp. The file name varies in different versions of GoldMax, but in all observed variants, the configuration file carries a .tmp file extension and is located in the same directory as GoldMax. The first time GoldMax is run, it uses a set of embedded default values to create and populate its configuration file on disk. The next time GoldMax  runs, instead of using its embedded configuration data, it loads the configuration data from its configuration file stored on the file system. The data from the configuration file typically matches the default configuration data embedded in GoldMax, since the embedded data was initially used to create the configuration file. However, GoldMax comes with a command-and-control feature that allows its operators to dynamically update its configuration data on the fly. When this happens, GoldMax overwrites the existing data in its configuration file with the new configuration data received from its operators, so the next time GoldMax is run, it uses the most up-to-date version of its configuration data to initialize its runtime settings. The configuration data is encrypted using the AES-256 encryption algorithm, CFB encryption mode, and the following cipher key: 4naehrkz5alao2jd035zjh3j1v1dvyyc (key varies in different versions of GoldMax). The AES encrypted configuration data is then Base64-encoded using the custom Base64 alphabet “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_” before it is stored in the configuration file on the file system. When run, GoldMax decodes (Base64) and decrypts (AES-256) the configuration data to reveal a custom data structure comprised of the following dynamically generated and hardcoded values (delimited by ‘|’):

Figure 2. Data structure of the GoldMax configuration data

GoldMax proceeds to parse the data structure depicted above and uses the values within to initialize its runtime settings and variables used by its different components. If the configuration file is not present on the system, (i.e., the first time it runs), GoldMax uses dynamically generated and embedded values to create and populate the data structure depicted above. It then uses the same AES encryption methodology to encrypt the data structure. After encrypting the data structure, GoldMax proceeds to Base64 encode the encrypted data structure and removes all instances of ‘=’ from the Base64 encoded string. It then creates a configuration file on the file system (e.g., features.dat.tmp) and stores the Base64 encoded data in the configuration file.

Activation date

GoldMax’s configuration data contains an execution activation/trigger date, stored as an ASCII Unix/Epoch time value as shown in the configuration data section above, that is essentially meant to function as an “activate after x date/time” feature. After loading its configuration data, GoldMax checks the current date-time value of the compromised system against the activation date from the configuration data.

Figure 3. Inline Unix() function and EPOCH comparison of the current and activation date/time

If an activation date-time value is specified in the configuration data (i.e., not set to ‘0’) and the activation date-time occurs on or before the current date-time of the compromised system, GoldMax commences its malicious activities. Otherwise, GoldMax terminates and continues to do so until the activation date is reached. If no activation date is specified in the configuration data (i.e., field set to ‘0’), the malware commences its malicious activities straightaway. In all versions of GoldMax analyzed during our investigation, the activation date is initially set to ‘0’. However, through its command-and-control feature, the operators can dynamically update the activation date using a specific C2 command, in which case the new activation date is stored in the configuration file and is checked each time GoldMax runs.

Decoy network traffic

GoldMax is equipped with a decoy network traffic generation feature that allows it to surround its malicious network traffic with seemingly benign traffic. This feature is meant to make distinguishing between malicious and benign traffic more challenging. If the decoy network traffic feature is enabled (set to ‘1’ in the configuration data), GoldMax issues a pseudo-random number of decoy HTTP GET requests (up to four) for URLs pointing to a mixture of legitimate and C2 domain names and/or IP addresses. The exact URL for each request is pseudo-randomly selected from a list of 14 hardcoded URLs. An example URL list comprised of 14 legitimate and C2 URLs is shown below:

Figure 4. Hardcoded URLs from which GoldMax selects up to four to issue HTTP requests for

As shown above, some of the decoy URLs point to the domain name of the actual C2 (e.g., onetechcompany[.]com). However, the particular HTTP resources referenced in the URLs above (e.g., style.css, script.js, icon.ico, etc.) are known to the C2 as being decoy resources that serve no role in the regular C2 communication between GoldMax and its C2. The Referer value for each decoy HTTP request is also pseudo-randomly selected from a list of four legitimate domain names. For example, we have seen the following in various combinations to make up lists of four domains: www[.]mail[.]com, www[.]bing[.]com, www[.]facebook[.]com, www[.]google[.]com, www[.]twitter[.]com, www[.]yahoo[.]com, etc. For demonstration purposes, an example decoy HTTP GET request is included below (the Connection and User-Agent HTTP headers and their values are manually added to each request by GoldMax and remain the same across all decoy HTTP requests, regardless of the destination URL):

Figure 5. Sample decoy HTTP GET request

RSA session key

The next step in the execution cycle involves establishing a secure session key between GoldMax and its C2 server. GoldMax first requests a session key from its C2 server by sending an HTTP GET request that contains a custom HTTP Cookie value that is unique to each implant. The Cookie value is comprised of the following dynamically generated and hardcoded values:

Figure 6. HTTP Cookie value in HTTP GET request

An example request containing the custom Cookie value is shown below:

Figure 7. Sample HTTP GET request with the custom Cookie value

The User-Agent and Connection values above are hardcoded in the HTTP request. The Referer value is pseudo-randomly selected from a list of four legitimate domain names using various combinations of the following: www[.]mail[.]com, www[.]bing[.]com, www[.]facebook[.]com, www[.]google[.]com, www[.]twitter[.]com, www[.]yahoo[.]com, etc. In response to the request above, GoldMax expects to receive an HTTP 200 response containing a very specific and hardcoded ASCII string (e.g., “uFLa12nFmKkjrmjj”). The seemingly random-looking string is typically 10-16 bytes long (after all leading and trailing white space has been removed). It can best be described as a “shared secret” between the C2 and each individual implant (the string varies in different versions of GoldMax). It serves as an acknowledgement that the C2 server has received GoldMax’s request for a new a session key. If GoldMax does not receive the expected string, it sleeps for a random amount of time and repeats (indefinitely) the process described above to obtain the expected string from its C2 server, or until the GoldMax process is terminated. After receiving the expected string, GoldMax sleeps for up to 14 seconds before proceeding. If the decoy traffic option is enabled in the configuration data, GoldMax issues a pseudo-random number of HTTP GET requests (as described under the decoy network traffic section above). GoldMax then issues a new HTTP GET request to its C2 server containing a new set of hardcoded Cookie values.

Figure 8. Sample HTTP GET request showing hardcoded Cookie values

The only observed difference between the first and second HTTP GET requests is the value of the second Cookie highlighted above (example values: iC0Pf2a48 from the first request vs. J4yeUYKyeuNa2 from the second request above). In response to the request, GoldMax receives an encrypted RSA session key (Base64-encoded). Each version of GoldMax contains an RSA private key which GoldMax proceeds to decode (using pem.Decode()) and parse (using x509.ParsePKCS1PrivateKey()). GoldMax uses rsa.DecryptOAEP() with the parsed private key to decrypt (using RSA-OAEP) the RSA-encrypted session key received from its C2 server. From this point on, the session key is used to encrypt data sent between GoldMax and its C2 server.

C2 commands

After establishing a session key, GoldMax reaches out to its C2 server to receive, decrypt (AES-256), parse, and execute commands. To retrieve an encrypted C2 command from its C2 server, GoldMax sends an HTTP GET request. This HTTP GET request only contains a single Cookie value, which matches the Cookie value used during the session key establishment process (the User-Agent and Connection headers and values are hardcoded, as before):

Figure 9. Sample HTTP GET request containing a single Cookie value

In response to the request above, GoldMax receives an encrypted (AES-256) and encoded (Base64 using custom Base64 alphabet) C2 command. The command is encrypted using the session key established between GoldMax and its C2 server. After decoding and decrypting the C2 command, GoldMax proceeds to parse the C2 command. C2 commands are represented as seemingly random alphanumerical ASCII strings (e.g., “KbwUQrcooAntqNMddu4XRj”) that are unique to each implant but known to the C2 server. The C2 commands allow the operator to download and execute files on the compromised system, upload files from the compromised system to the C2 server, execute OS commands on the compromised system, spawn a command shell, and dynamically update GoldMax’s configuration data. These dynamic updates to Goldmax configuration data enable ability to set a new activation date, replace the existing C2 URL and User-Agent values, enable/disable decoy network traffic feature, and update the number range used by its PRNG. It is worth noting that all observed versions of GoldMax were compiled with the Go compiler version 1.14.2 (released in April 2020). In all observed versions, the main Go source code file for GoldMax was located under the following directory: /var/www/html/builds/. The Go packages and libraries used during the compilation process of GoldMax were mostly located under the /var/www/html/go/src/ directory (e.g., /var/www/html/go/src/net/http/http.go).

Sibot

Sibot is a dual-purpose malware implemented in VBScript. It is designed to achieve persistence on the infected machine then download and execute a payload from a remote C2 server.  The VBScript file is given a name that impersonates legitimate Windows tasks and is either stored in the registry of the compromised system or in an obfuscated format on disk. The VBScript is then run via a scheduled task. Sibot reaches out to a legitimate but compromised website to download a DLL to a folder under System32. In observed instances the DLL is downloaded to C:\windows\system32\drivers\, renamed with a .sys extension, and then executed by rundll32. The scheduled task calls an MSHTA application to run Sibot via the obfuscated script. This simplistic implementation allows for a low footprint for the actor, as they can download and run new code without changes to the compromised endpoint by just updating the hosted DLL. The compromised website used to host the DLL is different for every compromised network and includes websites of medical device manufacturers and IT service providers. We have observed three variants of this malware, all of which are obfuscated:
  • Variant A is the simplest of the three. It only installs the second-stage script in the default registry value under the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\sibot.
  •  Variant B registers a scheduled task named Sibot and programmed to run daily. This task, which is saved by Windows in the file C:\Windows\System32\Tasks\Microsoft\Windows\WindowsUpdate\sibot, runs the following command-line daily:

The registry key referenced in this command-line contains the second-stage script.

  • Variant C is a standalone version of the second-stage script. However, while the second-stage script from Variant A is designed to be executed from the registry, this variant is designed to run from a file.

Figure 10. Sibot variants

The second-stage script

The purpose of the second-stage script is to download and run a payload from a remote server. The script can be customized with the following parameters:
  • Command-line with which to run the payload
  • Directory where the payload is installed
  • URL of the C2 server containing the payload to download
  • HTTP request to use for the download (e.g., GET)
When run, the first thing the script does is to retrieve a GUID associated to a LAN connection present on the machine by leveraging the interface offered by the WMI Class Root\Microsoft\Homenet\HNet_Connection. If a LAN connection is not available, the script defaults to a hardcoded GUID. This GUID is later communicated to the C2. It’s possible that the threat actor used this GUID to verify that the threat is running in a desirable environment, i.e., a real machine with LAN connections available. The next step of the second-stage script is to check if the machine is configured to use proxies, and if so, to get the address of a proxy. The script uses the StdRegProv WMI class to read the configuration data from the registry key  HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyServer and extract a valid proxy server. At this point, the script establishes an HTTP connection to the C2 server. It sets the user-agent and the connection GUID as HTTP header variables, then sends the HTTP request. In both versions of the script, the request is GET. If the server response is comprised only of the same GUID that the malware sent, the script deletes itself. In the case of the second-stage script from Variant A, the script deletes the registry key where it is installed. In the case of Variant C, the script deletes the file from which it is running. If instead the server responds with any data other than the GUID, the second-stage script decrypts the data and saves it as a file. In both variants of the second-stage script, the payload is a DLL with a .SYS extension and saved in the %windir%\system32\drivers folder. Finally, the script uses the Win32_Process WMI class to execute the payload DLL via the rundll32.exe utility. While the script is running in the context of a script host process (e.g. wscript.exe), the actions carried out through the WMI interface originates from the WMI host process (WmiPrvSe.exe). This effectively breaks the process chain between the action’s origin (the script host) and its execution (the WMI host), making it more difficult to trace back events to their true origin. Forensic analysis is also hindered by the lack of correlation between the execution of the second-stage script and the events it carries out via WMI. The following Python script can be used to decode encoded strings observed in Sibot samples analyzed in this report.
Python
encoded = '<ENCODED STRING'
decoded = ''

i = 0
while i < len(encoded):
a = int(chr(ord(encoded[i]) - 17))
i += 1
b = int(chr(ord(encoded[i]) - 17))
if a * 10 + b < 32:
i += 1
c = int(chr(ord(encoded[i]) - 17))
decoded += chr(a * 100 + b * 10 + c)

else:
decoded += chr(a * 10 + b)
i += 1

print(decoded)

GoldFinder

Another tool written in Go, GoldFinder was most likely used as a custom HTTP tracer tool that logs the route or hops that a packet takes to reach a hardcoded C2 server. When launched, the malware issues an HTTP request for a hardcoded IP address (e.g., hxxps://185[.]225[.]69[.]69/) and logs the HTTP response to a plaintext log file (e.g., loglog.txt created in the present working directory). GoldFinder uses the following hardcoded labels to store the request and response information in the log file:
  • Target: The C2 URL
  • StatusCode: HTTP response/status code
  • Headers: HTTP response headers and their values
  • Data: Data from the HTTP response received from the C2
An example log entry using a sample date is shown below:

Figure 11. Sample log entry

If the response is not an HTTP 200 (OK) response and contains an HTTP Location field (indicating a redirect), GoldFinder recursively follows and logs the redirects until it receives an HTTP 200 response, at which point it terminates. If a Location header is present in the response and the Location value starts with the string “http”, GoldFinder extracts the Location URL (i.e., redirect URL) and issues a new HTTP GET request for the redirect URL. It again logs the request and its response in the plaintext log file:

Figure 12. Sample log file

If GoldFinder receives an HTTP 200 status code in response to the request above, indicating no more redirects, it terminates. Otherwise, it recursively follows the redirect up to 99 times or until it receives an HTTP 200 response, whichever occurs first. When launched, GoldFinder can identify all HTTP proxy servers and other redirectors such as network security devices that an HTTP request travels through inside and outside the network to reach the intended C2 server. When used on a compromised device, GoldFinder can be used to inform the actor of potential points of discovery or logging of their other actions, such as C2 communication with GoldMax. GoldFinder was compiled using Go 1.14.2, released in April 2020, from a Go file named finder.go with the following path: /tmp/finder.go. The Go packages and libraries used during the compilation process of GoldFinder were mostly located under the /var/www/html/go/src/ directory (e.g., /var/www/html/go/src/net/http/http.go).

Comprehensive protections for persistent techniques

The sophisticated NOBELIUM attack requires a comprehensive incident response to identify, investigate, and respond. Get the latest information and guidance from Microsoft at https://aka.ms/nobelium. Microsoft Defender Antivirus detects the new NOBELIUM components discussed in this blog as the following malware:
  • Trojan:Win64/GoldMax.A!dha
  • TrojanDownloader:VBS/Sibot.A!dha
  • Trojan:VBS/Sibot.B!dha
  • Trojan:Win64/GoldFinder.A!dha
  • Behavior:Win32/Sibot.C
Turning on cloud-delivered protection and automatic sample submission on Microsoft Defender Antivirus ensures that artificial intelligence and machine learning can quickly identify and stop new and unknown threats. Tamper protection features prevent attackers from stopping security services. Attack surface reduction rules, specifically the rule Block executable files from running unless they meet a prevalence, age, or trusted list criterion, can help block new malware and attacker tools introduced by threat actors.

 

Figure 13. Security recommendations in threat and vulnerability management

Detections of new malware by Microsoft Defender Antivirus are reported as alerts in Microsoft Defender Security Center. Additionally, endpoint detection and response capabilities in Microsoft Defender for Endpoint detect malicious behavior related to these NOBELIUM components, which are surfaced as alerts with the following titles:
  • GoldMax malware
  • Sibot malware
  • GoldFinder Malware
The following alerts, which indicate detection of behavior associated with a wide range of attacks, are also raised for these NOBELIUM components:
  • Suspicious connection to remote service
  • Suspicious Rundll32 Process Execution
  • Suspicious PowerShell command line
  • Suspicious file or script accessed a malicious registry key
Intelligence about these newly surfaced components accrue to the information about NOBELIUM that Microsoft 365 Defender consolidates. Rich investigation tools in Microsoft 365 Defender allow security operations teams to comprehensively respond to this attack. Get comprehensive guidance for using Microsoft 365 Defender to identify, investigate, and respond to the NOBELIUM attack.

Indicators of compromise (IOCs)

Due to the nature of this attack, most samples are unique to each network they were discovered in, however Microsoft has confirmed that these samples available in public repositories are associated with this threat.
Type Threat name Indicator
SHA-256 GoldMax 70d93035b0693b0e4ef65eb7f8529e6385d698759cc5b8666a394b2136cc06eb
SHA-256 GoldMax 0e1f9d4d0884c68ec25dec355140ea1bab434f5ea0f86f2aade34178ff3a7d91
SHA-256 GoldMax 247a733048b6d5361162957f53910ad6653cdef128eb5c87c46f14e7e3e46983
SHA-256 GoldMax f28491b367375f01fb9337ffc137225f4f232df4e074775dd2cc7e667394651c
SHA-256 GoldMax 611458206837560511cb007ab5eeb57047025c2edc0643184561a6bf451e8c2c
SHA-256 GoldMax b9a2c986b6ad1eb4cfb0303baede906936fe96396f3cf490b0984a4798d741d8
SHA-256 GoldMax bbd16685917b9b35c7480d5711193c1cd0e4e7ccb0f2bf1fd584c0aebca5ae4c
SHA-256 GoldFinder 0affab34d950321e3031864ec2b6c00e4edafb54f4b327717cb5b042c38a33c9
SHA-256 Sibot 7e05ff08e32a64da75ec48b5e738181afb3e24a9f1da7f5514c5a11bb067cbfb
SHA-256 Sibot acc74c920d19ea0a5e6007f929ef30b079eb2836b5b28e5ffcc20e68fa707e66
IP address GoldMax and GoldFinder 185[.]225[.]69[.]69/
Domain GoldMax and GoldFinder srfnetwork[.]org
Domain GoldMax reyweb[.]com
Domain GoldMax onetechcompany [.]com

Additional IOCs added April 15, 2021:

Type Threat name Indicator
SHA-256 GoldMax 4e8f24fb50a08c12636f3d50c94772f355d5229e58110cccb3b4835cb2371aec
SHA-256 GoldMax ec5f07c169267dec875fdd135c1d97186b494a6f1214fb6b40036fd4ce725def
SHA-256 GoldMax 478b04c20bbf6717d10ee978b99339b7c4664febc8bcfdaf86c3f0fbfc83a5c5
SHA-256 GoldFinder f2a8bdf135caca0d7359a7163a4343701a5bdfbc8007e71424649e45901ab7e2
SHA-256 GoldFinder 4dec3eeefcec013f142386d5c54099d3daa2b48d559434db1d4f2078d704da1b
SHA-256 GoldFinder 6b01eeef147d9e0cd6445f90e55e467b930df2de5d74e3d2f7610e80f2c5a2cd
SHA-256 GoldFinder 0f04f199327d0d076815190dc024f4a6b0f27899d50d28e94662820ab9c945d2
SHA-256 Sibot e9ddf486e5aeac02fc279659b72a1bec97103f413e089d8fabc30175f4cdbf15
SHA-256 Sibot cb80a074e5fde8d297c2c74a0377e612b4030cc756baf4fff3cc2452ebc04a9c
Domain GoldMax megatoolkit[.]com
Domain GoldMax and GoldFinder Nikeoutletinc[.]org

GoldMax C2 decoy traffic

As detailed above, GoldMax employs decoy traffic to blend in with normal network traffic. Below are several examples demonstrating the patterns GoldMax uses to mix legitimate traffic with C2 queries:
185[.]225[.]69[.]69 C2 decoys “onetechcompany” C2 decoys “reyweb” C2 decoys
hxxps[:]//cdn[.]mxpnl[.]com/ hxxps[:]//code[.]jquery[.]com/ hxxps[:]//code[.]jquery[.]com/
hxxps[:]//code[.]jquery[.]com/ hxxps[:]//play[.]google[.]com/log?” hxxps[:]//cdn[.]cloudflare[.]com/
hxxps[:]//cdn[.]google[.]com/ hxxps[:]//fonts[.]gstatic[.]com/s/font.woff2″ hxxps[:]//cdn[.]google[.]com/
hxxps[:]//fonts[.]gstatic[.]com/s/font.woff2 hxxps[:]//cdn[.]google[.]com/ hxxps[:]//cdn[.]jquery[.]com/
hxxps[:]//ssl[.]gstatic[.]com/ui/v3/icons hxxps[:]//www.gstatic[.]comhttps://www.microsoft.com/images/? hxxps[:]//cdn[.]mxpnl[.]com/
hxxps[:]//www.gstatic[.]comhttps://www.microsoft.com/images/? hxxps[:]//onetechcompany [.]com/style.css hxxps[:]//ssl[.]gstatic[.]com/ui/v3/icons
hxxps[:]//185[.]225[.]69[.]69/style.css hxxps[:]//onetechcompany [.]com/script.js hxxps[:]//reyweb[.]com/style.css
hxxps[:]//185[.]225[.]69[.]69/script.js hxxps[:]//onetechcompany [.]com/icon.ico hxxps[:]//reyweb[.]com/script.js
hxxps[:]//185[.]225[.]69[.]69/icon.ico hxxps[:]//onetechcompany [.]com/icon.png hxxps[:]//reyweb[.]com/icon.ico
hxxps[:]//185[.]225[.]69[.]69/icon.png hxxps[:]//onetechcompany [.]com/scripts/jquery.js hxxps[:]//reyweb[.]com/icon.png
hxxps[:]//185[.]225[.]69[.]69/scripts/jquery.js hxxps[:]//onetechcompany [.]com/scripts/bootstrap.js hxxps[:]//reyweb[.]com/scripts/jquery.js
hxxps[:]//185[.]225[.]69[.]69/scripts/bootstrap.js hxxps[:]//onetechcompany [.]com/css/style.css hxxps[:]//reyweb[.]com/scripts/bootstrap.js
hxxps[:]//185[.]225[.]69[.]69/css/style.css hxxps[:]//onetechcompany [.]com/css/bootstrap.css hxxps[:]//reyweb[.]com/css/style.css
hxxps[:]//185[.]225[.]69[.]69/css/bootstrap.css hxxps[:]//reyweb[.]com/css/bootstrap.css

C2 decoy traffic added April 15, 2021:

“megatoolkit” C2 decoys “nikeoutletinc” C2 decoys
https://cdn.mxpnl.com/ https://cdn.mxpnl.com/
https://www.sdfsd/ https://cdn.bootstrap.com/
https://cdn.jquery.com/ https://www.sdfsd/
https://cdn.cloudflare.com/ https://play.google.com/log?
https://code.jquery.com/ https://cdn.jquery.com/
https://play.google.com/log? https://code.jquery.com/
https://megatoolkit.com/style.css https://nikeoutletinc.org/style.css
https://megatoolkit.com/script.js https://nikeoutletinc.org/script.js
https://megatoolkit.com/icon.ico https://nikeoutletinc.org/icon.ico
https://megatoolkit.com/icon.png https://nikeoutletinc.org/icon.png
https://megatoolkit.com/scripts/jquery.js https://nikeoutletinc.org/scripts/jquery.js
https://megatoolkit.com/scripts/bootstrap.js https://nikeoutletinc.org/scripts/bootstrap.js
https://megatoolkit.com/css/style.css https://nikeoutletinc.org/css/style.css
https://megatoolkit.com/css/bootstrap.css https://nikeoutletinc.org/css/bootstrap.css

Advanced hunting queries

Rundll32.exe .sys image loads by reference

Looks for rundll32.exe loading .sys file explicitly by name. Run query in Microsoft 365 security center:

DeviceImageLoadEvents | where InitiatingProcessFileName =~ 'rundll32.exe' | where InitiatingProcessCommandLine has_any('.sys,','.sys ') | where FileName endswith '.sys' | project Timestamp, DeviceId, InitiatingProcessParentFileName, InitiatingProcessFolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath, FileName

Rundll32.exe executing inline VBScript

Looks for rundll32.exe executing specific inline VBScript commands. Run query in Microsoft 365 security center:

DeviceProcessEvents | where FileName =~ 'rundll32.exe' | where ProcessCommandLine has 'Execute' and ProcessCommandLine has 'RegRead' and ProcessCommandLine has 'window.close' | project Timestamp, DeviceId, InitiatingProcessParentFileName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine

Run query in Azure Sentinel (Github link):

SecurityEvent | where EventID == 4688 | where Process =~ 'rundll32.exe' | where CommandLine has_all ('Execute','RegRead','window.close') | project TimeGenerated, Computer, Account, Process, NewProcessName, CommandLine, ParentProcessName, _ResourceId

VBScript payload stored in registry

Looks for VBScript payload stored in registry, specifically stored within a sub-key of CurrentVersion registry path and excluding common AutoRun persistence locations like Run and RunOnce registry keys. Run query in Microsoft 365 security center

DeviceRegistryEvents | where RegistryKey endswith @'\Microsoft\Windows\CurrentVersion' | where RegistryValueType == 'String' | where strlen(RegistryValueData) >= 200 | where RegistryValueData has_any('vbscript','jscript','mshtml,','mshtml ','RunHTMLApplication','Execute(','CreateObject','RegRead','window.close') | where RegistryKey !endswith @'\Software\Microsoft\Windows\CurrentVersion\Run' and RegistryKey !endswith @'\Software\Microsoft\Windows\CurrentVersion\RunOnce' | project Timestamp, DeviceId, InitiatingProcessFileName, InitiatingProcessCommandLine, RegistryKey, RegistryValueName, RegistryValueData

Run query in Azure Sentinel (Github link):

let cmdTokens0 = dynamic(['vbscript','jscript']); let cmdTokens1 = dynamic(['mshtml','RunHTMLApplication']); let cmdTokens2 = dynamic(['Execute','CreateObject','RegRead','window.close']); SecurityEvent | where TimeGenerated >= ago(14d) | where EventID == 4688 | where CommandLine has @'\Microsoft\Windows\CurrentVersion' | where not(CommandLine has_any (@'\Software\Microsoft\Windows\CurrentVersion\Run', @'\Software\Microsoft\Windows\CurrentVersion\RunOnce')) // If you are receiving false positives, then it may help to make the query more strict by uncommenting the lines below to refine the matches //| where CommandLine has_any (cmdTokens0) //| where CommandLine has_all (cmdTokens1) | where CommandLine has_all (cmdTokens2) | project TimeGenerated, Computer, Account, Process, NewProcessName, CommandLine, ParentProcessName, _ResourceId

Domain IOC lookup

Looks for identified C2 domains. Run query in Azure Sentinel (GitHub link)

let DomainNames = dynamic(['onetechcompany.com', 'reyweb.com', 'srfnetwork.org']); let IPList = dynamic(['185.225.69.69']); let IPRegex = '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}'; (union isfuzzy=true (CommonSecurityLog | where SourceIP in (IPList) or DestinationIP in (IPList) or DestinationHostName in~ (DomainNames) or RequestURL has_any (DomainNames) or Message has_any (IPList) | parse Message with * '(' DNSName ')' * | extend MessageIP = extract(IPRegex, 0, Message) | extend IPMatch = case(SourceIP in (IPList), "SourceIP", DestinationIP in (IPList), "DestinationIP", MessageIP in (IPList), "Message", RequestURL in (DomainNames), "RequestUrl", "NoMatch") | extend timestamp = TimeGenerated, IPCustomEntity = case(IPMatch == "SourceIP", SourceIP, IPMatch == "DestinationIP", DestinationIP, IPMatch == "Message", MessageIP, "NoMatch"), AccountCustomEntity = SourceUserID ), (DnsEvents | where IPAddresses in (IPList) or Name in~ (DomainNames) | extend DestinationIPAddress = IPAddresses, DNSName = Name, Host = Computer | extend timestamp = TimeGenerated, IPCustomEntity = DestinationIPAddress, HostCustomEntity = Host ), (VMConnection | where SourceIp in (IPList) or DestinationIp in (IPList) or RemoteDnsCanonicalNames has_any (DomainNames) | parse RemoteDnsCanonicalNames with * '["' DNSName '"]' * | extend IPMatch = case( SourceIp in (IPList), "SourceIP", DestinationIp in (IPList), "DestinationIP", "None") | extend timestamp = TimeGenerated, IPCustomEntity = case(IPMatch == "SourceIP", SourceIp, IPMatch == "DestinationIP", DestinationIp, "NoMatch"), HostCustomEntity = Computer ), (OfficeActivity | where ClientIP in (IPList) | extend timestamp = TimeGenerated, IPCustomEntity = ClientIP, AccountCustomEntity = UserId ), (DeviceNetworkEvents | where RemoteUrl has_any (DomainNames) or RemoteIP in (IPList) | extend timestamp = TimeGenerated, DNSName = RemoteUrl, IPCustomEntity = RemoteIP, HostCustomEntity = DeviceName ), (AzureDiagnostics | where ResourceType == "AZUREFIREWALLS" | where Category == "AzureFirewallDnsProxy" | parse msg_s with "DNS Request: " ClientIP ":" ClientPort " - " QueryID " " Request_Type " " Request_Class " " Request_Name ". " Request_Protocol " " Request_Size " " EDNSO_DO " " EDNS0_Buffersize " " Responce_Code " " Responce_Flags " " Responce_Size " " Response_Duration | where Request_Name has_any (DomainNames) | extend timestamp = TimeGenerated, DNSName = Request_Name, IPCustomEntity = ClientIP ), (AzureDiagnostics | where ResourceType == "AZUREFIREWALLS" | where Category == "AzureFirewallApplicationRule" | parse msg_s with Protocol 'request from ' SourceHost ':' SourcePort 'to ' DestinationHost ':' DestinationPort '. Action:' Action | where isnotempty(DestinationHost) | where DestinationHost has_any (DomainNames) | extend timestamp = TimeGenerated, DNSName = DestinationHost, IPCustomEntity = SourceHost ) )

The post GoldMax, GoldFinder, and Sibot: Analyzing NOBELIUM’s layered persistence appeared first on Microsoft Security Blog.

]]>