This article is contributed. See the original author and article here.

Microsoft recently blogged about the Recent Nation-State Cyber Attacks that has impacted high value targets both across the government and private sector. This attack is also known as Solorigate or Sunburst. This threat actor is believed to be highly sophisticated and motivated. Relevant security data required for hunting and investigating such a complex attack is produced in multiple locations – cloud, on-premises and across multiple security tools and product logs.  Being able to analyze all the data from a single point makes it easier to spot trends and attacks. Azure Sentinel has made it easy to collect data from multiple data sources across different environments both on-prem and cloud with the goal of connecting that data together more easily. This blog post contains guidance and generic approaches to hunt for attacker activity (TTPs) in data that is available by default in Azure Sentinel or can be onboarded to Azure Sentinel.


 


The goal of this article is post-compromise investigation strategies and is focused on TTPs and not focused on specific IOCs.  Azure Sentinel customers are encouraged to review advisories and IOC’s shared by Microsoft MSRC and security partners to search on specific IOC’s in their environment using Azure Sentinel.  Links to these IOC’s are listed in the reference section at the end.


 


To make it easier for security teams to visualize and monitor their environments for this attack the MSTIC team has shared a SolarWinds Post Compromise hunting workbook via Azure Sentinel and Azure Sentinel GitHub. There are many things in this workbook that threat hunters would find useful and the workbook is complimentary to the hunting methods shared below. Importantly, if you have recently rotated ADFS key material this workbook can be useful in identifying attacker logon activity if they logon with old key material. Security teams should leverage this hunting workbook as part of their workflow in investigating this attack.


 


Thanks to the MSTIC and M365 teams for collaborating to deliver this content in a timely manner.  This document will be updated as needed.


 


Please note that since Azure Sentinel and the M365 Advanced Hunting portal share the same query language and share similar data types, all of the referenced queries can be used directly or slightly modified to work in both.


 


Gaining a foothold


As shared in Microsoft’s technical blog – Customer Guidance on Recent Nation-state Cyber Attacks – attackers might have compromised the internal build systems or the update distribution systems of SolarWinds Orion software then modified a DLL component in the legitimate software and embedded backdoor code that would allow these attackers to remotely perform commands or deliver additional payloads. Below is a representation of various attack stages which you can also see in Microsoft Threat Protection (MTP) portal.


 


Image1.png



To hunt for similar TTPs used in this attack, a good place to start is to build an inventory of the machines that have SolarWinds Orion components. Organizations might already have a software inventory management system to indicate hosts where the SolarWinds application is installed. Alternatively, Azure Sentinel could be leveraged to run a simple query to gather similar details. Azure Sentinel collects data from multiple different logs that could be used to gather this information. For example, through the recently released Microsoft 365 Defender connector, security teams can now easily ingest Microsoft 365 raw data into Azure Sentinel. Using the ingested data, a simple query like below can be written that will pull the hosts with SolarWinds process running in last 30 days based on Process execution either via host on boarded to Sentinel or on boarded via Microsoft Defender for Endpoints (MDE). The query also leverages the Sysmon logs that a lot of customers are collecting from their environment to surface the machines that have SolarWinds running on them. Similar queries that leverage M365 raw data could also be run from the M365’s Advanced hunting portal.


 


SolarWindows Inventory check query


 

Spoiler (Highlight to read)


let timeframe = 30d; 

(union isfuzzy=true 

( 

SecurityEvent 

where TimeGenerated >= ago(timeframe) 

where EventID == ‘4688’ 

where tolower(NewProcessName) has ‘solarwinds’ 

extend MachineName = Computer , Process = NewProcessName

summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), MachineCount = dcount(MachineName), AccountCount = dcount(Account), MachineNames = make_set(MachineName), Accounts = make_set(Account) by Process, Type

), 

( 

DeviceProcessEvents 

where TimeGenerated >= ago(timeframe) 

where tolower(InitiatingProcessFolderPath) has ‘solarwinds’ 

extend MachineName = DeviceName , Process = InitiatingProcessFolderPath, Account = AccountName

summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), MachineCount = dcount(MachineName), AccountCount = dcount(Account), MachineNames = make_set(MachineName), Accounts = make_set(Account)  by Process, Type

), 

