IPv6 in OT: The Forgotten Attack Surface
Why IPv6 is lurking in your OT network - dual stack defaults, SLAAC chaos, and why 'just disable it' might be your best security control
You might wonder if I’m being serious with this post. In OT where sometimes IP stacks are completely missing (e.g. PROFIBUS, EtherCAT, IEC 101, IO-Link), why should you care about IPv6?
IPv6 is probably already running in your OT network. Not because you planned it, configured it, or even know about it. It’s there because modern operating systems enable dual-stack by default, and that Windows HMI or Linux Box in your cabinet you deployed last year is quietly chatting IPv6 on the side.
The security implications? An unmanaged attack surface with exposed services, dual-stack configs that enable DoS attacks via DHCPv6, and potentially vulnerable IPv6 stacks waiting to be exploited.
The Dual-Stack Default Dilemma
Here’s what most OT engineers overlook: virtually every modern system runs dual-stack by default. Your Windows Server 2019 SCADA host? IPv6 enabled. That Linux-based HMI? IPv6 enabled. Even on some modern PLCs. But surely it doesn’t matter if there’s no IPv6 routing? Wrong. Even without routing, IPv6 creates an attack surface in the local segment and if your network is flat and not microsegmented, these segments can be quite large. Because IPv6 has higher priority than IPv4 in most operating systems when a system can reach a target via both protocols, it prefers IPv6. This means an attacker who controls IPv6 on your local segment essentially controls your traffic. Most OT environments I’ve assessed have IPv6 enabled on some of the devices. While hardening standards like CIS Benchmarks recommend disabling it, enforcement is challenging across diverse OT assets—and some devices simply don’t provide the option. Most of the time there’s no DHCPv6 server, no router advertisements from legitimate sources—just devices quietly auto-configuring themselves via SLAAC (Stateless Address Autoconfiguration).
You’re also exposing a complex part of the IP stack to the network. There have been some wild vulnerabilities in IPv6 implementations:
- CVE-2024-38063 - Windows IPv6 stack RCE (CVSS 9.8) via malformed packets
- CVE-2020-16898 - “Bad Neighbor” Windows TCP/IP RCE via ICMPv6 Router Advertisement
- CVE-2023-4807 - FreeBSD IPv6 fragment reassembly buffer overflow
- CVE-2021-22901 - curl TELNET stack overflow with IPv6 addresses
Plus, you can achieve MITM attacks on the same segment and bypass ARP spoofing detection, since most network equipment requires a separate feature called RA Guard to detect IPv6-based attacks.
For IPv6 assessment, your arsenal should include:
- THC-IPv6 - The Swiss Army knife of IPv6 testing (install with
sudo apt install thc-ipv6
) - Nmap with IPv6 scripts - For discovery and enumeration
- mitm6 - Specifically for Active Directory attacks via IPv6
Understanding IPv6 Addresses
Before we go further, let’s decode what these addresses actually mean. Take fe80::d057:74ff:fe5d:f73f/64
:
- fe80:: - Link-local prefix (like 169.254.x.x in IPv4)
- d057:74ff:fe5d:f73f - Interface identifier (often derived from the interface MAC)
- /64 - Subnet size (standard for IPv6 LANs)
The address types you’ll encounter in OT:
- Loopback (::1/128): Like 127.0.0.1 in IPv4
- Unspecified (::/128): Like 0.0.0.0 in IPv4
- Link-local (fe80::/10): Every IPv6 interface has one, no configuration needed
- Unique Local (fc00::/7): Like RFC1918 private addresses
- fd00::/8 - Locally assigned (what you’ll typically use)
- fc00::/8 - Centrally assigned (rarely used)
- Global Unicast (2000::/3): Internet-routable
- Multicast (ff00::/8): Replaces broadcast
- IPv4-mapped (::ffff:0:0/96): For dual-stack, e.g., ::ffff:192.168.1.1
How can you tell if an address is MAC-derived or random? Simple: look for the ff:fe pattern in the middle. If it’s there, it’s derived from a MAC address using the modified EUI-64 format. No pattern? It’s likely using Privacy Extensions (RFC 4941) which generate random, temporary addresses that change periodically to prevent tracking. Windows, macOS, and many Linux distributions enable Privacy Extensions by default, creating multiple IPv6 addresses per interface - one stable for incoming connections and temporary ones for outgoing.
Example ULA address: fd17:625c:f037:2:a00:27ff:fe60:7052/64
- fd - Locally assigned ULA
- 17:625c:f037 - 40-bit randomly generated Global ID (unique to your site)
- 2 - Subnet ID (this is subnet 2)
- a00:27ff:fe60:7052 - Interface ID (derived from MAC 08:00:27:60:70:52)
Some OT technologies are using IPv6: Smart meters using G3-PLC communication run IPv6 mesh networks over power lines. Building automation systems leverage 6LoWPAN for wireless sensor networks. These systems were designed IPv6-first, making it mandatory rather than optional.
Finding the Hidden IPv6 Assets
So you want to know what’s actually running IPv6 in your OT network? Good luck with active scanning—the address space is so vast that brute force approaches are useless. A single /64 subnet has 18,446,744,073,709,551,616 possible addresses. Your scanner will finish sometime after the heat death of the universe, probably shortly after the last Siemens SIMATIC S5 gets decommissioned.
Passive Listening for IPv6 Broadcasts
The smartest approach? Listen passively for IPv6 traffic like ICMPv6 broadcasts and DHCPv6 requests.
1
2
3
4
5
6
7
8
9
10
11
# Listen for all IPv6 traffic on the local segment
tcpdump -i eth0 -nn ip6
# Passive detection with tcpdump
tcpdump -i eth0 -n 'ip6 and not host ::1' -w ipv6-traffic.pcap
# Monitor for Router Advertisements
tcpdump -i eth0 -n 'icmp6 and ip6[40] == 134'
# Capture Neighbor Discovery traffic
tcpdump -i eth0 -n 'icmp6 and (ip6[40] == 135 or ip6[40] == 136)'
Devices announce themselves through Duplicate Address Detection (DAD) when they boot, sending Neighbor Solicitations from the unspecified address (::). This is gold for passive discovery.
Mining Protocols for IPv6 Addresses
Some protocols leak configuration data including IPv6 addresses. LLDP (Link Layer Discovery Protocol) is particularly chatty. Vendors like Hirschmann and Cisco have their own discovery protocols that often leak IPv6 addresses when configured.
Windows RPC Enumeration
On Windows systems with RPC access, you can enumerate IPv6 addresses:
1
2
3
4
5
# Using rpcclient to enumerate network interfaces
rpcclient -U 'DOMAIN\user%password' $TARGET_IP -c 'netsharegetinfo'
# Or use WMI queries for network adapter configuration
wmic /node:$TARGET_IP nicconfig get ipaddress
MAC to IPv6 Calculation
If you know the MAC address (from ARP tables, switch CAM tables, or asset inventory), you can calculate potential IPv6 addresses. Most devices don’t use privacy extensions, so they generate predictable EUI-64 addresses based on their MAC:
1
2
# Python one-liner: MAC 08:00:27:60:70:52 -> fe80::0a00:27ff:fe60:7052
python -c "m='08:00:27:60:70:52'.replace(':',''); print(f'fe80::{int(m[0:2],16)^2:02x}{m[2:4]}:{m[4:6]}ff:fe{m[6:8]}:{m[8:10]}{m[10:12]}')"
Converting IPv4 to IPv6 Targets
1
2
3
4
5
# THC-IPv6's inverse neighbor discovery (requires MAC address)
sudo atk6-inverse_lookup6 eth0 <mac_address>
# Map IPv4 hosts to potential IPv6 addresses
nmap -6 -n --script targets-ipv6-map4to6 --script-args newtargets,targets-ipv6-map4to6.IPv4Hosts={192.168.1.0/24},targets-ipv6-subnet={fe80::/64} -sL
Active Scanning
Active scanning for IPv6 requires different strategies than IPv4 due to the vast address space. Here are the techniques that actually work:
Multicast Discovery
IPv6 uses multicast addresses instead of broadcast. Understanding these addresses helps target specific device types:
Common IPv6 Multicast Addresses:
ff02::1
- All nodes on local link (every IPv6 device)ff02::2
- All routersff02::c
- SSDP (Simple Service Discovery Protocol)ff02::fb
- mDNSv6ff02::1:2
- All DHCP servers and relay agentsff02::1:ff00:0
- Solicited-node address
For a complete list, see IANA IPv6 Multicast Addresses.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Best discovery method - tries multiple multicast addresses
sudo nmap -6 --script ipv6-multicast-mld-list
# Ping all nodes and extract unique IPs
ping -6 -I eth0 ff02::1 -c3 | grep "bytes from" | cut -d " " -f4 | cut -d "%" -f1 | sort -u
# Alternative for older systems
ping6 -I eth0 ff02::1
# Ping all routers (discover default gateways)
ping -6 -I eth0 ff02::2
# THC-IPv6's comprehensive alive scanning (requires sudo)
sudo atk6-alive6 eth0
# With increased wait time for slow responders
sudo atk6-alive6 -W -i eth0
# Scan for IPv6 node information
sudo nmap -6 --script ipv6-node-info.nse -n --packet-trace fe80::854f:891c:a724:fad6
DNS Reverse Lookups
Some systems leak IPv6 addresses through DNS queries if they self-register in DNS (e.g., via AD). Using reverse lookups on IPv4 addresses sometimes reveals dual-stack configurations:
1
2
3
4
5
6
7
8
9
10
11
# Query for AAAA records (IPv6 addresses)
dig AAAA server.domain.local
# Reverse lookup for IPv4 that might reveal IPv6
host 192.168.1.100
# DNS zone transfer (if allowed) to find IPv6 records
dig @192.168.1.53 company.local AXFR | grep AAAA
# Use dnsrecon for comprehensive enumeration
dnsrecon -d company.local -t std,rvl,srv
Protocol-Specific Discovery
Different tools for different goals:
1
2
3
4
5
6
7
8
# THC-IPv6 alive scanning (moved to Multicast Discovery section)
sudo atk6-alive6 eth0
# Find all devices via Neighbor Discovery (moved to Multicast Discovery section)
nmap -6 --script ipv6-multicast-mld-list
# Detect sniffing hosts on the network
sudo atk6-detect_sniffer6 eth0
Working with IPv6
Once you’ve identified IPv6 assets, connecting to them requires understanding the syntax differences from IPv4. Standard tools work with IPv6, but link-local addresses need interface specification:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Linux - View IPv6 addresses on all interfaces
ip -6 addr show
# Add IPv6 address to interface
ip -6 addr add 2001:db8::1/64 dev eth0
# Show IPv6 routes
ip -6 route show
# Add static IPv6 route
ip -6 route add 2001:db8::/32 via fe80::1 dev eth0
# Show IPv6 neighbors (like ARP for IPv4)
ip -6 neigh show
# Ping link-local address (requires %interface)
ping fe80::c641:1eff:fe83:199f%eth0
# HTTP with IPv6 (brackets required for addresses)
curl -6 "http://[fe80::a00:27ff:fed3:72ef%eth1]:8000/"
curl "http://[2001:db8::1]:8080/api/status"
# SSH to IPv6 address
ssh user@fe80::a00:27ff:fed3:72ef%eth0
ssh -6 user@2001:db8::1
# Ncat with IPv6 (nc may not support IPv6)
ncat -6 fe80::1%eth0 502
ncat -6 2001:db8::1 80
# Nmap IPv6 scanning
nmap -6 fe80::1%eth0
nmap -6 2001:db8::/64
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Windows PowerShell - View IPv6 configuration
Get-NetIPAddress -AddressFamily IPv6
# View IPv6 addresses with more detail
Get-NetAdapter | Get-NetIPAddress -AddressFamily IPv6
# Add IPv6 address to interface
New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress 2001:db8::1 -PrefixLength 64
# Show IPv6 routes
Get-NetRoute -AddressFamily IPv6
# Add static IPv6 route
New-NetRoute -DestinationPrefix "2001:db8::/32" -NextHop "fe80::1" -InterfaceAlias "Ethernet"
# Show IPv6 neighbor cache
Get-NetNeighbor -AddressFamily IPv6
# Test IPv6 connectivity
Test-NetConnection -ComputerName 2001:db8::1
Test-NetConnection -ComputerName fe80::1%12 # %12 is interface index
# HTTP requests with IPv6 (PowerShell 5.1+)
Invoke-WebRequest -Uri "http://[2001:db8::1]:8080/api/status"
Invoke-RestMethod -Uri "http://[fe80::a00:27ff:fe14:8307%12]:8000/"
# Using curl.exe on Windows 10/11
curl.exe -6 "http://[2001:db8::1]:8080/api/status"
# View interface indexes for link-local addresses
Get-NetAdapter | Select-Object Name, InterfaceIndex, Status
# Resolve IPv6 address
Resolve-DnsName server.domain.local -Type AAAA
# Flush IPv6 neighbor cache
Remove-NetNeighbor -AddressFamily IPv6 -Confirm:$false
Bridging IPv4 Tools to IPv6 Targets
Many tools were designed for IPv4 and haven’t been updated for IPv6. Engineering workstations, protocol analyzers and other software often can’t connect to IPv6 addresses directly. The workaround? Create local IPv4-to-IPv6 proxies that make IPv6 targets appear as IPv4 services. This works most of the time, except for services that do broadcast discovery and do not allow a direct IPv4 connection.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Install proxy tools
sudo apt install 6tunnel socat -y
# socat - TCP relay from IPv4 to IPv6 (note the quotes!)
socat TCP4-LISTEN:8080,fork,reuseaddr "TCP6:[2001:db8::1]:80"
# For link-local addresses with interface
socat TCP4-LISTEN:502,fork,reuseaddr "TCP6:[fe80::854f:891c:a724:fad6]:502,interface=eth0"
# 6tunnel - lightweight IPv6 to IPv4 proxy
# Basic syntax: 6tunnel [-d] [-v] localport ipv6address remoteport
6tunnel -d -v 4444 fe80::854f:891c:a724:fad6%eth0 22
# For global addresses
6tunnel 8080 2001:db8::1 80
# proxychains with IPv6 SOCKS proxy
# Edit /etc/proxychains.conf: socks5 ::1 1080
proxychains4 ./legacy-tool target.com
# SSH tunnel for IPv6 access (note brackets and quotes!)
ssh user@jumphost -L "4444:[fe80::854f:891c:a724:fad6%eth0]:22"
# For global addresses
ssh user@jumphost -L "5002:[2001:db8::1]:502"
# Now connect to localhost:4444 or localhost:5002 with IPv4 tools
Defense against IPv6
The best defense against IPv6 attacks in OT? Disable IPv6 if you’re not using it. However, Microsoft does not recommend disabling it for Windows without proper consideration, because some features will no longer work. According to Microsoft (https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-ipv6-in-windows), these features break:
- HomeGroup (file sharing)
- DirectAccess (remote access)
- Remote Assistance
- Windows Mail
- Exchange Server functionality
- Active Directory replication (Server 2019/2022)
- … ?
Interestingly, CIS Benchmarks for L2 recommend to turn it off anyway, prioritizing security over functionality. If you are unsure and cannot test whether your systems work without IPv6, you have some alternatives:
Windows Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Option 1: Prefer IPv4 over IPv6
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" /v DisabledComponents /t REG_DWORD /d 32 /f
# Option 2: Block IPv6 at the firewall instead of disabling it
# This keeps the protocol active but prevents external IPv6 traffic
New-NetFirewallRule -DisplayName "Block IPv6" -Direction Inbound -Protocol ICMPv6 -Action Block
New-NetFirewallRule -DisplayName "Block IPv6" -Direction Inbound -Protocol TCP -LocalPort Any -RemoteAddress ::/0 -Action Block
New-NetFirewallRule -DisplayName "Block IPv6" -Direction Inbound -Protocol UDP -LocalPort Any -RemoteAddress ::/0 -Action Block
# Option 3: Disable it
# Disable IPv6 on all adapters
Set-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6 -Enabled $false
# Or via registry (requires reboot)
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" `
-Name "DisabledComponents" -Value 0xFF -PropertyType DWord
Linux Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Prefer IPv4 over IPv6 (without disabling IPv6)
echo "precedence ::ffff:0:0/96 100" >> /etc/gai.conf
# Alternative: Modify preference via sysctl
echo "net.ipv6.conf.all.disable_ipv6 = 0" >> /etc/sysctl.conf
echo "net.ipv6.conf.default.disable_ipv6 = 0" >> /etc/sysctl.conf
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding = 0" >> /etc/sysctl.conf
# Using nftables
nft add table ip6 filter
nft add chain ip6 filter input { type filter hook input priority 0 \; policy drop \; }
nft add chain ip6 filter forward { type filter hook forward priority 0 \; policy drop \; }
nft add chain ip6 filter output { type filter hook output priority 0 \; policy drop \; }
nft add rule ip6 filter input iif lo accept
nft add rule ip6 filter output oif lo accept
# Disable IPv6 completely
sysctl -w net.ipv6.conf.all.disable_ipv6=1
sysctl -w net.ipv6.conf.default.disable_ipv6=1
# Permanent disable
echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
Conclusion
IPv6 represents an unmanaged attack surface in OT environments. While the protocol itself offers legitimate use cases, its default-enabled status on modern systems creates security risks that are often overlooked in industrial networks. Recommendation: Check your networks for IPv6, implement appropriate mitigations, and add IPv6 to your security assessment checklist.
Bonus: Detecting IPv6 Sniffers
Here’s something that shouldn’t work but does: detecting promiscuous mode hosts on IPv6 networks. The atk6-detect_sniffer6
tool exploits how systems in promiscuous mode handle malformed packets:
1
2
3
4
5
6
sudo atk6-detect_sniffer6 eth0
# Example output:
# Sending sniffer detection packets to ff02::1
# Sniffing host detected: fe80::be24:11ff:fe9e:8906
# Sniffing host detected: fe80::e343:960a:3dec:8d4f
How it works:
- Sends special ICMP6 echo requests to multicast address
ff02::1
- Includes both normal packets and packets with invalid destination headers (
NXT_INVALID
) - Normal systems drop packets with invalid headers
- Systems in promiscuous mode often process them anyway and respond
- The tool detects responses containing its signature “thcsniff”
This shouldn’t work because promiscuous mode is supposed to be passive, but many network stacks process malformed packets differently when sniffing, creating this detection opportunity.
Bonus Bonus : IPv6 as a Covert Channel
A colleague and I once joked about using cryptography to generate addresses and hide assets in the vast IPv6 space. Cryptographically Generated Addresses (CGAs) use this principle legitimately—they generate IPv6 addresses from cryptographic hashes of public keys as defined in RFC 3972. Only someone with the private key can prove ownership of that address. But here’s where it gets interesting: you could create entire networks of addresses that appear random but follow a cryptographic sequence only you can generate.