The NTLM relay feature of Impacket’s ntlmrelayx.py
used to offer only two servers, HTTP and SMB, for incoming NTLM authenticated connections using those two protocols. Which can then be relayed to more protocols: HTTP, SMB, LDAP, SMTP, etc. I had a situation where the incoming NTLM authenticated connection used ADWS (built upon NetTcpBinding WCF) so I implemented this new server protocol in Impacket 😉
How to use this new feature in a AD domain environment?
1. Run this on pentester machine:
$ ntlmrelayx --no-smb-server --no-http-server -t rpc://<target> -c "echo a > c:\test"
2. Simulate a vulnerable client by running this PowerShell command on a domain member while being authenticated as a user who has admin rights over
>> get-aduser -filter * -server <pentester_machine>
3. You should notice the creation of the c:\test file on the
Here is a successful result on pentester-side (PYTHONPATH
because I run Impacket from source):
If you want to learn more about the theory I recommend reading NTLM Relay by Pixis @hackndo
How it began… 🔗
I was pentesting a webapp which called PowerShell scripts using the ActiveDirectory module. One of the API calls took as parameter the domain controller FQDN/IP so I passed my IP and in Wireshark I saw an incoming TCP connection on port 9389. This port is used by ADWS! It means that my domain controller parameter was passed to the -Server
parameter of one of the cmdlets.
I created a TCP relay with socat which allowed me to relay the traffic on the network level to a domain controller and observe the traffic in Wireshark:
$ socat TCP-LISTEN:9389,fork,reusaddr TCP:<DC_IP>:9389
The beginning of the connection had clear-text content which showed that NTLMSSP authentication was used by the client and accepted by the server (domain controller here)
I explicitly used an IP instead of a FQDN for -Server
to make sure that NTLM would be preferred to Kerberos.
So, I could get an incoming NTLM authentication therefore it could be NTLM-relayed! 🏆 If only I had a ADWS server in Impacket, which was not the case yet… 😥
Before going further, here is how the Impacket NTLM-relay feature implementation is organized. Hats off to Dirk-jan Mollema (@_dirkjan) and Alberto Solino (@agsolino) for this modular design!
ADWS? 🔗
ADWS means Active Directory Web Services and is offered by domain controllers to interact with the domain instead of using more traditional protocols such as LDAP or RPC. The official PowerShell ActiveDirectory module notably uses it. You can learn more about ADWS and how to use it as programmer.
⚠️ The “Web Services” part of its name is misleading as it does not use HTTP at all since it is built upon WCF.
WCF / Net.TCP Binding? 🔗
WCF is “a unified programming model for building service-oriented applications” especially popular in .NET applications. ADWS uses specifically its NetTcpBinding (Net.TCP Binding) implementation. You can notice it when the “net.tcp://” scheme is used in URLs (and appears in clear at the beginning of the trafic). It is a binary protocol which is not yet widely supported by tools and libraries outside of .NET. And I had to implement enough of it in Python to complete the NTLM challenge-response… 😬
If we continue to dig (are you still following me?), it is built on two protocols published by Microsoft:
Since then, Uli H (@Pizza_4u) very kindly implemented it in Wireshark so we can finally easily analyze this traffic!
Three years after starting an implementation support for .NET Message Framing Protocol (MC-NMF) and MS-NNS has finally landed in @WiresharkNews. Will be available with upcoming 3.4 release. :-) Thanks @cnotin for sharing a capture file. https://t.co/bxckJiTJox pic.twitter.com/ydHp5x1IZ6
— Uli H (@Pizza_4u) October 6, 2020
Prior to version 4.1.0 (where I introduced a fix), it wasn’t applied automatically, so you had to right-click on the traffic -> Decode As… In the window ensure that “TCP port” and “9389” are selected then in the right-hand side “Current” column, select “MC-NMF”. Apply with “OK” and it will look better (no need to search for MS-NNS, it will appear automatically).
I also found the Pentesting Webservices with Net.TCP Binding article by Timo (@bluec0re) who shared very useful Python code which inspired a lot my implementation:
- https://github.com/ernw/net.tcp-proxy/blob/master/nettcp/nmf.py
- https://github.com/ernw/net.tcp-proxy/blob/master/nettcp/stream/negotiate.py
- https://github.com/ernw/python-wcfbin
Protections 🔗
NTLM Relaying can mitigated in several ways. We can distinguish between recommendations that can be applied on either sides.
- On the vulnerable application (which is the source of the authentication, the web app using AD cmdlets in our example):
- If possible, the user should not be allowed to choose the server to contact (and to which the client will authenticate). In the case of the app I was pentesting, there was no legimitate use case of passing the Server in the HTTP request. In a way it is similar to the easiest way of fixing SSRF: no more user supplied URL, no more vulnerability! 👌 Not always applicable though…
- Disable NTLM in clients: especially when contacting a domain controller, Kerberos will work fine and is not susceptible to NTLM relay.
- On all potential target services (which are the destination of the authentication, the domain controller in our example):
- Implement some sort of signing (e.g. SMB Signing or SPN Target Name Validation for SMB, LDAP Signing…) or channel binding (e.g. LDAP Channel Binding which should have been set as default in 2020 but as reminded recently still is not so follow instructions, or Extended Protection for Authentication for everything SSL/TLS)
- The RPC protocol is notably available on domain controllers and only partially protected. As described in Relaying NTLM authentication over RPC by Sylvain Heiniger (@sploutchy) this was covered in CVE-2020-1113 which only concerns abuses through the task scheduler. We can infer that the domain controller in my lab is not patched 😐
Conclusion 🔗
The main resulting code is available in wcfrelayserver.py shared through PR #944 now merged. I hope you will like it 😉
I only used ADWS in my tests and in the examples above, but this implementation should work with any WCF / Net.TCP Binding vulnerable client.