( 

Event 

where TimeGenerated >= ago(timeframe) 

where Source == “Microsoft-Windows-Sysmon” 

where EventID == 1 

extend Image = EventDetail.[4].[“#text”] 

where tolower(Image) has ‘solarwinds’ 

extend MachineName = Computer , Process = Image, Account = UserName

summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), MachineCount = dcount(MachineName), AccountCount = dcount(Account), MachineNames = make_set(MachineName), Accounts = make_set(Account)  by Process, Type

) 

) 

let timeframe = 30d; 
(union isfuzzy=true 

SecurityEvent 
| where TimeGenerated >= ago(timeframe) 
| where EventID == ‘4688’ 
| where tolower(NewProcessName) has ‘solarwinds’ 
| extend MachineName = Computer , Process = NewProcessName
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), MachineCount = dcount(MachineName), AccountCount = dcount(Account), MachineNames = make_set(MachineName), Accounts = make_set(Account) by Process, Type
), 

DeviceProcessEvents 
| where TimeGenerated >= ago(timeframe) 
| where tolower(InitiatingProcessFolderPath) has ‘solarwinds’ 
| extend MachineName = DeviceName , Process = InitiatingProcessFolderPath, Account = AccountName
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), MachineCount = dcount(MachineName), AccountCount = dcount(Account), MachineNames = make_set(MachineName), Accounts = make_set(Account)  by Process, Type
), 

Event 
| where TimeGenerated >= ago(timeframe) 
| where Source == “Microsoft-Windows-Sysmon” 
| where EventID == 1 
| extend Image = EventDetail.[4].[“#text”] 
| where tolower(Image) has ‘solarwinds’ 
| extend MachineName = Computer , Process = Image, Account = UserName
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), MachineCount = dcount(MachineName), AccountCount = dcount(Account), MachineNames = make_set(MachineName), Accounts = make_set(Account)  by Process, Type

Privilege Escalation


Once the adversary acquires an initial foothold on a system thru the SolarWinds process they will have System account level access, the attacker will then attempt to elevate to domain admin level access to the environment. The Microsoft Threat Intelligence Center (MSTIC) team has already delivered multiple queries into Azure Sentinel that identify similar TTPs and many are also available in M365. These methodologies are not specific to just this threat actor or this attack but have been seen in various attack campaigns.


Identifying abnormal logon activities or additions to privileged groups is one way to identify privilege escalation.



Related to this attack, in some environments service account credentials had been granted administrative privileges. The above queries can be modified to remove the condition of focusing “User” accounts by commenting the query to include service accounts in the scope where applicable:


 


//| where AccountType == “User”


 


Please see the Azure Sentinel Github for additional queries and hunting ideas related to Accounts under the Detections and Hunting Queries sections for AuditLogs, and SecurityEvents


Microsoft 365 Defender team has also shared quite a few sample queries for use in their  advanced hunting portal that could be leveraged to detect this part of the attack. Additionally, the logic for many of the Azure Sentinel queries can also be transformed to equivalent queries for Microsoft 365 Defender, that could be run in their Advanced Hunting Portal.


Microsoft 365 Defender has an upcoming complimentary blog that will be updated here once available.


 


Certificate Export


The next step in the attack was stealing the certificate that signs SAML tokens from the federation server (ADFS) called a Token Signing Cert (TSC). SAML Tokens are basically XML representations of claims.  You can read more about ADFS in What is federation with Azure AD? | Microsoft Docs and SAML at Azure Single Sign On SAML Protocol – Microsoft identity platform | Microsoft Docs. The process is as follows:



  1. A client requests a SAML token from an ADFS Server by authenticating to that server using Windows credentials.

  2. The ADFS server issues a SAML token to the client.

  3. The SAML token is signed with a certificate associated with the server.

  4. The client then presents the SAML token to the application that it needs access to.

  5. The signature over the SAML token tells the application that the security token service issued the token and grants access to the client.


