Skip to content

Instantly share code, notes, and snippets.

@bburky

bburky/whale.py Secret

Created April 14, 2019 21:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bburky/58edd7ce00cd4405429269695568fe2c to your computer and use it in GitHub Desktop.
Save bburky/58edd7ce00cd4405429269695568fe2c to your computer and use it in GitHub Desktop.
PlaidCTF 2019: A Whaley Good Joke
import tarfile
import json
from datetime import datetime
import sys
import shutil
import tempfile
from pathlib import Path
from io import BytesIO
import hashlib
# https://stackoverflow.com/a/44873382
def sha256sum(filename):
h = hashlib.sha256()
b = bytearray(128*1024)
mv = memoryview(b)
with open(filename, 'rb', buffering=0) as f:
for n in iter(lambda : f.readinto(mv), 0):
h.update(mv[:n])
return h.hexdigest()
image_filename = sys.argv[1]
def layer_timestamps(image_dir):
for layer_tar in Path(image_dir).glob("*/layer.tar"):
with tarfile.open(layer_tar) as layer:
max_timestamp = max(info.mtime for info in layer)
yield max_timestamp, layer_tar
with tempfile.TemporaryDirectory() as tmpdir:
with tarfile.open(image_filename, 'r:gz') as image:
image.extractall(path=tmpdir)
with Path(tmpdir, "manifest.json").open('r') as f:
manifest = json.load(f)
manifest_layers = manifest[0]["Layers"] = []
config_filename = manifest[0]["Config"]
with Path(tmpdir, config_filename).open('r') as f:
config = json.load(f)
diff_ids = config["rootfs"]["diff_ids"] = []
print("Sorting layers...")
layers = sorted(layer_timestamps(tmpdir))
for timestamp, layer_tar in layers:
layer_hash = layer_tar.parts[-2]
layer_tar = Path(tmpdir, layer_hash, "layer.tar")
layer_diffid = sha256sum(layer_tar)
manifest_layers.append(f"{layer_hash}/layer.tar")
diff_ids.append(f"sha256:{layer_diffid}")
print("Rebuilding image...")
with tarfile.open(f"{image_filename}-fixed", 'w:gz') as image_fixed:
image_fixed.add(Path(tmpdir, "repositories"), arcname="repositories")
# Add layer files
for image_file in Path(tmpdir).glob("*/*"):
# TODO: skip json?
image_fixed.add(image_file, arcname=image_file.relative_to(tmpdir))
# print(image_file)
manifest_file = BytesIO(json.dumps(manifest).encode("utf-8"))
manifest_tarinfo = tarfile.TarInfo("manifest.json")
manifest_tarinfo.size = manifest_file.getbuffer().nbytes
image_fixed.addfile(manifest_tarinfo, manifest_file)
config_file = BytesIO(json.dumps(config).encode("utf-8"))
config_tarinfo = tarfile.TarInfo(config_filename)
config_tarinfo.size = config_file.getbuffer().nbytes
image_fixed.addfile(config_tarinfo, config_file)
# Load with:
# docker load -i pctf-whales_169aeb74f82dcdceb76e36a6c4c22a89-fixed
# Run:
# docker run --rm -it pctf-whales:latest ./flag.sh
# This soluition has files 12 and 15 missing some reason.
# I extracted them manually.
# pctf{1_b3t_u_couldnt_c0nt4in3r_ur_l4ught3r}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment