Write-up/Crypto

[Dreamhack] Insecure Seed

mameul 2024. 10. 31. 21:47
728x90

풀이

먼저 주어진 코드를 살펴보면 아래와 같다.

# DREAMHACK CHALLENGE - INSECURE SEED #

import os
import random
from typing import List

class Seed:
    def __init__ (self) -> None:
        self.a: int = os.urandom(1)

    @staticmethod
    def GenerateSeed() -> List[int]:
    	# seed는 8비트 4개로 이루어진 리스트
        seed: bytearray = bytearray(random.getrandbits(8) for x in range(4))
        return list(seed)
        
    def CalculateKey(self, seed: List[int]) -> bool:
    	# self.a와 seed를 xor 연산하여 key를 계산, self.a는 랜덤 1byte
        key: List[int] = [0] * len(seed)
        
        for i in range(len(seed)):
            result: bytes = bytes([self.a[j] ^ seed[i] for j in range(len(self.a))])
            key[i] = int.from_bytes(result, byteorder='little')
        return key
    
    def Authentication(self, seed: List[int], k: List[int]) -> bool:
    	# CalculateKey로 생성된 키와 입력된 키가 일치하는지 비교하여 
        # 일치하면 Corret!!, 일치하지 않으면 Invalid Key!!를 출력
        key = self.CalculateKey(seed)
        
        if key == k:
            print('Correct!!')
            return True
        else:
            print('Invalid Key!!')
            return False

if __name__ == "__main__":
# 제공해주는 seed 값을 토대로 key를 계산하여 입력하면 flag가 출력됨
    s = Seed()
    
    seed = s.GenerateSeed()
    print(f"Seed: {seed}")
    
    while 1:
        k = input("Key: ").strip().split() # input ex) 41 42 43 44
        kl = [int(x) for x in k]
        
        if s.Authentication(seed, kl):
            break
    
    print('DH{fake_flag}')

문제에서 키는 a와 seed를 xor하는 단순한 형태로 계산된다. seed는 주어지므로 a에 해당하는 랜덤 1byte의 숫자를 알면 올바른 키를 구할 수 있다. a의 값의 범위가 크지 않으므로 브루트포싱으로 문제가 해결 가능하다.

 

실행을 해보면 아래와 같이 seed가 주어지고, 키 값을 입력할 수 있는 것을 확인할 수 있다.

 

익스플로잇 코드는 아래와 같이 작성하였다.

from pwn import *
import re

p = remote("host3.dreamhack.games",12983)

tmp = p.recvline().decode()

seed = list(map(int, re.findall(r'\d+', tmp))) # 제공된 seed를 list로 받아옴

for i in range(256): # a 브루트포스
    key = []
    for j in range(len(seed)):
        key.append(str(i^int(seed[j])).encode()) # a값과 seed를 xor하여 key를 구함
    
    s = b''
    for i in range(len(key)):
        s += key[i] + b' '
    p.sendlineafter('Key:', s)
    
    answer = p.recvline()
    if answer != b' Invalid Key!!\n': #Invaild가 나오는 않는 경우에 for문을 끝내고 출력값을 확인함
        break 

p.interactive()

 

위 코드를 실행하면 아래와 같이 flag를 확인할 수 있다.

 

* seed 리스트를 받아오는 부분에서 gpt의 도움을 받았는데 아주 신박하다 정규표현식을 쓰면 노가다를 안해도 되는구나!