Cascade

Flag: CYBN{C45c4d3_R3v34l3d}

Challenge

Description


Un oracle de chiffrement est à votre disposition. Envoyez-lui des messages, et il vous retournera leur version chiffrée. Mais attention, un secret est ajouté à la fin de votre message avant le chiffrement, à vous de le trouver !


FLAG_CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$-?!@_{}'

Solution

Il faut commencer par faire un peu d'analyse :

  1. C'est un chiffrement par bloc de 16 octets.

  2. Un même input donne la même sortie

  3. Le flag est ajouté à la fin de notre message (c'est dans l'énoncé)

On va faire une attaque par padding. L'objectif, c'est de "pousser" le flag à la limite d'un bloc pour ne faire dépasser qu'un caractère. On observera alors le résultat du chiffrement (ici, en violet ce sont les lettres du secret que l'on ne connaît pas).

Ensuite, on brute-force jusqu'à retrouver le même résultat de chiffrement. On saura alors que c'est le même caractère que celui qu'on a fait dépasser tout à l'heure :

On répète l'opération pour les caractères suivants, et 1 à 1, le flag se révélera. (en bleu, c'est notre input, en violet ce qu'on ne connaît pas encore)

Le script complet en python :

from pwnlib.tubes.remote import remote
from Crypto.Util.Padding import pad
import string

class Solver:
  def __init__(self, host, port):
    self.client = remote(host, port)
    self.client.recv(4096)
  
  def encrypt(self, plaintext: str) -> list[str]:
    self.client.send(f'{plaintext.encode().hex()}\n'.encode())
    response = bytes.fromhex(self.client.recvuntil(b'> ').decode().splitlines()[0])
    return [response[i:i+16].hex() for i in range(0, len(response), 16)]


def solve(host, port):
  solver = Solver(host, port)
  flag = ''
  charset = string.ascii_letters + string.digits + string.punctuation
  while not flag.endswith('}'):
    dummy = 'a' * (64 - 1 - len(flag))
    encrypted = solver.encrypt(dummy)
    expected = encrypted[3]
    for c in charset:
      encrypted = solver.encrypt(dummy + flag + c)
      obtained = encrypted[3]
      if obtained == expected:
        flag += c
        print(flag)
        break

if __name__ == '__main__':
  solve('127.0.0.1', 3000)
C
CY
CYB
CYBN
CYBN{
CYBN{C
CYBN{C4
CYBN{C45
CYBN{C45c
CYBN{C45c4
CYBN{C45c4d
CYBN{C45c4d3
CYBN{C45c4d3_
CYBN{C45c4d3_R
CYBN{C45c4d3_R3
CYBN{C45c4d3_R3v
CYBN{C45c4d3_R3v3
CYBN{C45c4d3_R3v34
CYBN{C45c4d3_R3v34l
CYBN{C45c4d3_R3v34l3
CYBN{C45c4d3_R3v34l3d
CYBN{C45c4d3_R3v34l3d}

Dernière mise à jour

Cet article vous a-t-il été utile ?