2026-07-12 20:22:15 +01:00

100 lines
2.9 KiB
Python

#!/usr/bin/env python3
import argparse
from pathlib import Path
# Correct PNG magic number/signature
PNG_MAGIC = b"\x89PNG\r\n\x1a\n"
# A fun “corrupted” header for the Xmas CTF challenge (8 bytes)
CORRUPTED_MAGIC = b"XMASCTF!" # exactly 8 bytes
def generate_challenge(input_png: Path, output_png: Path) -> None:
"""
Read a valid PNG, replace the first 8 bytes (magic number) with a corrupted
value, and write out the challenge PNG.
"""
data = input_png.read_bytes()
if not data.startswith(PNG_MAGIC):
print(
"[!] Warning: Input file does not start with a valid PNG signature. "
"Are you sure it's a PNG?"
)
if len(data) < 8:
raise ValueError("Input file is too small to be a valid PNG.")
corrupted = CORRUPTED_MAGIC + data[8:]
output_png.write_bytes(corrupted)
print(f"[+] Challenge written to: {output_png}")
print(f"[+] First 8 bytes changed from PNG magic to: {CORRUPTED_MAGIC!r}")
def solve_challenge(corrupted_png: Path, fixed_png: Path) -> None:
"""
Read a corrupted PNG (with broken magic number), restore the correct
PNG magic number, and write out the fixed PNG.
"""
data = corrupted_png.read_bytes()
if len(data) < 8:
raise ValueError("Corrupted file is too small to be a PNG.")
original_header = data[:8]
fixed = PNG_MAGIC + data[8:]
fixed_png.write_bytes(fixed)
print(f"[+] Fixed PNG written to: {fixed_png}")
print(f"[+] Replaced header {original_header!r} with {PNG_MAGIC!r}")
def main():
parser = argparse.ArgumentParser(
description="Xmas CTF: Magic Route - PNG magic number corrupter/solver."
)
subparsers = parser.add_subparsers(dest="command", required=True)
# generate subcommand
gen = subparsers.add_parser(
"generate", help="Generate a corrupted PNG challenge from a valid PNG."
)
gen.add_argument("input", type=Path, help="Path to a valid input PNG.")
gen.add_argument(
"output",
type=Path,
nargs="?",
help="Path to write corrupted challenge PNG (default: challenge.png)",
)
# solve subcommand
solve = subparsers.add_parser(
"solve",
help="Fix a corrupted PNG (restore correct magic number).",
)
solve.add_argument("input", type=Path, help="Path to the corrupted PNG.")
solve.add_argument(
"output",
type=Path,
nargs="?",
help="Path to write fixed PNG (default: fixed.png)",
)
args = parser.parse_args()
if args.command == "generate":
input_png = args.input
output_png = args.output or Path("challenge.png")
generate_challenge(input_png, output_png)
elif args.command == "solve":
corrupted_png = args.input
fixed_png = args.output or Path("fixed.png")
solve_challenge(corrupted_png, fixed_png)
if __name__ == "__main__":
main()