13 July 2022
Introducing Pretender - Your New Sidekick for Relaying Attacks
We’ve just released another open-source tool: pretender, a cross-platform tool to obtain a machine-in-the-middle position inside Windows networks in the spirit of Responder and mitm6. It implements local name resolution spoofing using the mDNS, LLMNR, and NetBIOS-NS protocols as well as a DHCPv6 DNS takeover attack. Since last year pretender has become a frequently used tool in our network penetration tests and we are proud to share our work with the community.
If you are already familiar with these tools and know why you might need them, you can skip right to the section “Why pretender? Advantages and Usage Scenarios”, where we explain how pretender differs from other tools such as Responder and mitm6. Before explaining the attacks themselves, let’s discuss why we would want to conduct a machine-in-the-middle attack in the first place.
Machine-in-the-Middle Attacks and NTLM Relaying
Since HTTPS really took off a little over a decade ago, machine-in-the-middle attacks against connections to Internet servers became less relevant and most protocols in use today are resistant against such attacks. However, in Windows domains (Active Directory) the situation is vastly different because the vulnerable NTLM authentication protocol (also referred to as NetNTLM) is still widely used for a variety of protocols such as SMB, HTTP or LDAP. Attacking the authentication protocol allows us to obtain an entry point into the domain or even a privilege escalation.
For example, if we can observe NTLM authentication between two hosts, we obtain a challenge-response-based NTLMv1 or NTLMv2 password hash (also known as NetNTLMv1/NetNTLMv2). Although it is very much possible to crack these hashes for weak passwords this is often not even necessary if we can not only observe the communication but also intercept it.
This brings us to the incredibly powerful NTLM relay attack. In most cases, it is possible to relay an intercepted authentication handshake to another host. This grants us access to the chosen host with the privileges of the user that initiated the intercepted connection. In most cases, NTLM relaying quickly leads to a full domain compromise and is therefore an immensely valuable attack technique. There are also a lot of nuances so it is very much recommended to learn as much about it as possible. We like to recommend the excellent hackndo blog post for further information.
The primary prerequisite to conduct such attacks is the machine-in-the-middle
position. Therefore, it is important for us to be able to reliably obtain such a
position with as little disruption for our clients as possible. That’s exactly
where pretender comes into
play: It handles the name resolution spoofing and works with other tools such as
Impacket’s ntlmrelayx.py
to
relay and capture hashes.
DHCPv6 DNS Takeover
The DHCPv6 DNS takeover attack is one of the most elegant attacks to obtain a machine-in-the-middle position in networks without IPv6 configuration. And let’s face it, the majority of company networks still don’t use IPv6 internally yet. For us, that’s good news because we can provide an IPv6 configuration for the clients ourselves.
The attack works by setting up a rogue DHCPv6 server. By default, Windows hosts
in the local network segment will pick up on our server as they regularly scan
the network for DHCPv6 servers and we can even speed this up by sending ICMPv6
router advertisements. As soon as the DHCPv6 server is discovered, Windows will
request an address and
pretender will just generate
one within the link-local address range (fe80::/64
) but it will also advertise
itself as an IPv6-reachable DNS server alongside the address.
Now the Windows host knows two DNS servers: The IPv4-reachable DNS server which it already knew before the attack, as well as the IPv6-reachable attacker-controlled DNS server in the link-local IPv6 network segment. But here comes the twist: By default, Windows prefers DNS servers that are reachable via IPv6 to those that are reachable via IPv4. This means that all queries are now sent to the attacker-controlled DNS server enabling a machine-in-the-middle position for the attacker.
Here you can see the attack in action. The --no-lnr
flag just disables local
name resolution attacks which we will cover in the next section.
DHCPv6 DNS Takeover in Action
Immediately after the ICMPv6 Router Advertisement is sent, the grey log lines indicate that our DHCPv6 server is discovered by the Windows host and our DNS server is assigned. Almost directly thereafter, we start to receive the DNS queries for both IPv4 (A) and IPv6 (AAAA) addresses which we can then answer with the IP of our attack system. This is a very powerful technique because it allows us to intercept the vast majority of outgoing connections of the systems. However, this also means that it can be a little disruptive if you simply attack all Windows hosts in the local network segment instead of choosing the targets carefully.
As mentioned in the beginning, the original implementation which we used as a reference for the DHCPv6 DNS takeover feature of pretender is called mitm6. It is also accompanied by a great blog post that covers the topic from a slightly different angle.
Local Name Resolution Spoofing
Another attack technique implemented by pretender is local name resolution spoofing. This technique targets hosts trying to contact other hosts whose addresses cannot be resolved via DNS. In this case, they basically just ask around within the local network. Windows currently utilizes three different protocols for this purpose: Multicast DNS (mDNS), Link Local Multicast Name Resolution (LLMNR) and the NetBIOS Naming Service (NetBIOS-NS or NBNS). As the names suggest, they all use either multicast or broadcast to reach all hosts in the network segment and nobody stops us from answering the queries ourselves.
But first, let’s talk about the circumstances in which local name resolution is used. One of the situations we frequently encounter involves network shares that no longer exist. The Active Directory DNS server has long forgotten about them, but some clients still try to mount them, for example, because they are still configured in some software or service. Sometimes people also just choose to use local name resolution instead of DNS out of convenience when referencing a hostname without appending the domain name.
The attack technique itself is very simple: Listen for local name resolution queries and answer with the address of the attack system. It’s as simple as that. So let’s have a look at the differences between the three protocols.
mDNS
mDNS is basically just plain old DNS for peer-to-peer connections using the
multicast addresses 224.0.0.251
when operating over IPv4 and ff02::fb
when
operating over IPv6. It uses UDP port 5353
, which is considered a high port on
Linux and usually requires no special privileges to bind to. By default, Windows
does not expose any services on this port and thus usually allows users to bind
to it.
LLMNR
LLMNR was developed by Microsoft as a successor to NetBIOS-NS and like mDNS it
closely resembles the regular DNS protocol. It uses the multicast addresses
224.0.0.252
(IPv4) and ff02::1:3
(IPv6) as well as the high UDP port 5355
.
It is usually also possible to bind to this port on Windows hosts.
NetBIOS-NS
NetBIOS-NS works a little differently. For once it uses broadcast instead of
multicast, uses the UDP port 137 and can only operate over IPv4. Binding to this
port often requires elevated privileges on Linux and on Windows it is usually
occupied by the builtin NetBIOS service (which can be disabled). The protocol
somewhat resembles DNS with regards to the general structure. In fact we managed
to implement NetBIOS-NS using a regular DNS library in
pretender. It only supports a
single query type that is used to resolve IPv4 addresses (type 32 which maps to
the regular DNS type NIMLOC which is currently not used). One of the starkest
differences is the ridiculous query name encoding. As an example this case
insensitive encoding maps WORKGROUP
to FHEPFCELEHFCEPFFFACACACACACACAAA
.
In contrast to DNS, queries also contain so-called suffixes that disclose the context of a query. For example suffixes can indicate that a query was performed to locate a workstation, a file server or even an Exchange server. Analyzing these suffixes as well helps us understand why particular queries happen inside a client’s network and allows us to suggest more suitable mitigations.
Local Name Resolution Spoofing in Action
Enough theory, here’s how local name resolution spoofing looks like in practice
with pretender. Again, the
flag --no-dhcp-dns
is only used to disable the DHCPv6 DNS takeover attack
covered above.
As you can clearly see, if Windows wants to resolve something, it usually tries everything: IPv4, IPv6, mDNS, LLMNR, NetBIOS-NS, A, AAAA, ANY and all possible combinations. Therefore, if privileges do not permit to listen on port 137, for example, it is still likely that all queries can be received and spoofed using other protocols.
When looking at the NetBIOS-NS queries, the query suffixes can also be seen in
practice. Here, the name NETSHARE
is queried with the File Share
suffix
which likely indicates an attempt to access an SMB share, whereas WORKGROUP
is
queried with the Primary DC
suffix.
It is also possible to first only monitor the local name resolution queries seen
on the network and repeat them prior to conducting the attack. A suitable
monitoring mode can be enabled for
pretender with the --dry
flag. If the repeated queries are not answered, it is likely that the names
belong to systems that no longer exist or are currently unavailable. Spoofing
only these queries will likely cause no disruptions leading to happier clients.
Why pretender? Advantages and Usage Scenarios
While the discussed attacks are extremely powerful, especially when combined with NTLM relaying, they are not particularly new. Before developing pretender, we used mitm6 for DHCPv6 DNS takeover and Responder for local name resolution spoofing. However, pretender differs from these projects in numerous ways, grants us additional capabilities and is tailored to improve pentests for us and our clients. In this section we look at the advantages of using pretender in various scenarios.
Design Considerations
In contrast to pretender, the
primary purpose of Responder is to not
only perform the local name resolution spoofing, but to accept subsequent
connections using various protocols and dump the NTLM hashes produced during
authentication attempts. This complicates things significantly when documenting
such attacks in the context of a pentest. We need two different tools
(Responder and possibly also
mitm6) to obtain a machine-in-the-middle
position. When exploiting this position, we again need
Responder but also
Impacket’s ntlmrelayx.py
.
We always try to put ourselves in the position of our clients. In this case, they have to understand a rather complex chain of attacks using a multitude of different protocols they may or may not be familiar with. Confusing the different tools and their respective responsibilities does not help at all. Therefore, we designed pretender with the single purpose to obtain machine-in-the-middle positions combining the techniques of mitm6 and only the name resolution spoofing portion of Responder. This allows us to introduce phases of the attack chain step-by-step alongside the respective tools, resulting in significantly more streamlined reports and live demonstrations.
Cross-Platform Compilation
As mentioned before, all techniques only target machines within the local network segment of the attack system. In practice, this limitation can greatly reduce the effectiveness in some scenarios. For example, the starting point of a network penetration test could be a system in a network segment dedicated only to Linux servers or a client network which is separated from the network segment used by the administrators, whose highly-privileged connections we would like to relay. In these cases, we need to run the attack not on the dedicated attack system but on some compromised system in a more suitable network segment.
mitm6 and
Responder are both written in Python, so
a suitable Python runtime as well as the required libraries need to be present
and we like to avoid installing software on the systems of our clients if
possible. Additionally, mitm6 operates on
raw packet captures which requires a special driver on Windows and
Responder only has an experimental beta
version for Windows that appears to have been abandoned. Also both Python and
the required libraries generally do not support architectures such as mips
which is commonly used for routers.
So let’s imagine we have managed to compromise a mips
-based router that’s
attached to the local network segment used by the domain administrators. With
pretender and a standard Go
installation, we can compile it for the router like this:
$ GOOS=linux GOARCH=mips64 go build .
That’s it – no laborious setup of cross-compiler toolchains, just changing one
or two environment variables. And that’s the same for other architectures such
as ARM or other operating systems such as Windows, macOS or BSD. As a result, we
obtain a single static binary that will work on the compromised system, allowing
us to attack different network segments. We can then instruct
pretender to answer name
queries with the addresses of our dedicated attack system (for example
10.0.0.5
and fe80::5
) in the original network segment as follows:
$ ./pretender -i eth0 --ipv4 10.0.0.5 --ipv6 fe8::5
Compile-Time Configuration
Let’s consider another scenario: Imagine we discovered a remote code execution
vulnerability for a Windows computer in a more promising network segment.
Imagine also that the exploit only allows us to run a binary without any
arguments and we only receive the standard output (not stderr
) after it
terminates. Again, pretender
has you covered. The default value of each command-line configuration flag can
be re-configured at compile time such that we can produce a pre-configured
binary for this special use case:
$ GOOS=windows go build -ldflags '-X "main.vendorInterface=Ethernet 1" -X main.vendorStopAfter=5m -X main.vendorRelayIPv4=10.0.0.10 -X main.vendorNoColor=true -X main.vendorRedirectStderr=true ' .
In this case we have generated a binary that automatically selects the correct
network interface of the compromised computer, answers name queries with our
attack system’s IPv4 address, terminates automatically after five minutes
instead of running continuously and redirects stderr
to stdout
without
coloured output so we can see any errors that may have occurred. This
flexibility lets pretender
shine in a variety of special situations.
We always build pretender with
the vendorStopAfter
variable (which corresponds to --stop-after
) set to make
sure we never unintentionally leave
pretender running longer than
necessary in order to avoid unnecessary disruption in our clients’ networks.
Detailed Logging for Additional Insights
During a pentest, it is always important to understand what is happening as much
as possible. This is especially true for the name resolution spoofing attacks
discussed here. If you know exactly who is requesting which name and possibly
why the name was requested, it is way easier to target specific hosts to
minimise disruption and detection. With detailed information it is also possible
to link the queries to relayed connections in ntlmrelayx.py
in order to
precisely explain to a client how it was possible to relay a connection of a
domain administrator to a high-value target. For this purpose,
pretender aims to provide as
much information as possible about the incoming connections.
While IP addresses are nice, it is even better to also know the corresponding hostnames. One way of obtaining a hostname from an IP in Windows networks is a reverse DNS lookup. However, this only works when the reverse lookup was performed via IPv4, at least in networks that usually don’t use IPv6. When using pretender, especially when performing a DHCPv6 DNS takeover attack, often only the IPv6 address of a target system is revealed. In this case it is not possible to perform a reverse DNS lookup in order to find the corresponding hostname. We also cannot learn anything from the IPv6 address itself, because by default Windows does not use EUI-64 IPv6 addresses generated from the MAC address, but uses random addresses instead. However, pretender is still able to help. Each IPv6 address is matched to the corresponding IPv4 address via the shared MAC address stored in the ARP cache. With the corresponding IPv4 address available, a reverse DNS lookup is likely successful. As a result pretender does not only display the IPv6 address but also a known IPv4 address of the same host as well as the hostname if possible. If this inference is not successful, a lookup in the MAC address vendor table is done. Consider the following example logs:
10:44:35 [mDNS] "win10vm.local" (ANY) queried by 192.168.56.101 (PcsCompu)
10:44:35 [mDNS] "win10vm.local" (ANY) queried by fe80::d422:2ab:8bf4:7381
(192.168.56.101, PcsCompu)
In the example we first see an IPv4-based mDNS query. The reverse lookup was not
successful, so only the network interface vendor PcsCompu
inferred from the
MAC address is displayed. Subsequently, the same query is received via IPv6. The
address was generated by the Windows host itself, but it can be matched to the
same IPv4 address from which the first query originated.
But wait, there is more. Even if the network’s DNS servers do not allow reverse lookups, we can still obtain the hostnames because, by default, Windows includes it in DHCPv6 queries. By caching this information, we can later associate the IPv6 address with its hostname. Via the MAC address we can again also tie IPv4 addresses to said hostname.
Ideally, this information allows us to give detailed explanations to our
clients, such as “your employee’s workstation workstation-jdoe.contoso.com
keeps querying for a share on the non-existing host old-nas
”. This already
gives valuable context even before the root cause of the vulnerability is
investigated.
But pretender does not stop
there. It can also generate a queryable log in JSON format with the --log
option. Imagine that we successfully relay an SMB connection received at
11:06:40 am and we want to know exactly how we obtained the
machine-in-the-middle position for this connection. To achieve this, we can use
jq queries to quickly find all queries
performed around that time:
$ jq 'select(.time | . >= $s and . <= $e)' \
--arg s '2022-06-22T11:06:39' --arg e '2022-06-22T11:06:41' \
< log.json
{
"name": "OLD-NAS",
"query_type": "Workstation Name",
"type": "NetBIOS",
"source": "192.168.56.101",
"source_info": [
"win10vm"
],
"time": "2022-06-22T11:06:39.627408294Z",
"ignored": false
}
In this case, we find a NetBIOS-NS query for the host old-nas
with matching
timestamp which is likely responsible.
Privileges
When operating pretender on compromised systems, we may or may not have elevated privileges. As mentioned before, mitm6 operates on raw packet captures which requires a driver and thus administrative privileges on Windows. However, pretender does not require special privileges on Windows if the required ports are not in use by another process and not blocked by a firewall. For example, this allows us to run the DHCPv6 DNS takeover attack (albeit without router advertisements) over RDP on a Windows system with a low-privileged domain user.
Acknowledgements
We hope you enjoyed this introduction to pretender and find it as useful as we do. We’d like to thank Dirk-jan Mollema for the inspiration, as well as the creators and maintainers of the excellent Go libraries for DNS, DHCP and NDP. Also take a look at our other open-source pentesting tool monsoon, a fast and versatile HTTP enumerator which we have introduced in another blog post.
Addendum: Kerberos Relaying
After the initial release of pretender we’ve been working on a powerful new feature: Support for Kerberos relaying. With the new release, pretender can now work together with Dirk-jan Mollema’s krbrelayx to relay Kerberos authentication to targets such as the Active Directory Certificate Services HTTP endpoint. Have a look at at the Google Project Zero blog post for the theoretical foundation of this attack as well as Dirk-jan Mollema’s blog post for a concrete implementation of such an attack.
Like mitm6, the role of
pretender in the context of
this attack is to make a client connect to
krbrelayx by answering SOA queries.
These replies can contain an arbitrary hostname that is configurable via
--soa-hostname
which will be used in the SPN of the Kerberos service ticket
sent to krbrelayx. This ticket can then
be relayed to a service on the host with the chosen hostname. One of the main
takeaways is that cross-protocol attacks are possible, because even though the
ticket contains the service class DNS, it is likely that all or most services on
a host share the same encryption key.
If you haven’t yet, give Kerberos relaying a try! It is really a very powerful attack which shows that disabling NTLM authentication is not always enough.