Introduction

This is a beginner-friendly writeup for "A Simple SP Box!", a challenge from the recently concluded dCTF. It's under the Crypto category and is worth 300 points which puts it around the Medium difficulty. Before I started working on this challenge I did a quick search for what SP cryptography is. Google says that it stands for "Substitution Permutation" and is applied by certain block ciphers like AES. We will see how this applies to our challenge later.

Description

It's just a simple SP-box, 150 tries should be enough for you.

TL;DR

If you are simply here for the code required to solve this problem, then look no further:

#!/usr/bin python3

from string import ascii_letters, digits
import socket

host = 'dctf1-chall-sp-box.westeurope.azurecontainer.io'
port = 8888

alphabet = ascii_letters + digits + "_!@#$%.'\\"+:;<=}{"
shuffled = []

s = socket.socket()
s.connect((host,port))

data = s.recv(2048).decode('utf-8')
print(data)
encryptedflag = data.split('\\n')[1]

for var in alphabet:
	character = var*36
	character += '\\n'
	bytesobj = bytes(character, 'utf-8')
	s.send(bytesobj)

	data = s.recv(2048).decode('utf-8')
	print(data)
	encrypted = data.split('\\n')[1]
	shuffled.append(encrypted[0])

substitution = {k : v for k, v in zip(shuffled, alphabet)}
decryptedshuffled = [substitution[c] for c in encryptedflag]

counter = 0;
index = 0;
decryptedflag = ''

while 1:
	if (index >= 42):
		counter += 1
		index = 0
		continue

	if (counter < 7):
		counter += 1
		index += 1
		continue

	currentchar = decryptedshuffled[index]
	decryptedflag += currentchar
	if (currentchar == '}'):
		break

	index += 1
	counter = 0

print(decryptedflag)

s.close()

The problem

Upon connecting to the host, we are greeted with a random string of letters and a message:

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7e7fea22-48ab-42cd-a9bb-3a00d226e341/Screen_Shot_2021-05-17_at_7.56.42_PM.png

Seems like the flag is encrypted and we need to decrypt it. If we try to enter any other input besides the decrypted flag, we get the following message:

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7e03145a-9874-4de4-9b70-501254ebbdae/Screen_Shot_2021-05-17_at_7.57.25_PM.png

The server actually tells us what our input would look like if it was encrypted with the same algorithm that it used to encrypt the flag. We will use this fact later in our solution. For now let's talk about the other half of the problem.

The source code

This part discusses the source code and how it works. If you already understand the code or simply want to see my thought process, proceed to the next section.

We are given the code which contains the exact algorithm used to encrypt the flag.

from string import ascii_letters, digits
from random import SystemRandom
from math import ceil, log
from signal import signal, alarm, SIGALRM
from secret import flag

random = SystemRandom()
ALPHABET = ascii_letters + digits + "_!@#$%.'\\"+:;<=}{"
shuffled = list(ALPHABET)

random.shuffle(shuffled) 
S_box = {k : v for k, v in zip(ALPHABET, shuffled)} 

def encrypt(message):
    if len(message) % 2:
        message += "_"

    message = list(message)
    rounds = int(2 * ceil(log(len(message), 2))) 

    for round in range(rounds):
        message = [S_box[c] for c in message]
        if round < (rounds-1):
            message =  [message[i] for i in range(len(message)) if i%2 == 1] + [message[i] for i in range(len(message)) if i%2 == 0]
    return ''.join(message)

def play():
    print("Here's the flag, please decrypt it for me:")
    print(encrypt(flag)) # 1

    for _ in range(150): # 2
        guess = input("> ").strip()
        assert 0 < len(guess) <= 10000

        if guess == flag: # 3
            print("Well done. The flag is:")
            print(flag)
            break

        else:
            print("That doesn't look right, it encrypts to this:")
            print(encrypt(guess)) # 4

def timeout(a, b):
    print("\\nOut of time. Exiting...")
    exit()

signal(SIGALRM, timeout) # 5
alarm(5 * 60) 

play()

A high-level breakdown of the code shows that:

  1. The flag is encrypted and displayed to the user