Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
wp2shell-poc — Analysis and end-to-end implementation of the patched wordpress RCE vulnerability - CVE-2026-60137 and CVE-2026-63030 | Kitploit
Tools/GitHubGitHub/colere-sys/wp2shell-poc
Exploit FrameworksVulnerability AnalysisWeb Application ExploitationCTFPenetration TestingLearning & EducationRed TeamingPayload Development
GitHubcolere-sys/wp2shell-poc

wp2shell-poc

Analysis and end-to-end implementation of the patched wordpress RCE vulnerability - CVE-2026-60137 and CVE-2026-63030

View Repository
21 month agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

plot

How does this work at all

plot

How this PoC differs from the public wp2shell exploits

all pocs weaponize the same two bugs - the REST batch route confusion (CVE-2026-63030) and the author__not_in SQL injection (CVE-2026-60137) - with the same double-nested batch shape. What differs between them is the RCE path chosen, the environment preconditions, and the safety defaults. This document states, concretely, where the implementation in this repository sits in that landscape.

The short version

  1. Works behind a persistent object cache. The public UNION-based PoCs use the populated-base injection form: the full-chain implementation this lineage shares false-negatives there, and the single-file unifier lists "no persistent object cache" as an explicit precondition. This repo's killed-base form keeps the UNION channel - and therefore the whole pre-auth RCE bridge - alive on exactly those hosts (the common managed-WordPress setup). See §1.
  2. Safe to run against production by default. check sends no SQL payload unless asked; all traffic can carry an attribution tag; everything the shell command writes to the target is removed automatically afterwards. See §3.

Comparison table

[1] Timing/blind only as a read channel; the UNION fake-post primitive exists inside the bridge but is not exposed as an extraction oracle. [2] The naive availability probe (0) UNION SELECT …) is silently dropped during object-cache hydration, available() returns false, and the whole pre-auth bridge aborts - see §1. [3] The project's own README lists "no persistent object cache (Redis/Memcached)" under Preconditions. [4] Public variant mirrored at Sploitus (link below): blind read plus an INTO OUTFILE dropper as the RCE step, using a per_page=-1 categories carrier. [5] The OUTFILE RCE path does not depend on fake-post rendering, so object caches do not block it - the MySQL FILE privilege and a shared writable directory do. Managed hosting almost never grants FILE to the WordPress DB user, and secure_file_priv is commonly set.

1. The object-cache problem (the real differentiator)

The UNION fake-post primitive depends on how WP_Query returns rows:

  • Full-row mode - the SQL returns whole wp_posts rows; a UNION-injected row becomes a WP_Post directly. The forgery renders.
  • Split (ID-only) mode - the SQL returns IDs only, and each ID is hydrated afterwards through the (persistent) object cache / database. The forged row's ID does not exist, so hydration silently drops it. No error, no forged post.

On hosts with a persistent object cache, a populated base result set pushes WP_Query into split mode. The stock probe used by the public PoCs -

root@kitploit:~
0) UNION SELECT <forged row> -- -
  • leaves the base set populated (post_author NOT IN (0) matches every row), so behind an object cache the forged row evaporates: the availability probe false-negatives, available() returns false, and the entire pre-auth bridge is reported "dead" on a host that is in fact fully exploitable. The public unifier documents the same boundary by listing "no persistent object cache" as a hard precondition.

This repo empties the base set instead:

root@kitploit:~
1) AND 1=0 UNION ALL SELECT <forged row> -- -

With zero base rows, the forged row is the only row; the query stays in full-row mode; no hydration lookup ever runs. One injected keyword (AND 1=0) is the entire difference between "UNION channel dead" and "full pre-auth RCE" on object-cached hosts - which are the majority of managed WordPress production environments. The diagnosis, the probe matrix (per_page × injection form).

Scope note: the blind/timing read channel is not object-cache-sensitive (counting rows in SQL does not involve fake-post hydration), so every PoC's blind read works everywhere. What the object cache kills in the other PoCs is specifically the UNION-dependent part: in-band extraction and the SQLi-to-admin bridge.

A second, related lesson documented in the case study: when both channels work, treat the in-band UNION read as authoritative - the production timing oracle produced bit flips under jitter on a value the in-band read settled unambiguously.

2. RCE path choice

Three pre-auth RCE paths exist across the public PoCs:

