feat: add royal-message-decryptor challenge

This commit is contained in:
Abel Stuker 2024-11-25 22:32:28 +01:00
parent 242f9b8636
commit 5991f63f5d
7 changed files with 235 additions and 0 deletions

View File

@ -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

View File

@ -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 wars cruel sting,
Now rests with you, the code-breakers king.
A scout bursts in with frantic cries,
A message clutched—your nations prize.
“Their plan is here, but locked away!
Decrypt it fast, theres no delay!”
The symbols dance, the clock does too.
The kingdoms fate depends on you.
## Author
Robbe De Greef
## Files
/
## How to Deploy
Deploy docker compose.

View File

@ -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}

View File

@ -0,0 +1,6 @@
version: '3.9'
services:
royal-message-decryptor:
build: .
ports:
- 3006:3006

View File

@ -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()

View File

@ -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()

View File

@ -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 <host> -p <port> <your arguments to spawn here>
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()