Ligolo-ng for OSCP and beyond
Ligolo‑ng is a tunneling tool built for speed and simplicity that makes pivoting into internal networks simple and efficient. Unlike traditional SOCKS‑based tools, it leverages a TUN interface to route traffic seamlessly, making lateral movement and service access feel almost native.
In this short demonstration I’ll use Ligolo‑ng for tunneling, pivoting, catching a reverse shell on the attacking machine originating from a host deep inside the internal network, and transferring files to and from a remote target. I will walk you through scenarios you are very likely to encounter while working through labs on platforms like HackTheBox, Proving Grounds, and others. These techniques are essential to understand if you are preparing for certifications such as the OSCP or similar.
In this lab we will go through the following scenarios:
1. Access the Apache service bound to 127.0.0.1 and establish the first pivot into the internal subnet
2. Trigger RCE on Webserver2 to get a reverse shell on Kali.
3. Deploy another agent on Webserver2 to establish the second pivot.
4. Transfer files to and from PC‑A.
Of course, needless to say all of this could also be achieved using SSH, Chisel and other tools but we are focusing on ligolo-ng, and everything we do today can be approached in multiple ways with Ligolo‑ng itself.
Our initial foothold is Webserver1, which sits on the edge of the internal network. Running an Nmap scan from Kali shows two open ports: FTP and MySQL.
kali@kali:~$ sudo nmap -T 4 -p- -n 172.16.50.10
Starting Nmap 7.95 ( https://nmap.org ) at 2025-10-14 10:36 AEDT
Nmap scan report for 172.16.50.10
Host is up (0.00043s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
21/tcp open ftp
3306/tcp open mysql
MAC Address: 00:0C:29:33:28:8B (VMware)
Nmap done: 1 IP address (1 host up) scanned in 2.59 secondsAs we can see, only port 21 and 3306 are exposed. However, Apache is running locally on 127.0.0.1, which means it is not visible to us from the outside network.
This is where Ligolo‑ng comes into play. We’ll use it to interact with the locally bound Apache service from Kali. Let’s move the agent to Webserver1, which we can do by starting a Python HTTP server on Kali and fetching it with curl from the other side.
python3 -m http.server 80With the Ligolo agent in place, start the proxy on Kali. For this lab I’ll use port 9999 instead of the default, just to make it clear. We’ll run it with sudo so the proxy has the necessary permissions to create interfaces later on.
sudo ligolo-proxy -laddr 0.0.0.0:9999 -selfcertNow I’ll run the agent on Webserver1 with the -connect and -ignore-cert arguments.
At this point the agent has successfully connected back, and we now have an active session in the proxy.
Next, inside the Ligolo proxy running on Kali, I’ll select the session so Ligolo knows which agent we’re working with, then create a TUN interface named ligolo1 since this is the first pivot point.
interface_create --name ligolo1Once the interface is created, I’ll run tunnel_start to start the tunneling, which also brings the ligolo1 interface from down to up.
tunnel_start --tun ligolo1With the tunnel established, we can now expose the localhost service through the agent and access it from the attacker machine by adding a special route to 240.0.0.1/32. This is cool because it works without any port forwarding and even in environments where SSH isn’t available.
add_route --name ligolo1 --route 240.0.0.1/32All we have to do now is browse to http://240.0.0.1:8080 from our Kali machine to reach the internal web server running on port 8080.
Before adding any routes, let’s confirm Kali’s current access.
As you can see, we cannot reach the internal hosts yet. Now I’ll add a route in the proxy interface to make the first internal subnet accessible from Kali.
add_route --name ligolo1 --route 10.0.1.0/24After adding the route, Kali can finally talk to the first internal subnet. We can test this by pinging the internal IP of Webserver1 and the second server.
But here is the catch! Right now we can reach webserver2 but it has no path back to kali. That means if we trigger a reverse shell from Webserver2, it won’t reach our attacker machine.
Get NullEsc’s stories in your inbox
Join Medium for free to get updates from this writer.
To solve this, we set up a Ligolo listener on the first agent. This listener accepts connections on port 1111 and forwards them through the tunnel to our Kali machine on port 6666.
listener_add --addr 0.0.0.0:1111 --to 172.16.50.50:6666Notice that reverse shell isn’t pointing to Kali’s IP, it’s actually targeting Webserver1. That’s because we set up the Ligolo listener there. So when Webserver2 runs the shell, it connects to Webserver1 on port 1111, and Ligolo forwards that traffic through the tunnel to Kali’s port 6666.
Ok we landed in as www-data user, as expected and we’ve got a foothold on Webserver2.
Our goal now is to drop another agent and pivot deeper into the network, since Webserver2 is connected to the second subnet. To do this, we’ll transfer and run another agent on Webserver2.
I’ll add a new listener to forward port 8000 from Webserver1 to Kali. This way when Webserver2 makes a request on port 8000, it will be passed through Webserver1 and reach Kali’s Python HTTP server to download the agent.
As usual we’ll download it with curl. Note we’re using Webserver1’s IP, not Kali’s.
With the Ligolo agent in place, I’ll add a new listener on the first agent so Webserver2’s agent can connect back through the proxy.
Now we run the agent on Webserver2, pointing it to the listener on Webserver1’s internal IP (10.0.1.10:9999).
Once the second agent pops up in the ligolo proxy interface, we switch to its session, create a new TUN interface, and add a route for the 10.0.2.0/24 network that this host is connected to. Which means we can now reach the second subnet.
interface_create --name ligolo2
tunnel_start --tun ligolo2
add_route --name ligolo2 --route 10.0.2.0/24To confirm the pivot is working, we can simply ping PC‑A from our attacker box.
So now we can reach this new host and we can download files from it.
Let’s try it in the other direction. With session 2 (Webserver2) selected, we add a listener that points back to Kali, where a simple Python HTTP server is hosting the file. From PC‑A the file can then be fetched through Webserver2.
And with that, this lab comes to an end. We explored how we accessed an internal website that is not exposed to the outside network, then created our first pivot point and caught a reverse shell from a host inside the internal network that doesn’t have direct access to our attacking machine. We then moved on to creating a second pivot, exposed another host, and transferred files to and from it.
Thank you for reading.
Stay curious, stay kind, and keep learning. Until the next pivot..







