Skip to content
KitploitKITPLOIT
उपकरणएक्सप्लॉइटब्लॉग
Log in
जमा करें
उपकरणएक्सप्लॉइटब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
wp2shell-lab — Non-destructive detector + Docker lab for wp2shell (CVE-2026-63030 REST /batch/v1 route confusion + CVE-2026-60137 author__not_in SQLi) in WordPress core 6.9.0-6.9.4 / 7.0.0-7.0.1 | Kitploit
उपकरण/GitHubGitHub/dinosn/wp2shell-lab
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationLabs & Practice
GitHubdinosn/wp2shell-lab

wp2shell-lab

Non-destructive detector + Docker lab for wp2shell (CVE-2026-63030 REST /batch/v1 route confusion + CVE-2026-60137 author__not_in SQLi) in WordPress core 6.9.0-6.9.4 / 7.0.0-7.0.1

रिपॉजिटरी देखें
5417212 महीने पहलेKitploit द्वारा समीक्षित

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें
अनुरोधित भाषा में सामग्री उपलब्ध नहीं है। अंग्रेज़ी संस्करण दिखाया जा रहा है।

wp2shell lab & detector + pre-auth RCE PoC

A self-contained lab, non-destructive detector, and full pre-auth RCE proof-of-concept for wp2shell — the pre-authentication vulnerability chain in WordPress core:

CVEComponentClassCVSS
CVE-2026-60137WP_Query::author__not_inSQL injection (CWE-89)9.1
CVE-2026-63030REST /batch/v1 route confusioninterpretation conflict (CWE-436) → chains to RCE7.5

Affected: WordPress core 6.9.0–6.9.4 and 7.0.0–7.0.1 (the SQLi sink alone also affects 6.8.0–6.8.5). Fixed in 6.8.6 / 6.9.5 / 7.0.2. Reported by Adam Kues (Assetnote / Searchlight Cyber); SQLi also credited to TF1T, dtro, haongo. Stock-default RCE chain (oEmbed → changeset → re-entry) by Mustafa Can İPEKÇİ (nukedx).

Update first. WordPress shipped forced auto-updates for this. This repo exists to help you verify your own estate is patched and to understand the bug — not to attack anyone. See SECURITY.md.


What it actually is

The always-true primitive is an unauthenticated, no-plugin, stock-core SQL injection giving full database read (admin password hashes, everything in wp_options/wp_users). That alone earns the 9.1 and immediate patching.

The RCE is real and works on stock-default WordPress — no FILE privilege, no persistent object cache, no plugins, no misconfigurations required. The chain uses the read-only SQLi as a row-forgery primitive (UNION ALL SELECT injects fake wp_posts rows), then leverages WordPress's own content-rendering pipeline to convert those forged rows into real database writes via oEmbed caching. From there, changeset elevation and re-entrant parse_request run in admin context, creating a new administrator account — all from a single unauthenticated HTTP request.

The full chain (no credentials, single entry point POST /?rest_route=/batch/v1)

1. Route confusion    — double-nested batch desyncs $matches/$validation so a GET
                        /wp/v2/widgets runs under posts::get_items() (public), reaching
                        WP_Query's author__not_in with attacker-controlled input.

2. Row forgery        — author__not_in is string-concatenated into SQL;
                        "1) AND 1=0 UNION ALL SELECT <23 cols> -- -" injects fake
                        WP_Post rows. per_page=-1 bypasses split_the_query (WP_Query
                        treats -1 as "no limit" → empty $limits → split=false →
                        full SELECT wp_posts.* → UNION columns match).

3. oEmbed write       — forged posts carry [embed]<self-url>[/embed]; rendering via
                        context=view makes WordPress cache real oembed_cache posts in
                        the DB (turns read-only SQLi into writes with predictable IDs).

4. Elevation+re-entry — a forged customize_changeset (user_id = real admin) plus a
                        forged post_type=request row with parent loops drives an
                        in-process re-entrant parse_request in admin context.

5. Admin creation     — POST /wp/v2/users in the same batch passes
                        current_user_can('create_users') → new administrator.

6. RCE                — login → plugin webshell upload → command execution → cleanup.

Load-bearing gadgets (why the chain holds)

The novelty is the composition, not any single bug — the individual gadgets are legitimate WordPress behaviours. Naming them (per Adam Kues's writeup) makes the forged-row graph in exploit() readable and flags what to re-audit after the entry points were patched:

  • Cache/DB reconciliation — when an in-memory cached post disagrees with its DB row, WordPress reconciles via wp_update_post() and prefers the in-memory post_type/post_status, letting an oembed_cache row be re-typed into a real post/customize_changeset.
  • Cycle-detection preserving post_content — the wp_insert_post_parent filter walks the parent chain; on a detected cycle it calls a second wp_update_post() that fixes the parent without overriding post_content. That is the critical link: it lets the forged changeset's malicious post_content survive. (This is why the forged rows use self-/mutual-parent loops.)
  • Hook replay via parse_request — publishing a post fires do_action("{$status}_{$type}"); a forged row with post_status=parse / post_type=request triggers parse_request, re-running the batch pipeline while the changeset's assumed-admin identity (wp_set_current_user) still holds.

These gadgets remain present in patched WordPress — only the two entry points (batch desync + author__not_in scalar bypass) were closed. Any new primitive that forges in-memory post cache or writes an oembed_cache row would re-enable the identical admin-takeover tail.

Render-time write primitives

All four render-time write primitives confirmed live against 7.0.1 — each forged as an unauthenticated post, rendered via the batch confusion, and the resulting DB write verified by blind SQLi:

PrimitiveTrigger markupSinkPredicted identifierVerified
oembed[embed]<url>[/embed]wp_posts row (oembed_cache)post_name = md5(url+attrs)post ID created
rsswp:rss {feedURL}wp_options site-transient_site_transient_feed_<md5(url)>option_id, 5192 B cached
navigationwp:navigationwp_posts row (wp_navigation)post_name = 'navigation' (fixed)ID created, slug navigation
calendarwp:calendarwp_optionswp_calendar_block_has_published_postsoption_id, value '1'

What each result proves:

  • oembed — the reference primitive: a fresh wp_posts row with an attacker-predicted slug (md5(url+serialize(attrs))) and a real auto-increment ID. This is the only one that gives you multiple, on-demand, attacker-named post rows — which is why the RCE chain uses it to back the forged changeset/request graph.
  • rss — the strongest general write: the key _site_transient_feed_<md5(url)> is fully predictable, and the stored bytes are the feed body the attacker's URL serves — i.e., attacker controls both key and value. It's an wp_options write (no post ID), so it's an option-poisoning primitive rather than a drop-in for the changeset backing.
  • navigation — the only other unauth "render creates a real post row" path. It's single-shot (skips if any published wp_navigation exists) with a fixed slug, so it can back at most one forged object, unlike oEmbed's N rows.
  • calendar — confirms the render→update_option path fires unauthenticated, but the option name and its '1'/'0' value are fixed/DB-derived, so it's a "write happens" demonstration with no attacker control over key or value.

Earlier conditional chains (superseded by the stock-default chain above)

  • INTO OUTFILE webshell: requires the WordPress DB user to hold global FILE privilege + a web-served secure_file_priv + the drop readable by the web user. On normal/managed hosts none of that holds.
  • SimplePie → WP_HTML_Token POP: call_user_func('wp_insert_user', user_data_array) — requires gc_enabled()=false + valid HMAC (wp_hash of exact serialized bytes, needs wp-config.php secrets).

Quick start

Requirements: Docker + Docker Compose v2, Python 3.8+ (stdlib only), make, curl.

टूल डाउनलोड करें