- Blog, Red Teaming
Inside the NAC Pi
July 5, 2024
The journey of how we’ve built our own all-in-one device to bypass NAC (including 802.1X)
Network access control (short: “NAC”) are measures for protecting physical and wireless networks. They act as gatekeepers for clients that want to connect to the network. Without these measures, connecting a device to the network is as easy as plugging it into any Ethernet port and … you’re in!
MAC filtering simply hardcodes the MAC addresses that are allowed to connect to the network. While this is easy to bypass with MAC spoofing, it still requires an attacker to take extra steps to access the network.
NAC based on 802.1X on the other hand forces the connecting client to authenticate itself cryptographically against a trusted server. The server then decides whether the client is allowed to access the network. This is quite a complex thing so let’s look at how it works first.
Basics of 802.1X NAC
One way to implement NAC is by using a protocol called IEEE 802.1X, which is a port-based network access control (PNAC). This means that the boundary is the network switch port (the so-called “authenticator”) to which the client (the so-called “supplicant”) is directly connected. Packets originating from a client not able to authenticate must not pass this port. The authenticator in turn talks to the so-called “authentication server” via the RADIUS protocol (or any other AAA protocol), and the server eventually decides whether the supplicant is allowed to enter the network. This is where the actual authentication procedure takes place.
802.1X defines the encapsulation of the Extensible Authentication Protocol (EAP) over wired and 802.11 wireless networks, which is known as “EAP over LAN”, or “EAPOL” for short. This is the protocol used between the supplicant and the authenticator, the latter of which transmits the authentication data of the former. EAPOL is transmitted on OSI layer 2, so the supplicant doesn’t need an IP address to use it. The authenticator then takes the EAP proportion in the EAPOL packet and transmits it to the authentication server via RADIUS.
EAP packets being wrapped in EAPOL and RADIUS:
Consultant
- Blog, Red Teaming
- July 5, 2024
So far so good. We now have an authentication layer in our network, so what’s next? Well … nothing. 802.1X in its original form only adds authentication to the otherwise unprotected network. There are extensions to it, but all of them are optional and not considered here. See for more information: “Possible mitigations scenarios in which the NAC Pi doesn’t work”.
Reasons to bypass MAC filtering and 802.1X NAC
The most obvious reason to bypass network protections for an attacker of any kind is, surprise, surprise, gaining access to the network. An attacker being allowed at the switch port often means that they may obtain a network IP address via DHCP, which allows him to communicate with all devices on his LAN segment and often also beyond it. This opens many doors for attacks like ARP spoofing, packet sniffing, denial of service and so on – which applies to all reachable devices in the network.
But there is more. An assumption is made especially in 802.1X-secured networks which is not made in unprotected networks: The clients in the network can be trusted, as they have authenticated themselves cryptographically, right? Well yes, but this statement is only true until some adversary bypasses this authentication. This would allow the attacker to spy on and act like a trusted client within the trusted network. Additionally, being physically “on the wire” between the supplicant and the authenticator might even allow for advanced techniques like relaying attacks, for example using the tool ntlmrelayx (https://github.com/fortra/impacket/blob/master/examples/ntlmrelayx.py).
A little bit of theory and why NAC bypasses are possible
But how exactly can we bypass the 802.1X NAC authentication process? Breaking it cryptographically is not a reasonable option due to state-of-the-art cryptography being used (at least most of the time). The other option is to bypass it physically by letting the supplicant do its authentication and somehow using the link in its already authenticated state.
There is a tool that does exactly this: Sitting in between a supplicant and an authenticator with the goal to let the supplicant authenticate itself and then injecting traffic into the authenticated link to effectively bypass 802.1X. The tool is called silentbridge and can be found on GitHub (https://github.com/s0lst1c3/silentbridge). It is meant to be run on a Linux device and creates a transparent Linux network bridge connecting supplicant, authenticator, and a side-channel interface (from where the traffic is injected).
In theory, we then just wait for the supplicant to authenticate itself to the network over the transparent bridge by forwarding the supplicant’s EAPOL frames to the authenticator. For security reasons, this is prevented by default in standard Linux network bridges (actually to prevent exactly attacks like this). In earlier versions of the Linux kernel, it was necessary to apply a kernel patch to reactivate EAPOL forwarding; today this can be done via the virtual sys file system. With the bridge configured to forward EAPOL frames, the attacker only needs to wait for the supplicant to complete the authentication process.
Port-based NAC effectively uses the MAC address to identify the supplicant, meaning that all packets originating from this address are allowed to pass the NAC, which is also true for MAC filtering. The link state is also monitored to enforce reauthentication in the event of a link termination. So, the only thing we need to do now is to use IP- and MAC-based source NAT to rewrite the source address of all packets originating from our silentbridge device or its side-channel interface to match the supplicant. However, port-based NAC is strict enough that even a single packet with the wrong MAC address on an authorized port immediately closes the port again. We must therefore always ensure that packets are only sent once this NATing is fully set up. This is called “start dark” in silentbridge terminology and has the drawback that once the rogue device is introduced onto the link, the connection is interrupted for some seconds. Consequently, we need to make sure that the supplicant can reauthenticate itself in order to keep the link in an authenticated state.
The cool thing about this is that the actual technical implementation of the authentication process is completely irrelevant for this approach, as we do not intervene in it directly. We simply let the supplicant do its thing. We’ve tested the authentication protocols “PEAP-MSCHAPv2” and “EAP-TLS” from 802.1X-2004 as well as networks that aren’t using 802.1X at all but might use MAC filtering. This means that this procedure is applicable to both all variants of 802.1X-2004 and MAC filtering. Nice!
All these things and a few more to keep the bridge invisible in the network are done by silentbridge. But we are still missing some vital information: In order to perform the source NAT, silentbridge needs to know which source address to use to overwrite the ones from of the outgoing packets. Consequently, we must determine the IP and MAC address of the supplicant. Silentbridge also sets static ARP entries to reach the switch, on which the authenticator runs, and the networks gateway so our own packets can be routed correctly. This is done manually in order to stay silent in the “start dark” stage. The authenticator ARP entry is required for EAPOL packets since they must reach the switch directly on layer 2. This means, we also need to acquire the MAC addresses of our authenticator and gateway before starting the actual attack.
Finding these is a simple but time-consuming process if done manually, especially if you think of a red-teaming scenario, where you don’t have all the time in the world to spin up Wireshark first. We need a better approach for this…
Sticking it all together: Genesis of the NAC Pi
Ok, so we now have some knowledge of MAC filtering, 802.1x and a tool to bypass them, but we still need the required network information. Now, where do we start? We build a hardware appliance for it!
We have chosen the Raspberry Pi 4 B for this purpose. It’s small, can be powered with USB-C and is more than capable to handle packet bridging. We use USB-Ethernet adapters to add more physical interfaces to the Raspberry Pi. Software-wise, we have developed multiple scripts that instrument silentbridge to perform the 802.1X bypass fully automated by doing the following steps:
- Detecting connected devices to confirm that bridging and injection via the side-channel interface is possible.
- Creating the transparent bridge with silentbridge without adding the side-channel yet.
- Preventing locally generated packets from leaving the NAC Pi (“start dark”) but leaving the bridging intact.
- Running a tcpdump on the bridge to collect the supplicant IP and MAC address as well as the switch and gateway MAC address (this is not as easy as it sounds).
- Adding the side-channel to the bridge and applying all iptables and arptables rules required for the source NAT.
- “Lifting the radio silence” created by the “start dark” action in step 3.
This eventually leads to the fact that we can now inject traffic onto the authenticated link via the side-channel and from the NAC Pi itself by impersonating the authenticated supplicant!
But we haven’t yet defined what the side-channel is. This is where the NAC Pi shines: This can be literally any device that can be represented as a Linux network interface. Currently, two possible side-channels are supported by our NAC Pi: a physical LAN device (like an attacker notebook) connected directly to it or a device in a OpenVPN tunnel that was created via LTE to allow for remote injection.
Traffic injection methods: LAN and VPN over LTE
In LAN mode, the NAC Pi’s side-channel is a physical device connected to one of the USB-Ethernet adapters. This is the simplest way of injecting traffic but requires a dedicated device on site with custom network settings, as the side-channel network is a special subnet only used in LAN mode. For advanced scenarios, up to 13 additional devices can be connected to this subnet, e.g., by using a switch between the NAC Pi and its side-channel devices.
In LTE mode, the side-channel is a tunnel device. It belongs to an OpenVPN connection to one of our servers intended for NAC Pi traffic injection. The connection is established via an LTE modem connected to the NAC Pi. This way, the connection is not dropped by some corporate firewall that prevents outgoing VPN connections. One of our pentesters can now connect to the OpenVPN server from any device within our IP address range, routing all of its traffic to it. The server then routes the incoming traffic from the consultant back into the VPN tunnel established from the NAC Pi. It eventually reaches the tunnel device defined as the silentbridge side-channel.
We also wanted to be able to deploy multiple NAC Pi’s at the same time. Each NAC Pi has been assigned an instance number that determines to which private OpenVPN network it connects. In addition, several OpenVPN configuration files have been created for the consultants, which are also assigned to a specific private network. The OpenVPN server routes the respective packets to the correct NAC Pi using policy-based routing, as can be seen in Figure 3. This way, the consultant can virtually choose the NAC Pi into which he wants to inject traffic remotely. In a large network, for example, several network segments or clients can be infiltrated simultaneously. Furthermore, several consultants can connect to one NAC Pi using the same OpenVPN configuration file to work in parallel, similar to LAN mode.
In both modes, the traffic-injecting side-channel device can target the NAC Pi directly via the subnet’s first available host address. This is required for DNS redirecting (more on this later) and to connect to it via SSH.
The side-channel mode in which the NAC Pi operates is determined at boot time depending on the devices connected to it. It uses udev with a combination of USB port number rules and USB vendor/product IDs to determine whether an LTE modem and if or how many USB-Ethernet adapters are connected. The latter is required so the NAC Pi operator knows to which USB port it needs to attach the adapters. The same udev rules then create named interfaces from those adapters which are in turn given to silentbridge. The boot script also automatically establishes the VPN connection if an LTE modem is detected.
A little fact in passing: For all networks created by the NAC Pi, we use addresses within the reserved carrier grade NAT subnet 100.64.0.0/10. But why not use the private IPv4 ranges instead? We observed that using a network like 10.0.0.0/8 runs the risk of colliding with addresses within the target network. First, we’ve used IPv4 link-local addresses (the ones you get when you don’t have a DHCP server in your network, but your interface is configured to use one), but they had a major drawback: On Linux, those addresses are not routable because, well, they are link-local. This meant that we couldn’t use Linux devices as side-channel devices. So, we switched to the carrier grade NAT subnet. It is used nearly nowhere in networks meant for usual clients while it also has the characteristics of being globally routable (which we do not need in our case, because we only use it for NAT). So, a perfect match for us!
But wait … there is more!
It would be a shame having a hardware appliance that bypasses NACs but does nothing more than this. Over time, our NAC Pi has evolved into a complex network attack framework supplying advanced DNS features to the operator, allowing for easy on-link credential sniffing and even providing an endpoint for Wi-Fi keyloggers. But one thing at a time.
DNS redirection service
One problem we had was the fact that the attacker’s device connected to the side-channel interface wasn’t a fully-fledged network participant. The device has not been assigned a network-valid IP address, and no DHCP traffic reaches it, as the client only sees its connection up to the NAC Pi. Everything else is invisible bridging and routing. This means that the attacker is not aware of any of the network’s information, including the internal DNS server. For some scenarios, this is bad: Let’s assume the attacker wants to access an internal web service. Sure, he can just enter the IP address to begin with, but what if the page’s JavaScript loads additional resources or data from let’s say “https://company.local/api”? Translating all calls with the corresponding IP addresses is not a trivial task and takes time. To solve this problem, our NAC Pi has a built-in DNS redirection service: While analyzing the bridge traffic to find out the required addresses for silentbridge, it also tries to find out where most of the DNS requests are sent to – and assumes that this is the network’s primary DNS server. The NAC Pi then creates iptables rules to redirect all DNS packets destined for it to this server. The attacker can now specify the NAC Pi as his DNS server (whose address is known to him), which will then redirect all DNS traffic to the detected DNS server, allowing the attacker to resolve internal hostnames, as if he would actually know the network’s DNS server. Nice!
Credential sniffing with BruteShark
We are physically sitting on a link between an authenticated supplicant and the network. It would be such a wasted potential to not take a quick look at what the supplicant is transmitting into the network. In Windows environments, the domain-joined clients usually transmit Kerberos packets over the wire. We use the CLI version of BruteShark (https://github.com/odedshimon/BruteShark) to extract those Kerberos tickets from the bridge, which we can then offline-brute-force with Hashcat. BruteShark is also capable of extracting plaintext credentials from unencrypted protocols like SMTP and IMAP, but also from HTTP URLs, headers, and POST payloads, which can be seen in Figure 4.
BruteSharkCli is embedded into nacpi-ctl, which we will cover later. This makes launching it as easy as executing “nacpi-ctl sniff-passwords” from the command line.
Wi-Fi access point for Wi-Fi keyloggers
In one of our penetration tests, where our NAC Pi was used for the first time, we also wanted to place some hardware keyloggers on some clients. These keyloggers had the ability to stream keystrokes over UDP, acting as Wi-Fi clients. The SSID and credentials for an access point needed to be configured before placing them in front of the target keyboard. We thought “hey, it would be nice if we would just bring in our own Wi-Fi network,” because we didn’t have the credentials to connect to the customer network yet. But we already wanted to place some NAC Pis into the network … which are effectively Raspberry Pis … which has a Wi-Fi card on-board … and also a dedicated Internet uplink over LTE. What a coincidence!
So, we used hostapd, a Linux tool to turn Wi-Fi cards into Wi-Fi access points. We set the same SSID and passphrase on all NAC Pis, effectively creating a fake mesh network: Clients connecting to this network simply use the nearest access point. To prevent signal interference, each NAC Pi uses a different Wi-Fi channel calculated via its VPN instance number set at installation. Now we have an access point for all our keyloggers. But we still need a UDP server to which the keyloggers can stream their keystrokes …
We decided to use a centralized server for this instead of collecting the keystrokes on each NAC Pi separately: our VPN gateway. First, the NAC Pi is configured as the UDP target for the keyloggers, because we already know the address (it’s simply the NAC Pi’s address inside the Wi-Fi network). The NAC Pi then takes these UDP packets and redirects them through the VPN tunnel to the VPN gateway. This also makes sure that we do not transfer potentially sensitive credentials unencrypted. On the VPN gateway, there is a small Go application that simply writes all incoming UDP traffic into a file. And this is the file where we will find our keystrokes!
To be able to differentiate from which keylogger the keystrokes are coming, they can identify themselves by setting different port numbers for the target UDP server: Sending packets to port 40001 identifies the keylogger as “keylogger 1”, 40002 as “keylogger 2” and so on. The Go application on the VPN server listens on a range of different ports and writes incoming traffic to different files, respectively. The server’s firewall is configured to only allow connections to the UDP endpoint from within the NAC Pi VPN tunnels.
Since the Wi-Fi access point uses the LTE uplink, we can also use it to ask our ISP about the remaining data volume, e.g., by connecting a phone to the access point. We also enabled the SSH listener for the Wi-Fi interface to be able to access the NAC Pi even when the side-channel devices cannot be accessed or something else goes wrong.
Controlling behavior with nacpi-ctl
That’s a hell of a lot of features. Most of them work with the default configuration – but keep in mind that this is a hacking tool! Hackers are not exactly known to interact with software and hardware as intended. That’s why we introduced nacpi-ctl – a command line tool to configure and use specific features of our NAC Pi at runtime.
nacpi-ctl, allows us to investigate its overall status (see Figure 6), to control the mapping of the peripherals, to control the Wi-Fi access point, to check LTE mobile data volume, to supply the attack script with network information if known, to skip the packet sniffing at boot time, or to launch any of the features like the beforementioned credential sniffing. We also added a function to clear information collected during a red teaming reliably, to protect the customer’s privacy when reusing the NAC Pi – just like a factory reset.
nacpi-ctl was built with Python using the Typer library (https://github.com/tiangolo/typer). It’s really “just” a stateless tool to keep track of udev rules, systemd services and some other configuration files. But it definitely made interaction with the NAC Pi much easier.
Streamlining the NAC Pi and gateway deployment processes
To simplify the deployment of our NAC Pi, all changes to the attack script, nacpi-ctl and the configuration files are kept in an internal GitLab repository and are bundled in a single deployment script. Everything you need to do in order to deploy a NAC Pi is to flash Raspbian OS Lite on an SD card, plug it into a Raspberry Pi 4 B, clone the repository on there and run the deployment script. It asks you for the VPN instance number and that’s it! After a reboot the NAC Pi is ready to go.
To simplify deployment even further, we have been providing pre-build images for each NAC Pi instance in recent versions, which can be flashed directly onto an SD card. The manual execution of the deployment script is therefore also obsolete. We have forked the pi-gen tool (https://github.com/RPi-Distro/pi-gen), which is also used to build the official Raspberry Pi images and adapted it so that the deployment script is also executed during the build process. This also makes it easier for us to control and determine which Raspberry Pi OS version the NAC Pi software will ultimately run on.
To manage the OpenVPN server for the NAC Pis in LTE mode, we use Ansible to deploy and maintain it. It also ensures a correct firewall configuration so that NAC Pis are allowed to connect from anywhere, but traffic injection is only possible from within the cirosec IP address ranges. The Ansible script also manages the UDP keylogger endpoints and its firewall rules on the server.
Possible mitigations scenarios in which the NAC Pi doesn’t work
Yes, you can protect yourself from this rogue device!
There is an EAPOL extension called IEEE 802.1AE (“MACsec”), which adds confidentiality and integrity to the network security cocktail. This means, we not only need to bypass authentication but encryption and key exchange mechanisms, too. Currently, this is not supported by our NAC Pi appliance. MACsec is used in 802.1X-2010, so any variant of 802.1X higher than the 2004 version is not vulnerable to be attacked with the NAC Pi. The problem, however, is that MACsec is currently not supported by most operating systems including Windows. Although this functionality can be retrofitted to client devices by using tools like Cisco AnyConnect with Network Access Manager (NAM), it is far from being applicable to all network devices, such as printers. They will still have to use fallback mechanisms like 802.1X-2004 to be able to use the network. Therefore, printers will probably remain a good target for us for the time being.
However, even though our NAC Pi currently can’t bypass MACsec, silentbridge itself actually can under certain circumstances: Using a “rogue gateway attack”, an attacker can effectively steal EAP credentials by diverting the supplicant’s traffic to a rogue authenticator through mechanical switching at the right time. This requires two physical A/B splitters, which function like rail switches: They can be programmatically set to either connect to the rogue device (the NAC Pi in this case) or to connect to each other directly, effectively bypassing the NAC Pi. Connecting the splitters with the rogue device allows the latter to temporarily act as the authenticator to steal the EAP credentials transmitted by the supplicant. After setting the splitters to bypass the rogue device again, the stolen credentials are then utilized by the rogue device to perform authentication on its own, without having to rely on the supplicant. This attack can even be improved by using a “bait ‘n’ switch attack”, where the supplicant is additionally forced to disconnect and reauthenticate itself. While reauthenticating, the credentials can be stolen in a more reliable way. Nevertheless, both variants only work with weak EAP methods, like EAP-MD5, and with the splitters as mentioned – thus requiring knowledge in electrics assembly and wiring. But maybe this complex attack will also find its way into our NAC Pi one day…
It is also always a good idea to adhere to other security recommendations for networks. Strong network segmentation does not help against NAC attacks, but it does make it much more difficult for an attacker to actually attack systems within the network or obtain valuable information from them. We’ve also seen customers of us using VPN within the company’s network to force clients to authenticate on the application layer, even when in the office. Firewall rules, for example, were then taken from the Active Directory based on the logged-in user and are only valid within the cryptographically secure VPN tunnel, rendering network attacks without having access to a valid user useless. Zero Trust is another such approach, which has a similar claim to isolation and micro segmentation and offers a good second-level protection, too. Networks with such configurations make our work much more difficult (in a good sense): The NAC Pi is still usable, but in most cases, it no longer yields any added value to us.
We also encountered problems in environments where there is simply no wired LAN available and only Wi-Fi was in use. In this case, the connection is already encrypted, and the NAC Pi cannot be deployed as a man-in-the-middle device, besides it not having support for Wi-Fi interfaces at all. Good physical security has also often prevented the placement of the NAC Pi: access restrictions or enforced supervision by an employee for rooms with computers and especially for server rooms are invaluable, especially to prevent network attacks like this. So, keep in mind: The most secure network is one in which nothing is accessible by default.
We currently do not plan to publish our source code for the NAC Pi. The defense line has fallen for every one of our customers, who have been convinced by their NAC solution, where we have deployed the NAC Pi. We do not consider ourselves responsible for passing on such a tool to the public as long as this is the case. We know that there are other public NAC bypassing tools, but we are not aware of any that can cause damage outside the local network boundaries. The addition of the VPN gateway and LTE mode of the NAC Pi makes it far more dangerous than the existing tools we are aware of. The built-in, automatable man-in-the-middle functions, such as the extraction of Kerberos credentials and the ability to transfer observed keystrokes to a central server through the Wi-Fi access point, are also not part of the tools known to us. Through this article, however, we want to draw attention to the fact that tools like this one exist and clarify that you should never be complacent despite a seemingly good NAC solution.
Further blog articles
Loader Dev. 2 – Dynamically resolving functions
March 10, 2024 – In this post, we discuss dynamically resolving functions, which help to avoid static detections based on the functions imported by our executable.
Author: Kolja Grassmann
Loader Dev. 1 – Basics
February 10, 2024 – This is the first post in a series of posts that will cover the development of a loader for evading AV and EDR solutions.
Author: Kolja Grassmann