commit a04e881dd365292f065f3ce3e8bd2d9ec5a4bbbe
parent 77a174dce674e0a9bef862db68d45e83cbb778f6
Author: Louis Burda <quent.burda@gmail.com>
Date: Sat, 13 Apr 2024 23:29:05 +0200
Add solution
Diffstat:
7 files changed, 242 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+__pycache__
diff --git a/solve/Dockerfile b/solve/Dockerfile
@@ -0,0 +1,18 @@
+FROM ubuntu:jammy-20240125@sha256:bcc511d82482900604524a8e8d64bf4c53b2461868dac55f4d04d660e61983cb
+
+# https://github.com/reproducible-containers/repro-sources-list.sh
+# Sorry for the mess.
+ADD --checksum=sha256:4e7e6536b206488b2414d1fa2272e8bbf17fbe7d11e5648eb51284c8fa96b0a9 \
+ https://raw.githubusercontent.com/reproducible-containers/repro-sources-list.sh/v0.1.1/repro-sources-list.sh \
+ /usr/local/bin/repro-sources-list.sh
+
+RUN chmod +x /usr/local/bin/repro-sources-list.sh && \
+ /usr/local/bin/repro-sources-list.sh && \
+ apt-get update && \
+ DOCKER_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ zsh socat bsdmainutils
+
+COPY run.sh run.sh
+RUN chmod +x run.sh
+EXPOSE 1024
+CMD socat TCP-LISTEN:1024,fork,reuseaddr EXEC:"./run.sh",stderr
diff --git a/solve/docker-compose.yml b/solve/docker-compose.yml
@@ -0,0 +1,6 @@
+services:
+ service:
+ build:
+ context: .
+ ports:
+ - "1024:1024"
diff --git a/solve/flag b/solve/flag
@@ -0,0 +1 @@
+CSCG{zsh_subshell_random_seeding?_good_catch!}
diff --git a/solve/notes b/solve/notes
@@ -0,0 +1,12 @@
+fuck this challenge, no interesting concepts, only tricky details
+
+look into zsh source, sind out that RANDOM uses C srand / rand
+
+srand is called on zsh init with epoch sec+usec seed
+
+we can guess the seed, filter using known values and do trial decryptions
+to find the right one
+
+tricky detail: subshell does not modify the rand state of the parent shell
+so in the encoded piece generation the random value does not change..
+haha that did not cost me my sanity
diff --git a/solve/run.sh b/solve/run.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env zsh
+
+echo "Welcome to the Mystical Coin CTF Adventure!"
+echo "In your bag, you carry a special coin, known for always bring you luck. You've been throwing it repeatedly, lost in thought."
+echo "After $RANDOM throws, you finally get your most precious sequence:"
+sleep 1
+echo ""
+
+PASSKEY=""
+
+function mystical_coin_flip {
+ echo "An ancient coin lands in your hand. Will it be heads (H) or tails (T)?"
+ read "?Your call: " user_call
+
+ # Validate input
+ if ! [[ "$user_call" =~ ^[HhTt]$ ]]; then
+ echo "Invalid call. Please choose heads (H) or tails (T)."
+ exit 1
+ fi
+
+ # Simulate a coin flip
+ VAL=$RANDOM
+ if (( $VAL % 2 )); then
+ coin_result="H"
+ else
+ coin_result="T"
+ fi
+ PASSKEY+=$coin_result
+ echo "The coin lands on $coin_result."
+
+ if [[ ${user_call:u} == ${coin_result:u} ]]; then
+ echo "Fortune smiles upon you!"
+ echo ""
+ return 0
+ else
+ echo "Oh no! The coin has other plans..."
+ echo ""
+ return 1
+ fi
+}
+
+mystical_coin_flip
+mystical_coin_flip
+mystical_coin_flip
+mystical_coin_flip
+mystical_coin_flip
+
+echo "Distracted by this particular sequence of flips, you suddenly find yourself at the entrance of an ancient and mystical land."
+echo "Your destiny in this realm is mysteriously linked to this coin."
+sleep 1
+echo ""
+
+echo "You stand at a mystical crossroads, with two paths forward: the Portal of Fate (P) or the Oracle's Den (O)."
+read "?Choose P for the Portal of Fate or O for the Oracle's Den: " choice
+
+
+if [[ $choice == [Pp]* ]]; then
+ echo "You approach the Portal of Fate, shimmering with ancient energy."
+ if mystical_coin_flip; then
+ echo "As the portal swirls open, a radiant light envelops you, transporting you to a realm beyond imagination."
+ echo "In this realm, you navigate a labyrinth of stars, solve riddles whispered by ancient trees, and unlock a celestial puzzle that aligns the constellations."
+ echo "At the heart of the realm, you find a crystal dais. Etched upon it in shimmering light is the flag part: 'PART2}'."
+ echo "A voice, as old as time, echoes around you, 'The journey through the stars is a reflection of the journey within. You have navigated both with wisdom.'"
+ echo "With a flash of light, you're back at the portal, holding a token from the starry realm as a memento of your adventure."
+ else
+ echo "The portal catapults you into space, where you become a human comet, blazing through the cosmos!"
+ echo "It's a breathtaking sight, but alas, a comet can't collect flag parts."
+ fi
+else
+ echo "You tread cautiously towards the Oracle's Den. The coin is soaring through the air, heading back to your hand."
+ if mystical_coin_flip; then
+ echo "The Oracle guides you to a hidden library, a sanctuary of ancient secrets."
+ echo "Within its silent walls, you discover a curious scroll sealed with a mystic symbol."
+
+ for ((i=1; i<=32; i++))
+ do
+ PASSKEY=$(echo $PASSKEY$RANDOM | md5sum)
+ echo "$PASSKEY"
+ done
+ export PASSKEY=$PASSKEY
+ echo "$PASSKEY"
+ echo "Unfurling the scroll reveals a series of enigmatic symbols and a cryptic cipher:"
+
+ echo "CSCG{FLAG_PART_1_" | openssl enc -aes-256-cbc -e -pass env:PASSKEY 2>/dev/null | hexdump -C
+
+ echo "The Oracle hints that it's a hexadecimal code, part of a larger puzzle."
+ echo "She whispers that understanding its meaning requires knowledge found only in the distant lands of your quest."
+ echo "This cipher, a fragment of your flag, ignites a deeper curiosity, leading you to seek the wisdom needed to unlock its secrets."
+ else
+ echo "The Oracle laughs so heartily at your coin call that a gust of wind from her chuckle sweeps you off your feet."
+ echo "You find yourself blown into a painting, transforming into a permanent, smiling figure in an idyllic landscape within the frame."
+ echo "As you adjust to your new painted existence, surrounded by serene hills and a peaceful river, you realize that while this painted world is beautiful, it holds no flag parts for your quest."
+ echo "You become a cherished character in the Oracle's gallery, admired by all who pass by, but your journey for the flag ends in this artistic realm."
+ fi
+fi
diff --git a/solve/solve b/solve/solve
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+
+from pwn import *
+from hashlib import md5
+from ctypes import CDLL
+
+import subprocess
+import sys
+
+# context.log_level = "DEBUG"
+
+args = sys.argv[1:]
+if args == []:
+ args = ["nc", "localhost", "1024"]
+
+libc = CDLL("libc.so.6")
+
+def connect():
+ io = process(args)
+ io.readuntil(b"After ")
+ leak = int(io.readline().split()[0])
+
+ flips = ""
+ for _ in range(5):
+ io.sendline(b"H")
+ io.readuntil(b"The coin lands on ")
+ flips += io.readline().decode()[0]
+
+ return io, leak, flips
+
+def gen_key(seed, flips):
+ libc.srand(seed)
+ libc.rand()
+ for _ in flips:
+ libc.rand()
+ key = flips
+ value = libc.rand() & 0x7fff
+ for _ in range(32):
+ concat = (key + str(value) + "\n").encode()
+ key = md5(concat).hexdigest() + " -"
+ return key
+
+def test_seed(seed, leak, flips):
+ libc.srand(seed)
+ value = libc.rand() & 0x7fff
+ if value != leak:
+ return False
+ nflips = ""
+ for _ in flips:
+ value = libc.rand() & 0x7fff
+ nflips += 'H' if value % 2 == 1 else 'T'
+ return flips == nflips
+
+def main():
+ while True:
+ io, _, _ = connect()
+ io.sendline(b"P")
+ io.sendline(b"H")
+ io.readuntil(b"The coin lands on")
+ io.readline()
+ if b"Fortune smiles upon you" in io.readline():
+ io.readuntil(b"light is the flag part:")
+ flag_suffix = io.readline().split(b"'")[1].decode()
+ io.close()
+ break
+ io.close()
+
+ while True:
+ timestamp = int(time.time())
+ io, leak, flips = connect()
+ io.sendline(b"O")
+ io.sendline(b"H")
+ io.readuntil(b"The coin lands on ")
+ flips += io.readline().decode()[0]
+ if b"Fortune smiles upon you" in io.readline():
+ io.readuntil(b"cipher:\n")
+ flag_prefix_enc = b""
+ while True:
+ line = io.readline()
+ if b" " not in line:
+ break
+ print(line)
+ parts = line.decode().split(" ")
+ hexline = (parts[1] + parts[2]).replace(" ", "")
+ flag_prefix_enc += bytes.fromhex(hexline)
+ print(flag_prefix_enc)
+ io.close()
+ break
+ io.close()
+
+ flag_prefix = None
+ for offset in range(-2000000, 2000001):
+ seed = timestamp + offset
+ if test_seed(seed, leak, flips):
+ key = gen_key(seed, flips)
+ cmd = ["openssl", "enc", "-aes-256-cbc",
+ "-d", "-pass", f"pass:{key}"]
+ out = subprocess.run(cmd, stdout=subprocess.PIPE,
+ input=flag_prefix_enc).stdout
+ print(out)
+ if b"CSCG" in out:
+ flag_prefix = out.decode().strip()
+ break
+ assert(flag_prefix)
+
+ print(flag_prefix + flag_suffix)
+
+if __name__ == "__main__":
+ main()