feat: add royal-message-decryptor challenge
This commit is contained in:
parent
242f9b8636
commit
5991f63f5d
13
royal-message-decryptor/Dockerfile
Normal file
13
royal-message-decryptor/Dockerfile
Normal 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
|
23
royal-message-decryptor/README.md
Normal file
23
royal-message-decryptor/README.md
Normal 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 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.
|
43
royal-message-decryptor/SOLUTION.md
Normal file
43
royal-message-decryptor/SOLUTION.md
Normal 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}
|
6
royal-message-decryptor/docker-compose.yaml
Normal file
6
royal-message-decryptor/docker-compose.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
version: '3.9'
|
||||
services:
|
||||
royal-message-decryptor:
|
||||
build: .
|
||||
ports:
|
||||
- 3006:3006
|
82
royal-message-decryptor/generator.py
Normal file
82
royal-message-decryptor/generator.py
Normal 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()
|
21
royal-message-decryptor/sol.py
Normal file
21
royal-message-decryptor/sol.py
Normal 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()
|
47
royal-message-decryptor/spawner.py
Normal file
47
royal-message-decryptor/spawner.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user