The implication of stealing the TSC is that once the certificate has been acquired, the actor can forge SAML tokens with whatever claims and lifetime they choose, then sign it with the certificate that has been acquired. This enables the actor to forge SAML tokens that impersonate highly privileged accounts. There are many publicly available pen-testing tools like ADFSDump and ADFSpoof that help with extracting required information from ADFS configuration database to generate the forged security tokens.  Microsoft’s M365 Defender team has created several high-fidelity detections related to this. A few of them include:



  • Possible attempt to access ADFS key material which detects when a suspicious LDAP query is searching for sensitive key material in AD.

  • ADFS private key extraction which detects ADFS private key extraction patterns from tools such as ADFSDump.


Note: These detections can be seen in Azure Sentinel Security Alerts or in the M365 security portal.


 


Outside of directly looking for tools, this adversary may have used custom tooling so looking for anomalous process executions or anomalous accounts logging on to our ADFS server can give us some clue when such attacks happen. Azure Sentinel provides queries that can help to:



Every environment is different and some of these queries being generic could be noisy. So, in the first step a good approach would be to limit this kind of hunting to our ADFS server.


 


Azure Active Directory Hunting


Having gained a significant foothold in the on-prem environment, the actor also targeted the Azure AD of the compromised organizations and made modifications to Azure AD settings to facilitate long term access. The MSTIC team at Microsoft has shared many relevant queries through the Azure Sentinel Github to identify these actions.


One such activity is related to modifying domain federation trust settings. In layperson terms, a federation trust signifies an agreement between two organizations so that users located in partner organization can send authentication requests successfully.



  • The Azure Sentinel query for domain federation trust settings modification will alert when a user or application modifies the federation settings on the domain particularly when a new Active Directory Federated Service (ADFS) Trusted Realm object, such as a signing certificate, is added to the domain. Modification to domain federation settings should be rare hence this would be a high-fidelity alert that Azure Sentinel users should pay attention to.

  • If a threat actor obtains access to an Application Administrator account, they may configure alternate authentication mechanisms for direct access to any of the scopes and services available to the Service Principal. With these privileges, the actor can add alternative authentication material for direct access to resources using this credential. The team has produced queries that look for addition of new key credentials to an Application or Service Principal.

  • While OAuth applications in this intrusion campaign are being used for persistent access via attacker-added credentials, while reviewing OAuth applications it may be valuable to look for evidence of previously unknown OAuth permissions, such as those that can read sensitive data and provide offline access:


Suspicious application consent similar to O365 Attack Toolkit


Suspicious application consent similar to PwnAuth


Suspicious application consent for offline access


 


In addition, consider hunting for new key credentials being added to Service Principals and Azure AD applications. Attackers have been observed adding new key credentials and then using these to access sensitive data within an environment. MSTIC has already published detections to help identify such activity, and this has also been incorporated into the hunting workbook.


 


Data Access


Accessing confidential data is one of the primary motives of this attack. Data access for the attacker here relied on leveraging minted SAML tokens to access user files/email stored in the cloud via compromised AppIds. One way to detect this is when a user or application signs in using Azure Active Directory PowerShell to access non-Active Directory resources.


 


Microsoft Graph is one way that the attacker may be seen accessing resources and can help find what the attacker may have accessed using the Service principal Azure Active Directory sign-in logs. If you have data in your Log analytics you could easily plot a chart to see what anomalous activity is happening in your environment that is leveraging the graph. Note that the data type in Azure Sentinel below is only available when XXX is configured.


 

Spoiler (Highlight to read)

AADServicePrincipalSignInLogs
| where TimeGenerated > ago(90d)
| where ResourceDisplayName == “Microsoft Graph”
| where ServicePrincipalId == “524c43c4-c484-4f7a-bd44-89d4a0d8aeab”
| summarize count() by bin(TimeGenerated, 1h)
| render timechart
AADServicePrincipalSignInLogs| where TimeGenerated > ago(90d)| where ResourceDisplayName == “Microsoft Graph”| where ServicePrincipalId == “524c43c4-c484-4f7a-bd44-89d4a0d8aeab”| summarize count() by bin(TimeGenerated, 1h)| render timechart

Security Analysts using Azure Sentinel could also leverage KQL’s built-in anomaly detection algorithms to find anomalous increases in Exchange mail items accessed operations which is another way to identify this attacker activity.


 


