Sitemap

Ligolo-ng for OSCP and beyond

7 min readOct 15, 2025

--

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.

Press enter or click to view image in full size

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 seconds

As 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.

Press enter or click to view image in full size

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 80

With 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 -selfcert
Press enter or click to view image in full size

Now I’ll run the agent on Webserver1 with the -connect and -ignore-cert arguments.

Press enter or click to view image in full size

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 ligolo1

Once 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 ligolo1

With 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/32
Press enter or click to view image in full size

All 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.

Press enter or click to view image in full size

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/24

After 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:6666
Press enter or click to view image in full size

Notice 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.

Press enter or click to view image in full size

Ok we landed in as www-data user, as expected and we’ve got a foothold on Webserver2.

Press enter or click to view image in full size

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.

Press enter or click to view image in full size

As usual we’ll download it with curl. Note we’re using Webserver1’s IP, not Kali’s.

Press enter or click to view image in full size

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.

Press enter or click to view image in full size

Now we run the agent on Webserver2, pointing it to the listener on Webserver1’s internal IP (10.0.1.10:9999).

Press enter or click to view image in full size

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/24
Press enter or click to view image in full size

To 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.

Press enter or click to view image in full size

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.

Press enter or click to view image in full size
Press enter or click to view image in full size

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..

--

--

NullEsc
NullEsc

Written by NullEsc

4× Microsoft Azure certified (AZ‑104, AZ‑305, AZ‑500, SC‑300)