또 뭐하지

[Dreamhack] STREAMer-Prototype 본문

Write-up/Crypto

[Dreamhack] STREAMer-Prototype

mameul 2024. 10. 4. 13:36
728x90

풀이

#!/usr/bin/env python3
from cipher import STREAM
import random


if __name__ == "__main__":
    with open("flag", "rb") as f:
        flag = f.read()

    assert flag[:3] == b'DH{' and flag[-1:] == b'}'

    seed = random.getrandbits(16)
    stream = STREAM(seed, 16)

    print(f"encrypted flag > {stream.encrypt(flag).hex()}")

문제의 prob.py 코드를 살펴보면 flag를 암호화하는 것을 확인할 수 있다. 이때 seed를 랜덤 16비트로 사용하고 있고, 그 seed를 STREAM 암호에 입력하고 있는 걸 확인할 수 있다. 이제 STREAM 암호를 확인해보자.

class STREAM:
    def __init__(self, seed, size):
        self.state = self.num2bits(seed, size)

    def num2bits(self, num, size):
        assert num < (1 << size)

        return bin(num)[2:].zfill(size)
    
    def bits2num(self, bits):
        return int('0b' + bits, 2)
    
    def shift(self):
        new_bit = self.state[-1]
        self.state = new_bit + self.state[:-1]

        return new_bit
    
    def getNbits(self, num):
        sequence = ""
        for _ in range(num):
            sequence += self.shift()
        
        return sequence

    def encrypt(self, plaintext):
        ciphertext = b""
        for p in plaintext:
            stream = self.bits2num(self.getNbits(8))
            c = p ^ stream
            ciphertext += bytes([c])

        return ciphertext

    def decrypt(self, ciphertext):
        plaintext = b""
        for c in ciphertext:
            stream = self.bits2num(self.getNbits(8))
            p = c ^ stream
            plaintext += bytes([p])

        return plaintext


if __name__ == "__main__":
    import os

    for seed in range(0x100):
        Alice = STREAM(seed, 16)
        Bob = STREAM(seed, 16)
        plaintext = os.urandom(128)
        ciphertext = Alice.encrypt(plaintext)
        assert plaintext == Bob.decrypt(ciphertext)

cipher.py를 통해 STREAM 암호를 확인해보면 암호화되는 방식은 아래와 같다.

1. 입력된 seed를 통해 self.state를 정의

2. getNbits에서 stream이 될 sequence를 생성

3. 이때, sequence는 self.state를 맨 하위 비트를 반환하고 right rotate를 시키는 방식으로 생성

4. sequence를 변환하여 stream을 생성하고, 평문과 xor하여 암호화

근데 복호화함수를 제공하고 있다!!!!!!!!

그러면 그냥 seed를 알아내서 복호화함수에 집어넣는 전수조사를 하면 될 것 같다.

from cipher import STREAM

with open('./STREAMer-Prototype/output.txt', 'r') as file:
    line = file.readline().strip()  

if line.startswith("encrypted flag >"):
    encrypted_flag = line.split("encrypted flag >")[1].strip()

# 암호문을 hex 문자열에서 바이트로 변환
ciphertext = bytes.fromhex(encrypted_flag)

# 전수조사 시작
for seed in range(0x10000):  # 0부터 65535까지 모든 seed를 시도
    stream = STREAM(seed, 16)  
    decrypted = stream.decrypt(ciphertext)  
    
    # 복호화된 결과가 'DH'로 시작하면 출력하고 종료
    if decrypted.startswith(b"DH"):
        print(f"Found seed: {seed}")
        print(f"Decrypted flag: {decrypted}")
        break

위와 같이 전수조사 코드를 작성해고 실행시켜보면, 아래와 같이 시드와 복호화된 flag를 얻을 수 있다.

'Write-up > Crypto' 카테고리의 다른 글

[Dreamhack] safeprime  (1) 2024.10.24
[Dreamhack] What is This???  (0) 2024.10.04
[Dreamhack] No Shift Please!  (4) 2024.09.27
[Dreamhack] Double DES  (0) 2024.09.20
[Dreamhack] ICM2022  (0) 2024.09.13