docs: spec for deauth UX, evil portal tab, monitor capture fixes
This commit is contained in:
@@ -0,0 +1,116 @@
|
|||||||
|
# Mark VIII — Deauth UX, Evil Portal, Monitor Capture Fixes
|
||||||
|
|
||||||
|
Date: 2026-08-23
|
||||||
|
Status: approved (user confirmed design in session)
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
1. Make deauth obvious: "Deauth All Clients" from Recon's AP focus sidebar, "Deauth All"
|
||||||
|
on the Deauth Targeting card — both gated by an engagement-scope confirmation.
|
||||||
|
2. New top-level **Evil Portal** tab compatible with Hak5 EvilPortalNano portals
|
||||||
|
(kleo/evilportals layout): import zips, serve pages to victims, capture credentials,
|
||||||
|
DNS-hijack delivery.
|
||||||
|
3. OpenAP gains an "Evil Portal" card: dropdown of imported templates, greyed out when
|
||||||
|
none exist; activating an OpenAP with a template selected starts the portal.
|
||||||
|
4. Fix Monitor Capture never appearing to work.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- No PHP interpreter; the backend shims only the trivial PHP patterns stock portals use.
|
||||||
|
- No TLS interception (HTTPS requests are not redirected).
|
||||||
|
- `.enable`/`.disable` portal scripts are stored but **not executed** (divergence from
|
||||||
|
stock EvilPortalNano, documented here deliberately).
|
||||||
|
|
||||||
|
## 1. Bulk deauth endpoint
|
||||||
|
|
||||||
|
`POST /api/attacks/deauth/bulk` body `{targets: [{bssid, client, channel?}, ...]}`
|
||||||
|
(max 32). Shared helper `_deauth_one(bssid, client, channel)` extracted from
|
||||||
|
`h_attacks_deauth`; per-target results returned `{results: [...], sent, failed}`.
|
||||||
|
Uses the existing band→inject-iface logic and `PINEAPPLE_DEAUTH_CLIENT`.
|
||||||
|
|
||||||
|
### Recon sidebar
|
||||||
|
|
||||||
|
In `renderFocus()` (www/js/views.js), when the focused AP has confirmed clients, add a
|
||||||
|
danger button **"Deauth All Clients"**. Clicking shows
|
||||||
|
`confirm("Deauthenticate N client(s) of <SSID>? Confirm this target is IN SCOPE for your engagement.")`,
|
||||||
|
then posts one bulk call.
|
||||||
|
|
||||||
|
### Deauth Targeting card
|
||||||
|
|
||||||
|
`deauthPanel()` gains a danger **"Deauth All"** button that bulk-deauths every device in
|
||||||
|
the current result list against the selected AP, behind the same scope confirmation.
|
||||||
|
|
||||||
|
## 2. Evil Portal
|
||||||
|
|
||||||
|
### Storage & import
|
||||||
|
|
||||||
|
- Root: `/mmc/mk8/portals/<name>/` (`[A-Za-z0-9._-]{1,64}` names, mk8_profiles rules).
|
||||||
|
- `POST /api/portals/import` `{data: <base64 zip>}` (≤10 MB decoded). Zip-slip guarded
|
||||||
|
(reject absolute paths and `..`). If every entry shares a single top-level directory,
|
||||||
|
that directory becomes the portal root. `index.php` must exist at the root.
|
||||||
|
- Other endpoints: `GET /api/portals`, `DELETE /api/portals/{name}`,
|
||||||
|
`POST /api/portals/{name}/activate|deactivate`, `GET /api/portals/captures`,
|
||||||
|
`DELETE /api/portals/captures`, `GET /api/portals/{name}/logs` (Download),
|
||||||
|
`GET /api/portals/{name}/download` (zip of the portal folder).
|
||||||
|
|
||||||
|
### Serving engine (port 80)
|
||||||
|
|
||||||
|
Second listener thread in server.py, own connection handler (no auth, no same-origin —
|
||||||
|
victims are unauthenticated). Admin UI on :8080 untouched.
|
||||||
|
|
||||||
|
- **GET anything** → active portal:
|
||||||
|
- Path resolves inside the portal dir (`_safe_join`); missing paths fall back to
|
||||||
|
`index.php`; asset files served raw with the standard mime map.
|
||||||
|
- `index.php` rendered through the PHP shim.
|
||||||
|
- **PHP shim**: replaces `getClientMac($_SERVER['REMOTE_ADDR'])` /
|
||||||
|
`getClientHostName(...)` with values resolved from DHCP leases for the requester IP,
|
||||||
|
`$_SERVER['REMOTE_ADDR']` with the requester IP, `$destination` with the request URL;
|
||||||
|
strips all other `<?php ... ?>` / `<?= ... ?>` blocks.
|
||||||
|
- **POST anything** → credential capture: parse urlencoded fields, enrich with client
|
||||||
|
ip/mac/hostname, append:
|
||||||
|
- `<portal>/.logs` in the exact MyPortal.php text format (stock-tool compatible), and
|
||||||
|
- `/mmc/mk8/portals/captures.jsonl` (one JSON object per line) for the UI.
|
||||||
|
Response: simple authorization-success HTML page.
|
||||||
|
|
||||||
|
### Delivery: DNS hijack
|
||||||
|
|
||||||
|
Activation writes `dhcp.@dnsmasq[0].address='/#/<lan-ip>'` (UCI), commits, restarts
|
||||||
|
dnsmasq; deactivation deletes the option and restarts. LAN IP discovered via
|
||||||
|
`ip -4 addr show br-lan` (env-overridable `PAGER_LAN_IFACE`, fallback 172.16.52.1).
|
||||||
|
Active portal name persists at `/mmc/mk8/portals/.active`; on service start the hijack
|
||||||
|
is re-applied best-effort if a portal was left active.
|
||||||
|
|
||||||
|
### Tab UI
|
||||||
|
|
||||||
|
Rail entry + route `#/evilportal` + `views.evilportal`: Active Portal status card,
|
||||||
|
Templates card (list with Activate/Stop/Delete/Download, Import via zip file input →
|
||||||
|
base64), Captured Credentials table (time, portal, fields) with Clear and Download.
|
||||||
|
|
||||||
|
## 3. OpenAP Evil Portal card
|
||||||
|
|
||||||
|
`attackLauncher('open', {..., portal: true})`: card with a template `<select>`
|
||||||
|
(populated from `GET /api/portals`), disabled with hint text when no templates exist.
|
||||||
|
Deploy body carries `portal: <name>`; `h_attacks_deploy` activates it after a successful
|
||||||
|
open-AP deploy; `h_attacks_stop` for kind `open` deactivates any active portal. Status
|
||||||
|
card shows the active binding.
|
||||||
|
|
||||||
|
## 4. Monitor Capture fix
|
||||||
|
|
||||||
|
Root cause: `views.js:1314` polls `/api/attacks/capture {action:'status'}` without
|
||||||
|
`iface`; the backend defaults to `wlan0mon`, so a live `wlan1mon` capture flips back to
|
||||||
|
"Not capturing" within one 5 s poll.
|
||||||
|
|
||||||
|
- Backend `status` with no `iface`: evaluates both pidfiles and reports whichever
|
||||||
|
capture is actually running (else the default). Start failures now `mkdir -p` the pcap
|
||||||
|
dir first and tee tcpdump stderr to `/tmp/mk8_capture_<iface>.log`, surfaced in the
|
||||||
|
502 detail.
|
||||||
|
- Frontend: when a status/start response reports a running capture, adopt its iface for
|
||||||
|
subsequent Stop clicks.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- New `tests/test_portals.py`: import validation (bad name, zip-slip, oversize, missing
|
||||||
|
index.php, nested top-dir flattening), PHP shim substitutions, capture log formats.
|
||||||
|
- Extend `tests/test_attacks.py`: bulk deauth validation/looping (mocked device_run),
|
||||||
|
capture status dual-iface resolution.
|
||||||
|
- Existing suite must stay green (run per-file as documented in README).
|
||||||
Reference in New Issue
Block a user