Iced TEA

CatΓ©gorie: Crypto DifficultΓ©: easy Flag: HTB{th1s_1s_th3_t1ny_3ncryp710n_4lg0r1thm_____y0u_m1ght_h4v3_4lr34dy_s7umbl3d_up0n_1t_1f_y0u_d0_r3v3rs1ng}

Challenge

Description


Locked within a cabin crafted entirely from ice, you're enveloped in a chilling silence. Your eyes land upon an old notebook, its pages adorned with thousands of cryptic mathematical symbols. Tasked with deciphering these enigmatic glyphs to secure your escape, you set to work, your fingers tracing each intricate curve and line with determination. As you delve deeper into the mysterious symbols, you notice that patterns appear in several pages and a glimmer of hope begins to emerge. Time is flying and the temperature is dropping, will you make it before you become one with the cabin?

Analyse du code

Voici le code :

import os
from secret import FLAG
from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from enum import Enum

class Mode(Enum):
    ECB = 0x01
    CBC = 0x02

class Cipher:
    def __init__(self, key, iv=None):
        self.BLOCK_SIZE = 64
        self.KEY = [b2l(key[i:i+self.BLOCK_SIZE//16]) for i in range(0, len(key), self.BLOCK_SIZE//16)]
        self.DELTA = 0x9e3779b9
        self.IV = iv
        if self.IV:
            self.mode = Mode.CBC
        else:
            self.mode = Mode.ECB
    
    def _xor(self, a, b):
        return b''.join(bytes([_a ^ _b]) for _a, _b in zip(a, b))

    def encrypt(self, msg):
        msg = pad(msg, self.BLOCK_SIZE//8)
        blocks = [msg[i:i+self.BLOCK_SIZE//8] for i in range(0, len(msg), self.BLOCK_SIZE//8)]
        
        ct = b''
        if self.mode == Mode.ECB:
            for pt in blocks:
                ct += self.encrypt_block(pt)
        elif self.mode == Mode.CBC:
            X = self.IV
            for pt in blocks:
                enc_block = self.encrypt_block(self._xor(X, pt))
                ct += enc_block
                X = enc_block
        return ct

    def encrypt_block(self, msg):
        m0 = b2l(msg[:4])
        m1 = b2l(msg[4:])
        K = self.KEY
        msk = (1 << (self.BLOCK_SIZE//2)) - 1

        s = 0
        for i in range(32):
            s += self.DELTA
            m0 += ((m1 << 4) + K[0]) ^ (m1 + s) ^ ((m1 >> 5) + K[1])
            m0 &= msk
            m1 += ((m0 << 4) + K[2]) ^ (m0 + s) ^ ((m0 >> 5) + K[3])
            m1 &= msk
        
        m = ((m0 << (self.BLOCK_SIZE//2)) + m1) & ((1 << self.BLOCK_SIZE) - 1) # m = m0 || m1

        return l2b(m)

if __name__ == '__main__':
    KEY = os.urandom(16)
    cipher = Cipher(KEY)
    ct = cipher.encrypt(FLAG)
    with open('output.txt', 'w') as f:
        f.write(f'Key : {KEY.hex()}\nCiphertext : {ct.hex()}')

Ici on utilise le Cipher en mode ECB, voici ce qu’il fait :

  1. Ajouter un padding au message pour que sa taille soit un multiple de 8 bytes

  2. DΓ©couper en blocs de 8 bytes le message

  3. Chiffrer ces blocs individuellement

  4. Retourner le rΓ©sultat du chiffrement

Concernant le chiffrement des blocs, il les sΓ©pare en 2 sous blocs de 4 bytes et appliquent des opΓ©rations simples et rΓ©versibles dessus, puis les rΓ©assemble

Il suffit donc d’inverser l’ordre des opΓ©rations :

def encrypt_block(self, msg):
  m0 = b2l(msg[:4])
  m1 = b2l(msg[4:])
  K = self.KEY
  msk = (1 << (self.BLOCK_SIZE//2)) - 1

  s = 0
  for i in range(32):
    s += self.DELTA
    
    m0 += ((m1 << 4) + K[0]) ^ (m1 + s) ^ ((m1 >> 5) + K[1])
    m0 &= msk
    
    m1 += ((m0 << 4) + K[2]) ^ (m0 + s) ^ ((m0 >> 5) + K[3])
    m1 &= msk
  
  m = ((m0 << (self.BLOCK_SIZE//2)) + m1) & ((1 << self.BLOCK_SIZE) - 1) # m = m0 || m1

        return l2b(m)
  
def decrypt_block(self, ct: bytes) -> bytes:
  m0 = b2l(ct[:4])
	m1 = b2l(ct[4:])
	K = self.KEY
	msk = (1 << (self.BLOCK_SIZE // 2)) - 1
	
	# Comme dans le chiffrement on ajoute DELTA Γ  s Γ  chaque tour de boucle
	# Ici s commence Γ  sa valeur max et on soustrait DELTA
	s = 32 * self.DELTA
	for i in range(32):
		# LittΓ©ralment juste inverser l'ordre des opΓ©rations
		# Il faut cependant bien laisser le ET logique avec le mask après, ça ne fait pas parti des opérations, c'est pour garder le résultat sur 4 4bytes
		m1 -= ((m0 << 4) + K[2]) ^ (m0 + s) ^ ((m0 >> 5) + K[3])
		m1 &= msk
		
		m0 -= ((m1 << 4) + K[0]) ^ (m1 + s) ^ ((m1 >> 5) + K[1])
		m0 &= msk
		
		s -= self.DELTA
	
	# on reassemble les blocs
	ct = ((m0 << (self.BLOCK_SIZE // 2)) + m1) & ((1 << self.BLOCK_SIZE) - 1)
	return l2b(ct)

Script de rΓ©solution

import os
from Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from enum import Enum

class Mode(Enum):
	ECB = 0x01
	CBC = 0x02

class Cipher:
	def __init__(self, key, iv=None):
		self.BLOCK_SIZE = 64
		self.KEY = [b2l(key[i:i + self.BLOCK_SIZE // 16]) for i in range(0, len(key), self.BLOCK_SIZE // 16)]
		self.DELTA = 0x9e3779b9
		self.IV = iv
		if self.IV:
			self.mode = Mode.CBC
		else:
			self.mode = Mode.ECB

	def _xor(self, a, b):
		return b''.join(bytes([_a ^ _b]) for _a, _b in zip(a, b))

	def decrypt(self, ct: bytes) -> bytes:
		blocks = [ct[i:i + self.BLOCK_SIZE // 8] for i in range(0, len(ct), self.BLOCK_SIZE // 8)]
		msg = b''

		if self.mode == Mode.ECB:
			for pt in blocks:
				msg += self.decrypt_block(pt)

		elif self.mode == Mode.CBC:
			X = self.IV
			for pt in blocks:
				enc_block = self.decrypt_block(self._xor(X, pt))
				ct += enc_block
				X = enc_block

		return unpad(msg, self.BLOCK_SIZE//8)

	def decrypt_block(self, ct: bytes) -> bytes:
		m0 = b2l(ct[:4])
		m1 = b2l(ct[4:])
		K = self.KEY
		msk = (1 << (self.BLOCK_SIZE // 2)) - 1
		s = 32 * self.DELTA
		for i in range(32):
			m1 -= ((m0 << 4) + K[2]) ^ (m0 + s) ^ ((m0 >> 5) + K[3])
			m1 &= msk
			m0 -= ((m1 << 4) + K[0]) ^ (m1 + s) ^ ((m1 >> 5) + K[1])
			m0 &= msk
			s -= self.DELTA
		ct = ((m0 << (self.BLOCK_SIZE // 2)) + m1) & ((1 << self.BLOCK_SIZE) - 1)
		return l2b(ct)

def solve():
	KEY = bytes.fromhex("850c1413787c389e0b34437a6828a1b2")
	CIPHERTEXT = bytes.fromhex("b36c62d96d9daaa90634242e1e6c76556d020de35f7a3b248ed71351cc3f3da97d4d8fd0ebc5c06a655eb57f2b250dcb2b39c8b2000297f635ce4a44110ec66596c50624d6ab582b2fd92228a21ad9eece4729e589aba644393f57736a0b870308ff00d778214f238056b8cf5721a843")
	cipher = Cipher(KEY)
	flag = cipher.decrypt(CIPHERTEXT).decode()
	print(f"Flag: {flag}")

if __name__ == '__main__':
	solve()

Dernière mise à jour

Cet article vous a-t-il Γ©tΓ© utile ?