For example, below is a sample query that brings out some of the logons to Azure AD that did not use multi factor authentication. It is possible this could produce many results, so additional tuning is suggested for your environment. This attack also used Virtual Private Servers (VPS) hosts to access victim networks. Combining the query below with data that list VPS server ranges will make this a high-confidence alert.


 

Spoiler (Highlight to read)

SigninLogs
| where TimeGenerated > ago(30d)
| where ResultType == 0
| extend additionalDetails = tostring(Status.additionalDetails)
| evaluate ipv4_lookup(IP_Data, IPAddress, network, return_unmatched = false)
| summarize make_set(additionalDetails), min(TimeGenerated), max(TimeGenerated) by IPAddress, UserPrincipalName
| where array_length(set_additionalDetails) == 2
| where (set_additionalDetails[1] == “MFA requirement satisfied by claim in the token” and set_additionalDetails[0] == “MFA requirement satisfied by claim provided by external provider”) or (set_additionalDetails[0] == “MFA requirement satisfied by claim in the token” and set_additionalDetails[1] == “MFA requirement satisfied by claim provided by external provider”)
//| project IPAddress, UserPrincipalName, min_TimeGenerated, max_TimeGenerated
SigninLogs| where TimeGenerated > ago(30d)| where ResultType == 0| extend additionalDetails = tostring(Status.additionalDetails)| evaluate ipv4_lookup(IP_Data, IPAddress, network, return_unmatched = false)| summarize make_set(additionalDetails), min(TimeGenerated), max(TimeGenerated) by IPAddress, UserPrincipalName| where array_length(set_additionalDetails) == 2| where (set_additionalDetails[1] == “MFA requirement satisfied by claim in the token” and set_additionalDetails[0] == “MFA requirement satisfied by claim provided by external provider”) or (set_additionalDetails[0] == “MFA requirement satisfied by claim in the token” and set_additionalDetails[1] == “MFA requirement satisfied by claim provided by external provider”)//| project IPAddress, UserPrincipalName, min_TimeGenerated, max_TimeGenerated

In relation to the VPS servers section above, the previously mentioned workbook has a section that shows successful user signins from VPS (Virtual Private Server) providers where only tokens were used to authenticate. This uses the new KQL operator ipv4_lookup to evaluate if a login came from a known VPS provider network range. This operator can alternatively be used to look for all logons not coming from known ranges should your environment have a common logon source.


In addition to pre-compromise logon hunting it is also possible to monitor for logons attempting to use invalid key material. This can help identify attempted logons using stolen key material made after key material has been rotated. This can be done by querying SigninLogs in Azure Sentinel where the ResultType is 5000811. Please note that if you roll your token signing certificate, there will be expected activity when searching on the above.


 


Additionally, as a cloud native SIEM Azure Sentinel can not only collect raw data from various disparate logs but it also gets alerts from various security products. For example, M365 Defender has a range of alerts for various attack components like SolarWinds malicious binaries, network traffic to the compromised domains, DNS queries for known patterns associated with SolarWinds compromise that can flow into Sentinel. Combining these alerts with other raw logs and additional data sources provides the security team with additional insights as well as a complete picture of nature and the scope of attack.


 


References


Recent Nation-State Cyber Attacks 


Behavior:Win32/Solorigate.C!dha threat description – Microsoft Security Intelligence


Customer guidance on recent nation-state cyberattacks 


FireEye Advisory: Highly Evasive Attacker Leverages SolarWinds Supply Chain to Compromise Multiple Global Victims With SUNBURST Backdoor


FireEye GitHub page: Sunburst Countermeasures 


DHS Directive


SolarWinds Security Advisory


FalconFriday – Fireeye Red Team Tool Countermeasures KQL Queries  


Microsoft-365-Defender-Hunting-Queries: Sample queries for Advanced hunting in Microsoft 365 Defender (github.com)


Azure Sentinel SolarWinds Post Compromise Hunting Workbook


 


 


 


 


 


 

Brought to you by Dr. Ware, Microsoft Office 365 Silver Partner, Charleston SC.