diff --git a/royal-message-decryptor/Dockerfile b/royal-message-decryptor/Dockerfile new file mode 100644 index 0000000..daf796c --- /dev/null +++ b/royal-message-decryptor/Dockerfile @@ -0,0 +1,13 @@ +FROM debian:bullseye + +RUN apt update -y && apt install -y python3 + +RUN useradd -m ctf + +WORKDIR /app +COPY spawner.py /app/spawner.py +COPY generator.py /app/generator.py + +CMD ["python3", "spawner.py", "--host", "0.0.0.0", "--port", "3006", "python3", "./generator.py"] + +EXPOSE 3006 \ No newline at end of file diff --git a/royal-message-decryptor/README.md b/royal-message-decryptor/README.md new file mode 100644 index 0000000..a379c2b --- /dev/null +++ b/royal-message-decryptor/README.md @@ -0,0 +1,23 @@ +# royal-message-decryptor +## Text +Long ago, on a field of red, +Two great legions fought and bled. +The fate of the realm, the war’s cruel sting, +Now rests with you, the code-breaker’s king. + +A scout bursts in with frantic cries, +A message clutched—your nation’s prize. +“Their plan is here, but locked away! +Decrypt it fast, there’s no delay!” + +The symbols dance, the clock does too. +The kingdom’s fate depends on you. + +## Author +Robbe De Greef + +## Files +/ + +## How to Deploy +Deploy docker compose. diff --git a/royal-message-decryptor/SOLUTION.md b/royal-message-decryptor/SOLUTION.md new file mode 100644 index 0000000..83c1fd3 --- /dev/null +++ b/royal-message-decryptor/SOLUTION.md @@ -0,0 +1,43 @@ +## Difficulty +50/100 | MEDIUM + +They need to figure out that the first part of the message is a +substitution alphabet, then decrypt the code, and then finally +figure out the flag is embedded in the last message they sent. + +## Category +Crypto + +## How To Solve + +The first part of every message line was a substitution table, +the second part the message. Just substitute the message and the +last message will contain the flag. + +```python3 +from pwn import * +import string + +p = remote("localhost", 3006) +p.recvline() + + +while True: + msg = p.recvline().decode() + p.recvuntil(b"> ") + print(msg) + + alphabet = msg.split(" | ")[0] + message = msg.split(" | ")[1][:-1] + alphabet_map = dict(zip(alphabet, string.ascii_letters)) + + decoded = "".join([alphabet_map[c] if c in alphabet_map else c for c in message]) + print(f"Sending back decoded: '{decoded}'") + p.sendline(decoded.encode()) + + p.recvline() + +``` + +## Flag +IGCTF{Some-wicked-story-unfolded-here-great-work} \ No newline at end of file diff --git a/royal-message-decryptor/docker-compose.yaml b/royal-message-decryptor/docker-compose.yaml new file mode 100644 index 0000000..879148e --- /dev/null +++ b/royal-message-decryptor/docker-compose.yaml @@ -0,0 +1,6 @@ +version: '3.9' +services: + royal-message-decryptor: + build: . + ports: + - 3006:3006 \ No newline at end of file diff --git a/royal-message-decryptor/generator.py b/royal-message-decryptor/generator.py new file mode 100644 index 0000000..4a16470 --- /dev/null +++ b/royal-message-decryptor/generator.py @@ -0,0 +1,82 @@ +import random +import string +import signal + +messages = [ + "Advance the knights from the east; let the banners be visible to all.", + "Charge the cavalry across the plains with speed—make the ground tremble beneath them.", + "Send a column of soldiers from the forest, shields raised, weapons at the ready.", + "Deploy scouts to the riverbank; observe their defenses without being seen.", + "Line the battlements with archers, and have the infantry gather at the forest's edge.", + "Send a detachment of riders to flank the hillside and strike their rear.", + "Move the siege engines forward; prepare them to target the castle walls.", + "March the battalion of spearmen down the valley road in perfect formation.", + "Halt the army at the mountain's base; wait for the reinforcements to arrive.", + "Under the cover of night, move the foot soldiers through the forest toward the west gate.", + "Break the cavalry formation, spread across the plains, and encircle them.", + "Cross the river with the infantry, shields up, and press toward their defenses.", + "Advance the siege towers slowly; let them loom over the field as a show of power.", + "Begin constructing palisades outside the village; block the main road leading to the castle.", + "Set up camp near the bridge; have the engineers secure the crossing by dusk.", + "Split the forces: one group toward the northern gate, the other to secure the southern ridge.", + "Raise the banners on the hill; archers prepare to volley, infantry ready behind them.", + "Withdraw the scouts after they encounter the patrol; signal the main force to advance.", + "Position the catapults under cover of darkness; infantry hold a defensive line until ready.", + "Send raiders to attack the supply convoy near the western pass—cut off their resources.", + "Begin scaling the cliffs with infantry to bypass the watchtower guarding the southern approach.", + "Set up camp on the plains just beyond the river; prepare for a dawn assault.", + "Advance the pikemen in a solid wall through the valley; have the archers provide cover from the ridges.", + "Reinforcements from the north, join the main army in the valley—prepare for a combined assault.", + "Divide the army into three columns: one toward the eastern gate, another through the southern forest, and a third to secure the ridge for a coordinated dawn attack.", + "Our smartest wizards have found the secret to eternal life, it is not 42, but rather IGCTF{Some-wicked-story-unfolded-here-great-work}.", +] + +letters = [c for c in string.ascii_letters] + + +def generate_substitution(): + l = letters[:] + random.shuffle(l) + return l + + +def kill(): + print( + "*You get violently decapitated while a crowd of desensitized dirty towns people watch the show with their family*" + ) + exit(0) + + +def generate_and_solve(msg): + sub = generate_substitution() + search_map = dict(zip(letters, sub)) + new_msg = "".join([search_map[c] if c in search_map else c for c in msg]) + + print(f"{''.join(sub)} | {new_msg}") + inp = input("What does it say!!!> ") + if inp != msg: + print("YOU IDIOT! That was wrong. We lost troops because of you!") + kill() + + print("Ok, it seemed to be right, the king is pleased") + +def took_too_long(_, __): + print("YOU IDIOT! You took too long. We lost troops because of you!") + kill() + +def main(): + print( + "Help! We intercepted messages from the enemy, if you do not decipher them quickly our royal highness will have you decapitated!" + ) + + signal.signal(signal.SIGALRM, took_too_long) + signal.alarm(10) + + for msg in messages: + generate_and_solve(msg) + + print("You did great, no decapitations today!") + + +if __name__ == "__main__": + main() diff --git a/royal-message-decryptor/sol.py b/royal-message-decryptor/sol.py new file mode 100644 index 0000000..7907177 --- /dev/null +++ b/royal-message-decryptor/sol.py @@ -0,0 +1,21 @@ +from pwn import * +import string + +p = remote("localhost", 3006) +p.recvline() + + +while True: + msg = p.recvline().decode() + p.recvuntil(b"> ") + print(msg) + + alphabet = msg.split(" | ")[0] + message = msg.split(" | ")[1][:-1] + alphabet_map = dict(zip(alphabet, string.ascii_letters)) + + decoded = "".join([alphabet_map[c] if c in alphabet_map else c for c in message]) + print(f"Sending back decoded: '{decoded}'") + p.sendline(decoded.encode()) + + p.recvline() diff --git a/royal-message-decryptor/spawner.py b/royal-message-decryptor/spawner.py new file mode 100644 index 0000000..feecba5 --- /dev/null +++ b/royal-message-decryptor/spawner.py @@ -0,0 +1,47 @@ +import socket +from threading import Thread +import subprocess +import sys +import argparse + +# How to use: +# Set the host and port above +# Invoke the spawner like this +# python3 spawner.py -h -p + +def on_new_client(conn, addr, cmd): + print(f"New connection with {addr}") + file = conn.makefile('w') + p = subprocess.Popen(cmd, stdout=file, stdin=file) + conn.close() + +def main(): + parser = argparse.ArgumentParser(description="Spawn some processes over a socket") + parser.add_argument('--host', type=str, required=True, nargs=1, help='The host ip to listen on') + parser.add_argument('--port', type=int, required=True, nargs=1, help='The host port to listen on') + parser.add_argument('rest', nargs=argparse.REMAINDER) + args = parser.parse_args() + + s = socket.socket() + host = args.host[0] + port = args.port[0] + cmd = args.rest + print(f"Command spawn each time: {cmd}") + + s.bind((host, port)) + s.listen(5) + + print(f"Now listening for connections on {host}:{port}") + try: + while True: + conn, addr = s.accept() + t = Thread(target=on_new_client, args=(conn,addr, cmd)) + t.run() + except KeyboardInterrupt: + pass + + s.close() + print("Goodbye") + +if __name__ == '__main__': + main()