MagicWeb: NOBELIUM’s post-compromise trick to authenticate as anyone
Credit to Author: Paul Oliveria| Date: Wed, 24 Aug 2022 17:00:00 +0000
Microsoft security researchers have discovered a post-compromise capability we’re calling MagicWeb, which is used by a threat actor we track as NOBELIUM to maintain persistent access to compromised environments. NOBELIUM remains highly active, executing multiple campaigns in parallel targeting government organizations, non-governmental organizations (NGOs), intergovernmental organizations (IGOs), and think tanks across the US, Europe, and Central Asia. The Microsoft Threat Intelligence Center (MSTIC) assesses that MagicWeb was likely deployed during an ongoing compromise and was leveraged by NOBELIUM possibly to maintain access during strategic remediation steps that could preempt eviction.
NOBELIUM has used abuse of identities and credentialed access as a method for maintaining persistence, and a specialized capability like MagicWeb is not novel for the actor: in September 2021, Microsoft disclosed a post-exploitation capability named FoggyWeb with methods and intent similar to MagicWeb. FoggyWeb was capable of exfiltrating the configuration database of compromised AD FS servers, decrypting token-signing certificates and token-decryption certificates, and downloading and executing additional malware components. MagicWeb goes beyond the collection capabilities of FoggyWeb by facilitating covert access directly. MagicWeb is a malicious DLL that allows manipulation of the claims passed in tokens generated by an Active Directory Federated Services (AD FS) server. It manipulates the user authentication certificates used for authentication, not the signing certificates used in attacks like Golden SAML.
NOBELIUM was able to deploy MagicWeb by first gaining access to highly privileged credentials and moving laterally to gain administrative privileges to an AD FS system. This is not a supply chain attack. The attacker had admin access to the AD FS system and replaced a legitimate DLL with their own malicious DLL, causing malware to be loaded by AD FS instead of the legitimate binary. The backdoor was discovered by Microsoft’s Detection and Response Team (DART) in coordination with MSTIC and Microsoft 365 Defender Research during an ongoing incident response investigation. Microsoft is sharing this information with consent from the client. At the time of this investigation, MagicWeb appears to be highly targeted.
Like domain controllers, AD FS servers can authenticate users and should therefore be treated with the same high level of security. Customers can defend against MagicWeb and other backdoors by implementing a holistic security strategy including the AD FS hardening guidance. In the case of this specific discovery, MagicWeb is one step of a much larger intrusion chain that presents unique detection and prevention scenarios.
With all critical infrastructure such as AD FS, it is important to ensure attackers do not gain administrative access. Once attackers gain administrative access, they have many options for further system compromise, activity obfuscation, and persistence. We recommend that any such infrastructure is isolated, accessible only by dedicated admin accounts, and regularly monitored for any changes. Other security measures that can prevent this and other attacks include credential hygiene to prevent lateral movement. AD FS is an on-premises server, and as with all on-premises servers, deployments can get out of date and/or go unpatched, and they can be impacted by local environment compromises and lateral movement. For these reasons, migration to a cloud-based identity solution such as Azure Active Directory for federated authentication is recommended for the robust security it provides. See the mitigation section below for more information. Though we assess the capability to be in limited use, Microsoft anticipates that other actors could adopt similar methodologies and therefore recommends customers review hardening and mitigation guidance provided in this blog.
How MagicWeb subverts authentication
MagicWeb is a post-compromise malware that can only be deployed by a threat actor after gaining highly privileged access to an environment and moving laterally to an AD FS server. To achieve their goal of maintaining persistent access to an environment by validating authentication for any user account on the AD FS server, NOBELIUM created a backdoored DLL by copying the legitimate Microsoft.IdentityServer.Diagnostics.dll file used in AD FS operations. The legitimate version of this file is catalog signed by Microsoft and is normally loaded by the AD FS server at startup to provide debugging capabilities. NOBELIUM’s backdoored version of the file is unsigned. The threat actor’s highly privileged access that allowed them to access the AD FS server meant they could have performed any number of actions in the environment, but they specifically chose to target an AD FS server to facilitate their goals of persistence and information theft during their operations.
After gaining administrative access to an AD FS server via elevation of privilege and lateral movement, the loading of NOBELIUM’s malicious Microsoft.IdentityServer.Diagnostics.dll into the AD FS process is possible by editing C:WindowsAD FSMicrosoft.IdentityServer.Servicehost.exe.config to specify a different public token, which controls what loads into the AD FS process when it is started. Because AD FS is a .NET application, it loads the DLLs specified in the config file from the Global Assembly Cache (GAC). By changing the token in the configuration, the adversary directed AD FS to load in the malicious DLL. The interception and manipulation of claims by MagicWeb enables the actor to generate tokens that allow the adversary to bypass AD FS policies (role policies, device policies, and network policies) and sign in as any user with any claims, including multifactor authentication (MFA).
To understand how NOBELIUM can subvert the AD FS process with the MagicWeb malware, it’s important to understand how AD FS claims work. AD FS extends the ability to use single sign-on functionality available within a single security or enterprise boundary to internet-facing applications to provide customers, partners, and suppliers a streamlined user experience while accessing an organization’s web-based applications. AD FS relies on claims-based authentication to validate the identity of the user and their authorization claims. These claims are packaged into a token that can be used for authentication. MagicWeb injects itself into the claims process to perform malicious actions outside the normal roles of an AD FS server.
Security Assertion Markup Language (SAML) uses x509 certificates to establish trust relationships between identity providers and services and to sign and decrypt tokens. These x509 certificates contain enhanced key usage (EKU) values that specify what applications the certificate should be used for. For instance, an EKU containing an Object Identifier (OID) value of 1.3.6.1.4.1.311.20.2.2 would allow for the use of a SmartCard logon. Organizations can create custom OIDs to further narrow certificate usage.
MagicWeb’s authentication bypass comes from passing a non-standard Enhanced Key Usage OID that is hardcoded in the MagicWeb malware during an authentication request for a specified User Principal Name. When this unique hard coded OID value is encountered, MagicWeb will cause the authentication request to bypass all standard AD FS processes (including checks for MFA) and validate the user’s claims. MagicWeb is manipulating the user authentication certificates used in SAML sign-ins, not the signing certificates for a SAML claim used in attacks like Golden SAML.
NOBELIUM uses unique tradecraft per target, so it’s highly likely that the OIDs and public tokens are unique per target as well. We’ve redacted these OIDs and tokens in this report. Please see the hunting guidance section for information on how to look for variants related to this attack.
How to mitigate this threat
NOBELIUM’s ability to deploy MagicWeb hinged on having access to highly privileged credentials that had administrative access to the AD FS servers, giving them the ability to perform whatever malicious activities they wanted to on the systems they had access to.
It’s critical to treat your AD FS servers as a Tier 0 asset, protecting them with the same protections you would apply to a domain controller or other critical security infrastructure. AD FS servers provide authentication to configured relying parties, so an attacker who gains administrative access to an AD FS server can achieve total control of authentication to configured relying parties (include Azure AD tenants configured to use the AD FS server). Practicing credential hygiene is critical for protecting and preventing the exposure of highly privileged administrator accounts. This especially applies on more easily compromised systems like workstations with controls like logon restrictions and preventing lateral movement to these systems with controls like the Windows Firewall.
Migration to Azure Active Directory (Azure AD) authentication is recommended to reduce the risk of on-premises compromises moving laterally to your authentication servers. Customers can use the following references on migration:
Advanced hunting queries
Recommended hunting guidance
- Have Inventory Certificate Issuance policies in your Public Key Infrastructure (PKI) environment, including all EKU attributes used in the environment and compare to known OID values.
- Hunt across Windows Event Logs by enabling AD FS verbose logging and collect AD FS event logs specifically looking for Event ID 501, which specifies all the EKU attributes on a claim. Hunt across these logs to look for EKU values which your PKI infrastructure isn’t configured to issue.
- Look for portable executable files in the GAC or AD FS directories on your systems that aren’t signed by Microsoft and inspect these files or submit them for analysis.
- Perform an audit of your exclusion settings to be sure that the AD FS and GAC are included in scans. Many organizations exclude the AD FS directories from security software scanning because of performance degradation concerns.
Microsoft Sentinel
Microsoft Sentinel customers who have enabled verbose mode logging for ADFS can use this query to look for suspicious OIDs: https://github.com/Azure/Azure-Sentinel/tree/master/Detections/SecurityEvent/ADFSAbnormalEnhancedKeyUsageAttribute-OID.yaml.
let OIDList = SecurityEvent| where TimeGenerated >= ago(1d) | where EventSourceName == 'AD FS Auditing'| where EventID == 501| where EventData has '/eku'| extend OIDs = extract_all(@"<Data>([d+.]+)</Data>", EventData) | mv-expand OIDs| extend OID = tostring(OIDs) | extend OIDLength = strlen(OID) | project TimeGenerated, Computer, EventSourceName, EventID, OID, OIDLength, EventData; OIDList| where TimeGenerated >= ago(1h) | join kind=leftanti ( OIDList| where TimeGenerated between (ago(1d) .. ago(1h)) | summarize by OID) on OID
Searching for unsigned files in the GAC
The legitimate Microsoft.IdentityServer.Diagnostics.dll is catalog signed by Microsoft. Catalog signing is a method Windows uses for validating code integrity different from Authenticode, and is used for offline validation rather than runtime enforcement of running only signed code. The catalog signing on this file means the file may appear to be unsigned on the file properties pane and in file integrity checkers, security tools, and online malware repositories. The scripts below allow you to look for unsigned binaries and understand both catalog-signed binaries and Authenticode-signed binaries.
Surface unsigned DLLs in GAC using Microsoft 365 Defender
This query surfaces unsigned DLLs in the GAC folder created within the last 60 days.
DeviceFileEvents | where Timestamp between( ago(60d)..now() ) | where FolderPath has @"C:WindowsMicrosoft.NETassemblyGAC_MSILMicrosoft.IdentityServer." and FileName endswith ".dll" | join ( DeviceFileCertificateInfo | where not(IsSigned) ) on SHA1
Enumerate non-Microsoft signed DLLs in the GAC using PowerShell
Below is an example script that could be used to enumerate non-Microsoft signed DLLs in the relevant GAC folder, where servers.txt is a list of servers you wish to scan. Because the legitimate Microsoft.IdentityServer.Diagnostics.dll is catalog signed, signing won’t appear when viewing file properties, but it will show in PowerShell querying and on load of the DLL.
$servers = get-content -Path (path to file)servers.txt Foreach ($server in $servers) { Write-Output "Processing server: $server" Invoke-Command -ComputerName $server {Get-ChildItem -Filter "*.dll" -Recurse "C:WindowsMicrosoft.NETassemblyGAC_MSIL" | get-authenticodesignature | ft} }
Detections
Microsoft Defender Antivirus
Microsoft Defender Antivirus provides detection for this threat under the following malware name:
- Trojan:MSIL/MagicWeb.A!dha
Microsoft Defender for Endpoint
Microsoft Defender for Endpoint customers may see the following alert as an indication of possible attack:
- ADFS persistent backdoor detected
Indicators of compromise (IOCs)
Microsoft isn’t sharing IOCs on this NOBELIUM activity at this time. However, NOBELIUM frequently customizes infrastructure and capabilities per campaign, minimizing operational risk should their campaign specific attributes be discovered. If MagicWeb is identified in your environment, it’s unlikely to match any static IOCs from other targets such as a SHA-256 value. It’s recommended to use the hunting guidance provided above to investigate your environment.
Technical analysis of MagicWeb
NOBELIUM has modified the legitimate Microsoft.IdentityServer.Diagnostics.dll by adding malicious code to the TraceLog class from the Microsoft.IdentityServer.Diagnostics namespace/type.
The header section of the TraceLog class from the legitimate Microsoft.IdentityServer.Diagnostics.dll is shown below:
Meanwhile, the header section of the TraceLog class from NOBELIUM’s backdooredversion of Microsoft.IdentityServer.Diagnostics.dll is shown below:
In the backdoored version of the code, as shown above, NOBELIUM has added a static constructor for the TraceLog class. A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed only once. It’s called automatically before the first instance is created or any static members are referenced.
The malicious static constructor gets executed once before the first instance of the TraceLog class is created. Given that new instances of the TraceLog class is created in various locations in this DLL, the execution of the malicious static constructor is guaranteed to occur as soon as the DLL is loaded for the first time (which would be upon startup of the AD FS server after the malicious changes to Microsoft.IdentityServer.Servicehost.exe.config described above).
NOBELIUM’s malicious static constructor contains a reference to the Initialize() method from a class named AuthLog.
The AuthLog class is a brand-new and malicious class that’s been added to the DLL by NOBELIUM.
As shown above, the Initialize() method references a class named RuntimeHelper, yet another class added to the DLL by the actor. The primary purpose of the RuntimeHelper class and its OverloadMethod() method is to hook legitimate AD FS related methods at runtime. By hooking the legitimate AD FS methods, the backdoor is capable of intercepting calls to the legitimate methods to instead invoke its own custom methods.
The screenshot above shows the following legitimate AD FS methods being hooked by MagicWeb:
Target assembly/DLL | Target type | Target method to hook | Malicious hook method (actor introduced) |
Microsoft.IdentityServer.IdentityModel.dll | Microsoft.IdentityModel.X509CertificateChain | Build | BeginBuild |
Microsoft.IdentityServer.WebHost.dll | Microsoft.IdentityServer.WebHost.WrappedHttpListenerRequest | GetClientCertificate | BeginGetClientCertificate |
Microsoft.IdentityServer.WebHost.dll | Microsoft.IdentityServer.WebHost.Proxy.ProxyConfigurationData | EndpointConfiguration | BeginEndpointConfiguration |
Microsoft.IdentityServer.Service.dll | Microsoft.IdentityServer.Service.IssuancePipeline.PolicyEngine | ProcessClaims | BeginProcessClaims |
Hook method: BeginBuild()
MagicWeb’s BeginBuild() method is used to hook the legitimate target method Build() (from Microsoft.IdentityServer.IdentityModel.dll).
The BeginBuild() method first calls the MagicWeb’s helper method ValidateX509Extensions().
If the helper method ValidateX509Extensions() returns true, BeginBuild() returns true.
If ValidateX509Extensions() returns false, or an exception is thrown by calling ValidateX509Extensions(), BeginBuild() invokes and returns the value returned by the legitimate Build() method from Microsoft.IdentityServer.IdentityModel.dll.
This means that before the legitimate target method Build() from the legitimate Microsoft.IdentityServer.IdentityModel.dll gets an opportunity to inspect/build a certificate, MagicWeb’s hook method first inspects the certificate and returns true if the helper method ValidateX509Extensions() returns true.
This allows the attacker to subvert the normal certificate inspection/build process by introducing a custom certificate inspection/build method that’s invoked before the legitimate Build() method is invoked.
Helper Method: ValidateX509Extensions()
MagicWeb’s helper method ValidateX509Extensions() is called by BeginBuild() and other methods.
ValidateX509Extensions() returns false if the X509 certificate passed to the method is null or the Microsoft Cryptographic API certificate context handle/pointer isn’t set.
Next, the method enumerates the extensions in the X509 certificate passed to the method. If an enumerated extension is of type X509EnhancedKeyUsageExtension, the method iterates the OIDs of the extension, calculating the MD5 hash of each OID (using a custom hash computation helper method ComputeHash() that leverages the .NET MD5 class).
If the MD5 hash value of the OID matches one of the two following hardcoded MD5 values, the method returns true (this methodology is used to check if one of the two OID values below are present in the extension):
- 67F5BD28A842A1C9[REDACTED] (MD5 hash value corresponding to the OID value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED])
- 6E3466296D2F63D[REDACTED] (MD5 hash value corresponding to the OID value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED])
If none of the OID values are present, the method returns false.
This helper method returns true if the certificate passed to the method contains one of the two magic OID values listed above.
Hook method: BeginGetClientCertificate()
To retrieve the client’s X509 certificate, this method first calls the legitimate GetClientCertificate() method from Microsoft.IdentityServer.WebHost.dll. Next, the hook method calls the helper method ValidateX509Extensions() to determine whether the client certificate contains one of the two “magic” OID values. If the client certificate contains one of the two OID values, the hook method:
- Obtains the _adapter field from the current object
- Obtains the _request field from the _adapter object
- Sets the value of the m_ClientCertificateError field (from the _request object) to 0
This means that regardless of what the legitimate method GetClientCertificate() (from Microsoft.IdentityServer.WebHost.dll) sets the m_ClientCertificateError field to, if a client certificate contains one of the magic OID values, the hook method overwrites or sets the m_ClientCertificateError field to 0.
By using this technique, the hook method appears to be influencing the normal behavior of the application to treat or accept a non-valid client certificate as a valid certificate.
Hook method: BeginProcessClaims()
The hook method first indirectly invokes the legitimate ProcessClaims() method by invoking the ProcessClaims() method of the AuthLog class.
On line 198 in figure 16, the hook method calls MagicWeb’s helper method GetClaims(), passing in the processed identity object returned by invoking the legitimate ProcessClaims() method.
As shown above, the GetClaims() method accepts an identity object as a parameter. The method then initializes three variables named type, type2, and type3 with values obtained from the RuntimeHelper’s static field/array named types:
The types field contains the following values:
The assemblyByName2 variable above contains an assembly object representing the legitimate assembly Microsoft.IdentityServer.IdentityModel.dll (if not already loaded, the RuntimeHelper class loads the assembly into the current application domain). By calling the GetType() method, RunHelper initializes the member of the types field/array with .NET types from the Microsoft.IdentityServer.IdentityModel.dll assembly.
Returning to the GetClaims() method and the initialization of type, type2, and type3 the variables type, type2, and type3 get initialized with the following type objects from Microsoft.IdentityServer.IdentityModel.dll:
- type: Microsoft.IdentityModel.Claims.IClaimsIdentity type object
- type2: Microsoft.IdentityModel.Claims.ClaimCollection type object
- type3: Microsoft.IdentityModel.Claims.Claim type object
Next, the GetClaims() method retrieves the Claims property of the Microsoft.IdentityModel.Claims.IclaimsIdentity identity object. It also retrieves the number of claims (of type Microsoft.IdentityModel.Claims.ClaimCollection) present in the Claims property:
GetClaims() then enumerates the claims (of type Microsoft.IdentityModel.Claims.Claim), retrieving the string containing each claim and the corresponding claim type:
As shown above, the claim string and claim type string are then stored in a list named list. This list of claims and their corresponding claim types is then returned to the caller of the GetClaims() method, BeginProcessClaims().
Returning to the BeginProcessClaims() method, after retrieving the claims using the GetClaims() method, the hook method BeginProcessClaims() searches the claims list for presence of a claim with claim type of http://schemas.microsoft.com/claims/authnmethodsreferences:
As shown on line 198 above, the claim(s) of type http://schemas.microsoft.com/claims/authnmethodsreferences (if any) is stored in a list named list. If claim of type http://schemas.microsoft.com/claims/authnmethodsreferences is present and its value is set to http://schemas.microsoft.com/claims/multipleauthn, the hook method returns the IclaimsIdentity object returned by the legitimate target method ProcessClaims() (from Microsoft.IdentityServer.Service.dll) on line 191 of the hook method.
This behavior ensures that if MFA is already satisfied, then the hook method simply acts as a pass-through method and doesn’t affect the normal behavior of the claim processing pipeline.
If a claim of type http://schemas.microsoft.com/claims/authnmethodsreferences is not present or its value is not set to http://schemas.microsoft.com/claims/multipleauthn, the hook method proceeds to perform additional checks on the unprocessed claims (that is, the claims contained in the unprocessed identity object identity passed to the hook method). Once again, the hook method obtains a list of claims by calling the GetClaims() helper method. As mentioned above, instead of calling the GetClaims() helper method with the processed identity object returned by invoking the legitimate ProcessClaims() method (stored in the result variable on line 191), the hook method calls the GetClaims() helper method with the unprocessed identity object identity passed to the hook method:
On line 204, the hook method enumerates the value of each claim and uses the ComputeHash() helper method to calculate the MD5 hash value of each claim value (from the unprocessed identity object). It then checks if the MD5 value of any of the claims equals the MD5 hash value 6E3466296D2F63DE[REDACTED]. This hash value is the only element of a hardcoded hash list named oidMFAHashes (that is, this list can be expanded to include other hash values of interest):
If none of the claims have a value with MD5 hash value of 6E3466296D2F63DE[REDACTED], on line 206, the method simply returns the processed identity object returned by the legitimate target method ProcessClaims() (from Microsoft.IdentityServer.Service.dll) on line 191 of the hook method. As previously discussed, the hash value 6E3466296D2F63DE[REDACTED] corresponds to the OID value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED].
Hence, the hook method enumerates the claims and if a claim with value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED] isn’t present on the claim list, the hook method simply acts as a pass-through method and doesn’t affect the normal behavior of claim processing pipeline.
If by this point in the execution cycle the hook method hasn’t returned yet, it means one of the claims contains the OID value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED] (otherwise, according to the logic described in the paragraph above, the hook method would’ve returned).
Proceeding with confirmation that one of the claims contains the OID value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED], the hook method proceeds to the section that represents the main purpose of MagicWeb, to perform claim injection.
Before describing the code responsible for the claim injection process, it’s important to revisit what’s already stored in the list and claims variables:
- list: As mentioned before, the hook method invokes the legitimate method ProcessClaims() to process the incoming identity object. The processed identity object (stored in result on line 191) is then passed to the GetClaims() helper method to obtain a list of claim type/value pairs extracted from the processed identity object (line 198). After obtaining the claim type/value pairs, the claim(s) of type http://schemas.microsoft.com/claims/authnmethodsreferences (if any) are stored in a list named list (line 198).
claims: As mentioned above, this variable is used to store a list of claim type/value pairs extracted from the unprocessed identity object:
With this information in mind (and the fact that one of the claims contains the OID value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED]), once again here’s the first part of the claim injection code:
As shown above, if list is empty (that is, the processed identity object contained no claim type/value pairs of type http://schemas.microsoft.com/claims/authnmethodsreferences), the hook method instead turns to claims (containing the list of all claim type/value pairs extracted from the unprocessed identity object) and searches for claim type/value pairs of type http://schemas.microsoft.com/claims/authnmethodsreferences in the claims list. If the claims list contains one or more claim type/value pairs of type http://schemas.microsoft.com/claims/authnmethodsreferences, the hook method uses the claim information to add an identical claim of type http://schemas.microsoft.com/claims/authnmethodsreferences to the processed identity object (line 213 above).
Using this method, if after passing the identity object to the legitimate ProcessClaims() method, no claim of type http://schemas.microsoft.com/claims/authnmethodsreferences is returned by the legitimate method, the hook method manually adds a fraudulent claim of type http://schemas.microsoft.com/claims/authnmethodsreferences to the list of claims returned to the caller of the hooked legitimate method ProcessClaims().
As shown above, to add the fraudulent claim to the list of claims, the hook method calls a helper method named AddClaim().
Like the code in the helper method GetClaims(), AddClaims() initializes two variables with the following type objects:
- type: Microsoft.IdentityModel.Claims.IClaimsIdentity type object
- type2: Microsoft.IdentityModel.Claims.ClaimCollection type object
On line 235, AddClaims() gets the constructor for type Microsoft.IdentityModel.Claims.Claim and invokes the constructor (passing in the claim type and value from the caller of AddClaim()) to instantiate a new Claim object.
The legitimate internal constructor from Microsoft.IdentityModel.Claims.Claim, retrieved and invoked by AddClaim(), invokes the internal constructor Claim (overloaded method) with the following method parameters:
After instantiating a new Claim object, AddClaim() uses the Add() method from type Microsoft.IdentityModel.Claims.ClaimCollection to add the new claim to the identity object passed to AddClaim() by its caller (in this case, the new claim is added to the identity object containing the list of claims returned by the call to the legitimate method ProcessClaims()).
Revisiting the claim injection code in the hook method BeginProcessClaims() (and recalling the fact that one of the claims contains the OID value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED]), here’s the second part of the claim injection code:
Recall that list contains claim type/value pairs of type http://schemas.microsoft.com/claims/authnmethodsreferences extracted from the processed identity object. If none of the claims in list have the value http://schemas.microsoft.com/claims/multipleauthn, the hook method proceeds to call AddClaim() to add a fraudulent claim of type http://schemas.microsoft.com/claims/authnmethodsreferences and value http://schemas.microsoft.com/claims/multipleauthn to the list of claims returned to the caller of the hooked legitimate method ProcessClaims().
Using the fraudulent claim injection techniques described above, if a claim with the Magic OID value 1.3.6.1.4.1.311.21.8.868518.12957973.4869258.12250419.[REDACTED].[REDACTED].[REDACTED].[REDACTED] is presented to AD FS, regardless of how the legitimate hooked method ProcessClaims() handles the claim, the BeginProcessClaims() hook function ensures that a claim with value http://schemas.microsoft.com/claims/multipleauthn is returned to the caller of the legitimate hooked method ProcessClaims().
Hook method: BeginEndpointConfiguration()
The backdoor BeginEndpointConfiguration() method, used to hook the legitimate target method EndpointConfiguration() (from Microsoft.IdentityServer.WebHost.dll) is shown below:
The enumType variable is initialized with RuntimeHelper.types[0] which is a Microsoft.IdentityServer.WebHost.Proxy.CertificateValidation type object. The PropertyInfo variables propertyInfo, propertyInfo2, and propertyInfo3 are initialized with property objects retrieved from ‘properties’ field/array of RuntimeHelper:
- propertyInfo: CertificateValidation property from type Microsoft.IdentityServer.WebHost.Proxy.ProxyEndpoint of Microsoft.IdentityServer.WebHost.dll
- propertyInfo2: Path property from type Microsoft.IdentityServer.WebHost.Proxy.ProxyEndpoint of Microsoft.IdentityServer.WebHost.dll
- propertyInfo3: Endpoints property from type Microsoft.IdentityServer.WebHost.Proxy.ProxyEndpointConfiguration of Microsoft.IdentityServer.WebHost.dll
Next, the hook method retrieves the value of the Endpoint property of the value object that the legitimate EndpointConfiguration() method was called with. The Endpoint property holds a collection of ProxyEndpoint objects. The hook method enumerates the ProxyEndpoint objects and for each object, it checks if the value of the CertificateValidation enum is set to ‘1’ which signifies ‘SSL’. If the CertificateValidation enum for a ProxyEndpoint object is set to ‘1’/’SSL’, on line 165, the hook method overwrites the value of the CertificateValidation enum with ‘0’ which signifies ‘None’. To ensure the change is reflected, the hook method then overwrites the Endpoint property of the value object with the updated Endpoint property containing the overwritten CertificateValidation enum values (that is, ‘SSL’ overwritten with ‘None’).
Behaving as a true hook method, on line 179, the method calls the legitimate EndpointConfiguration() method but with the modified ‘value’ object. Hence, when the legitimate EndpointConfiguration() method is invoked during the normal operation of AD FS, this hook method intercepts the call and, before passing the object to the legitimate EndpointConfiguration() method was invoked with, it overwrites the CertificateValidation value of each ProxyEndpoint object and only then it calls the legitimate EndpointConfiguration() method but now with modified CertificateValidation value(s), changed from ‘SSL’ to ‘None’.
The purpose of overwriting CertificationValidation value to ‘None’ (wherever it’s ‘SSL’) is to allow WAP to pass the request with the specific malicious certificate to AD FS for further authentication processing. According to Microsoft.IdentityServer.ProxyService/TLSClientReqeustHandler, WAP stops sending the current request from client to AD FS if CertificateValidation is ‘1’ (‘SSL’) and the client certificate has an error during validation.
References
- “I am AD FS and so can you: Attacking Active Directory Federated Services”, Austin Baker and Douglas Bienstock, Troopers 2019
- Understanding Key Active Directory Federation Services Concepts, Microsoft documentation
The post MagicWeb: NOBELIUM’s post-compromise trick to authenticate as anyone appeared first on Microsoft Security Blog.