This repo implements the bridge: it needs no database privileges beyond what WordPress already has, works when DB and web tiers share nothing, and leaves no file behind for the FILE-privilege path to depend on. The trade-off is complexity - the bridge is a seven-row poisoned post graph - which is exactly where the §1 object-cache false negative used to hide the path.

3. Safety defaults for authorized use

Built for running against production systems under authorization, not just labs:

  • check is non-destructive by default - passive fingerprint plus a benign marker batch; no SQL payload is sent unless --confirm-sqli is given. After patching, the disappearing marker triplet doubles as fix validation.
  • Attribution tagging - --user-agent on every command so all exploit traffic is identifiable in logs (an engagement rule-of-thumb the public tools don't default to).
  • Automatic cleanup - the webshell is token-locked under a randomized path and removes itself; a bridge-created administrator is deleted afterwards with its content reassigned to the borrowed admin account. Failure of cleanup is reported loudly, not swallowed.
  • Request accounting - every command prints how many requests it sent.

4. What this repo does not claim

  • No new vulnerability. Both bugs are the publicly disclosed CVEs; the double-nested batch shape, the author_exclude → author__not_in sink, the fake-WP_Post UNION primitive, and the customizer-bridge concept are all public techniques (lineage acknowledged below).
  • No new exploitation primitive. The delta over the public landscape is: the killed-base object-cache fix with production evidence, the production-safe defaults, and the detection documentation - robustness and operational safety, not novelty of technique.
  • IoC strings are arbitrary. Login prefixes, plugin slugs, shell markers, and User-Agent values differ across every variant and per run;

References

  • Icex0/wp2shell-poc - full-chain implementation this repo's lineage shares - https://github.com/Icex0/wp2shell-poc
  • sergiointel/wp2shell-poc - first public PoC; origin of the crack-free admin-creation technique - https://github.com/sergiointel/wp2shell-poc
  • 0xsha/wp2shell - single-file unifier of six public PoCs, with Docker labs and a version×DB matrix (documents the object-cache precondition) - https://github.com/0xsha/wp2shell
  • OUTFILE variant (blind read + INTO OUTFILE dropper), mirrored at Sploitus - https://sploitus.com/exploit?id=7CD079AD-E27B-5C54-A696-60635BFDB241
  • Curated list of public PoCs and checkers (for defenders) - https://www.cyberkendra.com/2026/07/wp2shell-guide.html
  • GHSA-ff9f-jf42-662q / GHSA-fpp7-x2x2-2mjf; WordPress 7.0.2 release announcement - see README.md references.
Download Tool
CapabilityThis repoIcex0/wp2shell-pocsergiointel/wp2shell-poc0xsha/wp2shellOUTFILE variant [4]
Pre-auth blind/timing SQLi readyesyesyes (timing)yesyes
In-band UNION read (1 request/value)yesyes- [1]- [1]-
Error-based read (EXTRACTVALUE)yesyes---
UNION channel survives persistent object cacheyes (killed-base)no - probe false-negatives [2]not documentedno - documented precondition [3]n/a [5]
Crack-free pre-auth RCEyes (SQLi-to-admin bridge)yes (same bridge)yes (bridge origin)yes (same bridge)yes, via INTO OUTFILE [5]
Extra preconditions for RCEnone beyond default installnone (on non-object-cache hosts)none (same)none (same)MySQL FILE priv + web-writable path shared with mysqld
Non-destructive check / patch validationyes (marker triplet; no payload by default)yesnoyes (block_cannot_read)yes (marker batch)
Attribution/User-Agent taggingyes, on all commandsnonotransport flagno
Automatic cleanup (webshell + generated admin)yesyesnot documentedtoken-gated webshell onlydropper removed [5]
Blue-team detection guideyes, from a production runnonolab matrix insteadmitigation notes
Dependenciesstdlib onlystdlib onlysingle filestdlib only, single filePython ≥3.10 package
PathUsed byExtra preconditions
SQLi-to-admin bridge (forge oEmbed/changeset/nav rows → POST /wp/v2/users → login → plugin upload)this repo, sergiointel (origin), Icex0, 0xshanone beyond a default install
INTO OUTFILE dropper (write a PHP file via SQLi, fetch it for a shell)OUTFILE variant [4]MySQL FILE privilege, secure_file_priv allowing it, and a directory writable by mysqld and served by the web server
Hash recovery → crack → login (dump user_pass, crack offline, then plugin upload)all (as fallback)the bcrypt hash must actually crack ($wp$2y$, hashcat -m 35500) - slow, often never