ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Clear] LINE CTF 2021 babycrypto1 Writeup
    CTF/LINE CTF 2021 2021. 3. 22. 20:34

    주어진 문제 파일을 분석해보자.

    #!/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 = "flag!!"
    
    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')
        ##### ..................................................................... (2)
        client.send(b'**Cipher oracle**\n')
        client.send(b'IV...: ')
        iv = b64decode(client.recv(1024).decode().strip())
        ##### ..................................................................... (3)
        client.send(b'Message...: ')
        msg = b64decode(client.recv(1024).decode().strip())
        ##### ..................................................................... (4)
        client.send(b'Ciphertext:' + AESCipher(aes_key).encrypt_iv(msg,iv) + b'\n\n')
        ##### ..................................................................... (5)
        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]:
            ##### ..................................................................... (6)
                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] 
    		##### ..................................................................... (1)
            
            process = multiprocessing.Process(target=run_server, args=(client, aes_key, token))
            process.daemon = True
            process.start()

    (1) 160길이의 token을 생성한다. ( CBC 방식의 AES의 블록의 크기는 일반적으로 16byte이다. )

    (2) token+b'test' 를 암호화하여 사용자에게 test Command 로 제공한다.

    (3) 사용자에게 IV를 입력받는다.

    (4) 사용자에게 암호화 할 메시지 값을 입력받는다.

    (5) (3), (4)를 통해 암호화 된 값을 사용자에게 Ciphertext로 제공한다.

    (6) 사용자에게 입력받은 값을 복호화한 값이 token+b'show' 이면 플래그를 획득한다.

     

    처음에는 패딩 오라클 공격을 활용한 문제일 것이라고 생각하였으나 위와 같이 코드를 분석해보니  token+b'show' 일때의 암호문 을 찾는 문제였다.

    (패딩 오라클 관련 문제에 관심이 있다면 아래 문제를 추천한다.)

    dreamhack.io/wargame/challenges/127/

     

    Padding Oracle

    Description 작성했던 메모를 볼 수 있도록 도와주는 API 서비스 입니다. 관리자가 Secret키를 잊어버리지 않도록 글을 작성 해두었습니다. 공격을 통해 관리자가 써둔 글을 읽어주세요!

    dreamhack.io

     

    문제 해결을 위해서는 우선 CBC 형식 암호화에 대해 알아야 한다.

    CBC 방식 암호화는 다음과 같이 수행된다.

    1. 평문을 각 블록으로 나눈다.

    2. 초기 벡터와 첫 평문블록을 XOR한 후 암호화를 수행하여 첫 번째 암호블록을 만든다.

    3. 생성된 암호블록과 다음 평문 블록을 XOR한 후 암호화를 수행하여 두 번째 암호 블록을 만든다.

    4. 3의 과정을 반복한다.

     

    우선 구해야 하는 token+b'show' 를 블록으로 나누어 보자.

    [16BYTE] * 10 (token) + [b'show'+padding] 으로 구성 될 것이다.

    (padding이란 마지막 블록의 크기가 16byte가 안 될 경우 크기를 맞추기 위해 넣어주는 값이다.) 

     

    [16BYTE] * 10 (token)의 암호문은 제공된 test Command 에서 마지막 블록을 제거한 부분이 된다.

    여기에, b'show' 블록의 암호문을 구해서 붙이면 될 것이다.

     

    token+b'show' 블록의 암호문을 생성하는 과정을 생각해보자. 마지막 블록인 b'show' 블록의 암호문 계산 과정은

    ( [이전 블록의 암호문] XOR [b'show 블록] ) -> 암호화

    이다.  

     

    우리는 (3) ~ (5) 기능을 통해 IV를 특정하고, 그 때의 원하는 평문의 암호문을 얻을 수 있다.

    따라서 (3) 에 test Command로 제공된 암호문의 뒤에서 두번째 블록을 입력하고

    (4) 에 b'show' 를 입력하면 IV+[b'show'블록의 암호문] 을 획득 할 수 있기 때문에

     

    Enter your command 에 [test Command 에서 마지막 블록을 제거한 부분] + [b'show'블록의 암호문] 를 입력하면 플래그를 획득 할 수 있다.

     

    < exploit code >

    from pwn import *
    import base64
    
    p=remote("35.200.115.41", 16001)
    
    p.recvuntil("test Command: ")
    test = p.recvuntil("\n**Cipher oracle**\n", drop=True)
    print(test)
    #print(test[-32:-16])
    final_iv = base64.b64decode(test)[:16]
    chiper = base64.b64decode(test)[16:]
    iv_modify = chiper[-32:-16]
    
    print(iv_modify)
    
    p.recvuntil("IV...: ")
    p.send(base64.b64encode(iv_modify))
    p.recvuntil("Message...: ")
    p.send(base64.b64encode(b'show'))
    
    p.recvuntil("Ciphertext:")
    b = p.recvuntil(b'\n', drop=True)
    #print(b)
    b = base64.b64decode(b)
    
    final = base64.b64encode(final_iv+chiper[:-32]+b)
    #print(final)
    p.recv()
    p.send(test)
    p.recv()
    p.send(final)
    p.interactive()

     

     

    FLAG = LINECTF{warming_up_crypto_YEAH}

    반응형

    'CTF > LINE CTF 2021' 카테고리의 다른 글

    [Study] LINE CTF 2021 Your note  (0) 2021.03.29
    [Clear] LINE CTF 2021 babycrypto2 Writeup  (0) 2021.03.22
    [Clear] LINE CTF 2021 diveinternal Writeup  (0) 2021.03.22

    댓글

Designed by Tistory.