Rating: 4.5

Original/Source Writeup

We’re also given a template to get started, but I didn’t bother using that, since I’m not a total n00b.

Connecting to the endpoint, we get a game that gives us the encryption method, and the ciphertext, and asks us for the original plaintext:

nc chal.ctf.b01lers.com 2008
Method: rot13
Ciphertext: jbeyq
Input: world
Method: Base64
Ciphertext: c3Vycm91bmQ=
Input: c3Vycm91bmQ=
Hm that doesn't seem quite right we must be awake.

After playing around manually to try to find all possible encryptions, I found the following to be possible:

  1. ROT13
  2. base64
  3. atbash
  4. Baconian

From the template, we can expect there to be 1000 total rounds. With this knowledge, we can code something up in Python that just repeatedly receives and then solves the challenge (creds for the baconian/atbash cipher in comments):

#!/usr/bin/env python3.8
from pwn import *
import sys
from base64 import b64decode

atbash_cipher = {'A': 'Z', 'a': 'z', 'B': 'Y', 'b': 'y', 'C': 'X', 'c': 'x', 'D': 'W', 'd': 'w', 'E': 'V', 'e': 'v', 'F': 'U', 'f': 'u', 'G': 'T', 'g': 't', 'H': 'S', 'h': 's', 'I': 'R', 'i': 'r', 'J': 'Q', 'j': 'q', 'K': 'P', 'k': 'p', 'L': 'O', 'l': 'o', 'M': 'N', 'm': 'n', 'N': 'M', 'n': 'm', 'O': 'L', 'o': 'l', 'P': 'K', 'p': 'k', 'Q': 'J', 'q': 'j', 'R': 'I', 'r': 'i', 'S': 'H', 's': 'h', 'T': 'G', 't': 'g', 'U': 'F', 'u': 'f', 'V': 'E', 'v': 'e', 'W': 'D', 'w': 'd', 'X': 'C', 'x': 'c', 'Y': 'B', 'y': 'b', 'Z': 'A', 'z': 'a', ' ': ' ', '.': '.', ',': ',', '?': '?', '!': '!', '\'': '\'', '\"': '\"', ':': ':', ';': ';', '\(': '\)', '\)': '\)', '\[': '\[', '\]': '\]', '\-': '\-', '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0'}

def rot_alpha(n):
    from string import ascii_lowercase as lc, ascii_uppercase as uc
    lookup = str.maketrans(lc + uc, lc[n:] + lc[:n] + uc[n:] + uc[:n])
    return lambda s: s.translate(lookup)

# https://stackoverflow.com/a/45717802/13158274
# -- Jérôme
class AtBash:

   def __init__(self):
       self.alphabets = ' ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_+|:"<>-=[];,.?/`'
       self.alphabets = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}'

   def encode(self, plaintext):
       cipher = ""
       for i in plaintext:
           index = self.alphabets.index(i)
           cipher += self.alphabets[abs(len(self.alphabets) - index - 1) % len(self.alphabets)]
       return cipher

   def decode(self, ciphertext):
       return self.encode(ciphertext)

# https://www.geeksforgeeks.org/baconian-cipher/
def decrypt_bacon(message):
    lookup = {'A':'aaaaa', 'B':'aaaab', 'C':'aaaba', 'D':'aaabb', 'E':'aabaa',
        'F':'aabab', 'G':'aabba', 'H':'aabbb', 'I':'abaaa', 'J':'abaab',
        'K':'ababa', 'L':'ababb', 'M':'abbaa', 'N':'abbab', 'O':'abbba',
        'P':'abbbb', 'Q':'baaaa', 'R':'baaab', 'S':'baaba', 'T':'baabb',
        'U':'babaa', 'V':'babab', 'W':'babba', 'X':'babbb', 'Y':'bbaaa', 'Z':'bbaab'}

    decipher = ''
    i = 0

    # emulating a do-while loop
    while True :
        # condition to run decryption till
        # the last set of ciphertext
        if(i < len(message)-4):
            # extracting a set of ciphertext
            # from the message
            substr = message[i:i + 5]
            # checking for space as the first
            # character of the substring
            if(substr[0] != ' '):
                '''
                This statement gets us the key(plaintext) using the values(ciphertext)
                Just the reverse of what we were doing in encrypt function
                '''
                decipher += list(lookup.keys())[list(lookup.values()).index(substr)]
                i += 5 # to get the next set of ciphertext

            else:
                # adds space
                decipher += ' '
                i += 1 # index next to the space
        else:
            break # emulating a do-while loop

    return decipher

def main():
    r = remote('chal.ctf.b01lers.com', 2008)
    i = 0
    while 1 < 2:
        print("=== ", i)
        method = r.recvline().decode().strip().split()[-1]
        ct = r.recvline().decode().strip().split()[-1]
        plaintext = ""
        if method == "atbash":
            for char in ct:
                plaintext += atbash_cipher[char]
            r.sendafter('Input: ', plaintext+'\n')
        elif method == "rot13":
            r.sendafter('Input: ', rot_alpha(13)(ct)+'\n')
        elif method == "Base64":
            r.sendafter('Input: ', b64decode(ct).decode()+'\n')
        elif method == "bacon":
            r.sendafter('Input: ', decrypt_bacon(ct.lower()).lower()+'\n')
        i += 1
        if i == 1000:
            print(r.stream())
            sys.exit(0)

if __name__ == '__main__':
    main()

Which after running, gives us the flag:

...
===  993
===  994
===  995
===  996
===  997
===  998
===  999
We must be dreaming, here's your flag: ctf{4n_313g4nt_s01ut10n_f0r_tr4cking_r341ity}
b"We must be dreaming, here's your flag: ctf{4n_313g4nt_s01ut10n_f0r_tr4cking_r341ity}\n"

Flag is ctf{4n_313g4nt_s01ut10n_f0r_tr4cking_r341ity}.