100 lines
2.9 KiB
Python
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()
|