[Crypto] LINE CTF 2021: babycrypto1

­damdam·2024년 8월 22일

CTF

목록 보기
1/2

풀이 준비

먼저, 코드 내용을 한 번 살펴보자.

#!/usr/bin/env python
from base64 import b64decode
from base64 import b64encode
import socket
import multiprocessing

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
import hashlib
import sys

class AESCipher:
    def __init__(self, key):
        self.key = key

    def encrypt(self, data):
        iv = get_random_bytes(AES.block_size)
        self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + self.cipher.encrypt(pad(data, 
            AES.block_size)))

    def encrypt_iv(self, data, iv):
        self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + self.cipher.encrypt(pad(data, 
            AES.block_size)))

    def decrypt(self, data):
        raw = b64decode(data)
        self.cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size])
        return unpad(self.cipher.decrypt(raw[AES.block_size:]), AES.block_size)

flag = open("flag", "rb").read().strip()

COMMAND = [b'test',b'show']

def run_server(client, aes_key, token):
    client.send(b'test Command: ' + AESCipher(aes_key).encrypt(token+COMMAND[0]) + b'\n')
    client.send(b'**Cipher oracle**\n')
    client.send(b'IV...: ')
    iv = b64decode(client.recv(1024).decode().strip())
    client.send(b'Message...: ')
    msg = b64decode(client.recv(1024).decode().strip())
    client.send(b'Ciphertext:' + AESCipher(aes_key).encrypt_iv(msg,iv) + b'\n\n')
    while(True):
        client.send(b'Enter your command: ')
        tt = client.recv(1024).strip()
        tt2 = AESCipher(aes_key).decrypt(tt)
        client.send(tt2 + b'\n')
        if tt2 == token+COMMAND[1]:
            client.send(b'The flag is: ' + flag)
            client.close()
            break

if __name__ == '__main__':
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('0.0.0.0', 16001))
    server.listen(1)

    while True:
        client, address = server.accept()

        aes_key = get_random_bytes(AES.block_size)
        token = b64encode(get_random_bytes(AES.block_size*10))[:AES.block_size*10]

        process = multiprocessing.Process(target=run_server, args=(client, aes_key, token))
        process.daemon = True
        process.start()

위 코드는 Python으로 작성된 간단한 서버 프로그램으로, 클라이언트와 통신하며 AES 암호화와 관련된 기능을 제공한다. 클라이언트는 암호화된 Data를 받아서 복호화한 후, 서버에게 명령을 전송할 수 있다.

드림핵 암호 파트 워게임에는 서버에 접속 후 상호작용하며 받은 정보와 주어진 코드를 바탕으로 푸는 문제가 많다. 코드는 주로 Python 언어로 주어지기 때문에 Python에 대한 이해도 필요하다.

코드를 좀 더 면밀히 살펴보자.

class AESCipher:
    def __init__(self, key):
        self.key = key

    def encrypt(self, data):
        iv = get_random_bytes(AES.block_size)
        self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + self.cipher.encrypt(pad(data, 
            AES.block_size)))

    def encrypt_iv(self, data, iv):
        self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + self.cipher.encrypt(pad(data, 
            AES.block_size)))

    def decrypt(self, data):
        raw = b64decode(data)
        self.cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size])
        return unpad(self.cipher.decrypt(raw[AES.block_size:]), AES.block_size)

먼저 AESCipher 클래스이다.

init 부분에서 initializing이 일어난다. 입력 받은 값을 sefl.key로 설정한다.

encrypt 부분에서는 인자로 self, data를 받는다.

AES 블록 크기에 해당하는 무작위 바이트를 생성하여 iv(initial vecter, 초기 벡터)에 할당한다.

self.key와 iv를 사용하고, AES.new를 호출하여 암호화를 위한 AES 암호화 객체를 생성한다.
(AES.MODE_CBC : AES-CBC 모드를 사용한다. CBC 모드는 이전 블록의 암호문과 현재 블록의 평문을 XOR 연산한 후에 암호화를 수행한다. )

pad를 이용하여 평문 데이터를 패딩하고, self.cipher.encrypt를 호출하여 패딩된 데이터를 암호화한다.

iv + self.ciphter.encrypt 를 수행하여 초기화 벡터와 암호문을 합친 후 base64로 인코딩하여 암호화된 결과를 반환한다.

=> 요약 : 주어진 데이터를 AES-CBC 모드로 암호화하고, 초기화 벡터와 암호문을 합친 후 base64로 인코딩하여 반환한다.

encrypt_iv 부분에서는 주어진 데이터를 특정 초기화 벡터를 사용하여 AES-CBC 모드로 암호화하고, 초기화 벡터와 암호문을 합친 후 base64로 인코딩하여 반환한다.

decrypt 부분에서는 암호문 데이터가 base64로 인코딩 되어 있으므로 디코딩 한 후에 주어진 암호문 데이터를 복호화하고, 패딩을 제거하여 평문 데이터를 반환한다.

run_server 부분은 클라이언트와의 통신을 처리한다.
클라이언트에게 초기화 메시지를 보내고, 암호화 및 복호화를 수행한다.
클라이언트로부터 명령을 받아 처리하고, 그 응답으로 flag를 전송한다.

주요 동작 요약:
서버가 실행되면 클라이언트의 연결을 기다린다.
클라이언트가 연결되면, 서버는 AES 키와 토큰을 생성한다.
run_server 함수가 별도의 프로세스에서 실행된다.
서버는 토큰과 명령어 'test'를 암호화한 결과로 초기화 메시지를 클라이언트에게 보낸다.
클라이언트는 초기화 메시지를 받고, 암호화된 데이터를 복호화하여 토큰과 명령어를 확인한다.
클라이언트는 서버에게 명령을 보내고, 서버는 해당 명령을 복호화하여 처리한다.
만약 클라이언트가 'show' 명령을 보내면, 서버는 flag를 암호화하여 클라이언트에게 전송한다.

풀이

Block Cipher에 대한 이해가 필요한 문제이다.

암호화 구조를 보면, 16byte씩 나눠 iv로 XOR 하고 key로 암호화를 한다. 그리고 뒤 데이터는 앞 cipher text를 iv 값으로 사용한다.

token(160) + test(4) + pad(14) 마지막 뒤에 test+pad 를 암호화할 때는 token의 마지막 16byte를 암호화한 데이터를 iv로 사용해서 XOR연산하고 암호화를 한다.

즉, token의 마지막 16byte를 iv로 사용해서 show 를 암호화한 후. test 암호화된 부분을 빼고 show 암호화 데이터를 보내면, flag를 얻을 수 있다.

profile
EWHA CS 22

0개의 댓글