RedTeam Pentesting GmbH - werde eine*r von uns

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.

DHCPv6 DNS Takeover Attack Overview

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.