# 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 ? 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//` (`[A-Za-z0-9._-]{1,64}` names, mk8_profiles rules). - `POST /api/portals/import` `{data: }` (≤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 `` / `` blocks. - **POST anything** → credential capture: parse urlencoded fields, enrich with client ip/mac/hostname, append: - `/.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='/#/'` (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 `