#!/usr/bin/env python3 """HackThisSite Steganography 4 ("I am being hexed!") — extract the appended payload. The GIF is a single-frame GIF89a image. After the normal image data it carries a standard GIF trailer byte 0x3B, and then 64 extra bytes that are not part of the image stream: the ASCII characters '0' and '1'. Grouped into bytes they spell an 8-character lowercase password. """ import sys
defextract(path): data = open(path, "rb").read() trailer = data.rfind(b"\x3b") # last GIF trailer byte (0x3B = ';') trailing = data[trailer + 1:] # everything after the trailer ifnotset(trailing) <= set(b"01"): raise SystemExit("trailing data is not a '0'/'1' bit string: %r" % trailing[:32]) bits = trailing.decode("ascii") iflen(bits) % 8: raise SystemExit("bit string length %d is not a multiple of 8" % len(bits)) out = bytes(int(bits[i:i + 8], 2) for i inrange(0, len(bits), 8)) return trailer, bits, out
#!/usr/bin/env python3 """HackThisSite Steganography 4 (playit) live solver. The level ships a single-frame GIF89a. Its normal container ends with the GIF trailer byte 0x3B, but 64 more bytes follow: the ASCII characters '0' and '1'. Grouped eight at a time they spell the 8-character password. Submission follows the playit contract: read the level page for a fresh formkey (it changes on every load), then POST formkey/lvl/pass to template.php with the level page as Referer. Without the Referer the server answers ``Invalid Referer`` and the attempt does not count. The session cookie is read from the HTS_COOKIE environment variable and never written to disk. Usage: export HTS_COOKIE='HackThisSite=...' cd <hts-workspace>/challenges/hts-stego/4 && uv run python solve.py """ import os import re import urllib.parse import urllib.request
UA = ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/131.0.0.0 Safari/537.36") BASE = "https://www.hackthissite.org" LEVEL = BASE + "/missions/playit/stego/4/" SUBMIT = BASE + "/missions/stego/template.php" GIF = BASE + "/missions/stego/lvl/stego4.gif" NEXT = "/missions/playit/stego/5/" COOKIE = os.environ["HTS_COOKIE"] USER = os.environ.get("HTS_USER", "")
defprofile_levels(): page = fetch("%s/user/view/%s/" % (BASE, USER)).decode("utf-8", "replace") m = re.search(r"<b>Stego:</b></font>(.*?)<br\s*/?>", page, re.S) return re.findall(r"\((\d+)\)", m.group(1)) if m else []
defmain(): answer = solve() print("answer:", answer) resp = submit(answer) if NEXT in resp: print("[+] accepted - server handed out the go-on link to level 5") elif USER and"4"in profile_levels(): print("[+] accepted - profile Stego list contains (4)") else: print("[-] no acceptance marker in response") if USER: print("profile Stego:", profile_levels())
if __name__ == "__main__": main()
运行输出:
1 2 3 4
$ cd <hts-workspace>/challenges/hts-stego/4 && uv run python solve.py answer: p68cq1hb [+] accepted - profile Stego list contains (4) profile Stego: ['3', '4']