fixed format
This commit is contained in:
@@ -1,69 +0,0 @@
|
||||
import argparse
|
||||
from typing import List
|
||||
import qrcode
|
||||
from PIL import Image
|
||||
from pathlib import Path
|
||||
import random
|
||||
|
||||
NUM_PIECES = 3
|
||||
TILE = 78
|
||||
|
||||
def generate_qrcode(data: str) -> Image:
|
||||
qr = qrcode.QRCode(version=25, box_size=2, error_correction=qrcode.ERROR_CORRECT_L, border=0)
|
||||
qr.add_data(data)
|
||||
return qr.make_image()
|
||||
|
||||
|
||||
def cut_pieces(image: Image) -> List[Image]:
|
||||
pieces = []
|
||||
|
||||
for x, y in [(x, y) for x in range(NUM_PIECES) for y in range(NUM_PIECES)]:
|
||||
pieces.append(image.crop((x * TILE, y * TILE, x * TILE + TILE, y * TILE + TILE)))
|
||||
|
||||
return pieces
|
||||
|
||||
|
||||
def scramble_qr_code(payloadList: List[Image], seed: str) -> Image:
|
||||
height_piece, width_piece = payloadList[0].size
|
||||
random.seed(seed)
|
||||
scrambled_width = 3
|
||||
random.shuffle(payloadList)
|
||||
|
||||
new_qr_code = Image.new("RGB", (width_piece * scrambled_width, height_piece * NUM_PIECES))
|
||||
for x, y in [(x, y) for x in range(scrambled_width) for y in range(NUM_PIECES)]:
|
||||
piece = payloadList[x + scrambled_width * y]
|
||||
new_qr_code.paste(piece, (x * width_piece, y * height_piece))
|
||||
|
||||
return new_qr_code
|
||||
|
||||
|
||||
def generate_scrambled_qrcode(seed: str, output_dir: Path, name: str):
|
||||
with open("./secret_message.txt", "r") as secret:
|
||||
message = secret.read()
|
||||
|
||||
qr_code = generate_qrcode(message + seed)
|
||||
|
||||
qr_pieces = cut_pieces(qr_code)
|
||||
scrambled_qr_code = scramble_qr_code(qr_pieces, seed)
|
||||
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
scrambled_qr_code.save(output_dir / name)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generates a scrambled QR code with optional decoy pieces.")
|
||||
parser.add_argument("seed", type=str, help="The password to scramble the QR code.")
|
||||
parser.add_argument("output_dir", type=Path, help="Directory where the output will be stored")
|
||||
parser.add_argument("-o", "--output", type=str, default="secret_message.png", help="The name of the output image file.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
generate_scrambled_qrcode(
|
||||
seed=args.seed,
|
||||
output_dir=args.output_dir,
|
||||
name=args.output
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -16,3 +16,8 @@ Hint:
|
||||
content: "h4tum{sm4ll_key_spac3s_are_deadly}"
|
||||
dynamic: true
|
||||
files: dynamic # dynamic requires a gen_files script
|
||||
dependencies:
|
||||
type: debian
|
||||
system: ["python3", "libzbar0"]
|
||||
python: ["pillow", "qrcode", "pyzbar", "numpy", "tqdm"]
|
||||
check: "python3 solve.py"
|
||||
|
||||
@@ -9,7 +9,7 @@ Consider a toy "QR-encryption" scheme where a message is encoded into
|
||||
a QR code and then randomized by permuting its tiles with a secret seed.
|
||||
|
||||
(a) Under ideal assumptions, explain why this scheme could satisfy
|
||||
Kerckhoffs’ principle.
|
||||
Kerckhoffs’libzbar0 principle.
|
||||
(b) Now assume an attacker can test permutations against the QR format
|
||||
(find patterns, error correction, alignment markers). Why does this
|
||||
reduce the effective security of the scheme?
|
||||
@@ -40,8 +40,8 @@ def scramble_qr_code(payloadList, decoyList, seed):
|
||||
return new_qr_code
|
||||
|
||||
|
||||
def generate_scrambled_qrcode(seed, output_dir, decoy, name):
|
||||
with open("res/secret_message.txt", "r") as secret:
|
||||
def generate_scrambled_qrcode(seed, output_dir, res_dir, decoy, name):
|
||||
with open(res_dir / "secret_message.txt", "r") as secret:
|
||||
message = secret.read()
|
||||
|
||||
qr_code = generate_qrcode(message + seed)
|
||||
@@ -49,7 +49,7 @@ def generate_scrambled_qrcode(seed, output_dir, decoy, name):
|
||||
qr_pieces = cut_pieces(qr_code)
|
||||
decoy_pieces = None
|
||||
if decoy:
|
||||
with open("res/decoy.txt", "r") as decoy_msg:
|
||||
with open(res_dir / "decoy.txt", "r") as decoy_msg:
|
||||
decoy = decoy_msg.read()
|
||||
|
||||
decoy_qr_code = generate_qrcode(decoy)
|
||||
@@ -65,6 +65,7 @@ def main():
|
||||
parser = argparse.ArgumentParser(description="Generates a scrambled QR code with optional decoy pieces.")
|
||||
parser.add_argument("seed", type=str, help="The password to scramble the QR code.")
|
||||
parser.add_argument("output_dir", type=Path, help="Directory where the output will be stored")
|
||||
parser.add_argument("res_dir", type=Path, help="Directory where needed resources are stored")
|
||||
parser.add_argument("--without-decoy", action="store_false", help="Exclude decoy pieces in the scrambled QR code.")
|
||||
parser.add_argument("-o", "--output", type=str, default="secret_message.png", help="The name of the output image file.")
|
||||
|
||||
@@ -73,6 +74,7 @@ def main():
|
||||
generate_scrambled_qrcode(
|
||||
seed=args.seed,
|
||||
output_dir=args.output_dir,
|
||||
res_dir=args.res_dir,
|
||||
decoy=args.without_decoy,
|
||||
name=args.output
|
||||
)
|
||||
|
||||
@@ -12,14 +12,14 @@ test_flag = f"h4tum{{healthy_healthy_healthchecks_{random_bytes}}}"
|
||||
|
||||
generate_scrambled_qrcode(
|
||||
seed=test_flag,
|
||||
output_dir=Path("tmp/health_check"),
|
||||
output_dir=Path("/tmp/health_check"),
|
||||
decoy=True,
|
||||
name=f"{random_bytes}.png"
|
||||
)
|
||||
|
||||
output = main(f"tmp/health_check/{random_bytes}.png", False)
|
||||
output = main(f"/tmp/health_check/{random_bytes}.png", False)
|
||||
|
||||
if output and test_flag in output:
|
||||
print("Healthy!")
|
||||
print(output)
|
||||
else:
|
||||
print("Oh oh somethings wrong :()")
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
Wow you solved the challenge!
|
||||
Meet us at our booth we really want to get in touch with you :D
|
||||
|
||||
Show us the flag:
|
||||
7
solve.py
7
solve.py
@@ -1,4 +1,3 @@
|
||||
from logging import disable
|
||||
from PIL import Image
|
||||
from pyzbar.pyzbar import decode
|
||||
import itertools
|
||||
@@ -104,7 +103,7 @@ def solve_combo(combo):
|
||||
return None
|
||||
|
||||
|
||||
def main(img_path="student_fair/secret_message.png", verbose=True):
|
||||
def main(img_path="files/secret_message.png", verbose=True):
|
||||
img = Image.open(img_path)
|
||||
global new_image
|
||||
new_image = Image.new("RGB", (img.height, img.height))
|
||||
@@ -129,10 +128,8 @@ def main(img_path="student_fair/secret_message.png", verbose=True):
|
||||
|
||||
result = None
|
||||
|
||||
MAX_WORKER = 6
|
||||
|
||||
# Use `multiprocessing.Pool` with the number of available cores
|
||||
with Pool(MAX_WORKER) as pool:
|
||||
with Pool() as pool:
|
||||
# `imap_unordered` returns results as they are completed.
|
||||
# This is a lazy iterator, so it doesn't create all combos at once.
|
||||
# `tqdm` can still be used to show progress.
|
||||
|
||||
Reference in New Issue
Block a user