본문 바로가기

Dreamhack/CryptoPS

[Dreamhack][CryptoPS] Textbook-CBC

728x90

1.문제 분석

  • CBC 모드의 Key를 구하는 문제
  • IV와 Key가 같을 때를 가정한 문제
  • 임의의 문장에 대한 암복호화가 가능한 문제

2. 기본 아이디어

  1. XOR의 특성상 text ^ 0 = text임을 이용한다.
  2. key를 구했다면 flag를 추출해 복호화한다.

3.문제 풀이

 

이 문제에서는 다음과 같은 파이썬 코드가 주어진다.

 

from Crypto.Util.Padding import pad, unpad
from random import choices, randint
from Crypto.Cipher import AES

BLOCK_SIZE = 16
flag = open("flag", "rb").read()
key = bytes(randint(0, 255) for i in range(BLOCK_SIZE))

encrypt = lambda pt: AES.new(key, AES.MODE_CBC, key).encrypt(pad(pt, BLOCK_SIZE))
decrypt = lambda ct: unpad(AES.new(key, AES.MODE_CBC, key).decrypt(ct), BLOCK_SIZE)

print("Welcome to dream's AES server")
while True:
    print("[1] Encrypt")
    print("[2] Decrypt")
    print("[3] Get Flag")

    choice = input()

    if choice == "1":
        print("Input plaintext (hex): ", end="")
        pt = bytes.fromhex(input())
        print(encrypt(pt).hex())

    elif choice == "2":
        print("Input ciphertext (hex): ", end="")
        ct = bytes.fromhex(input())
        print(decrypt(ct).hex())

    elif choice == "3":
        print(f"flag = {encrypt(flag).hex()}")
        exit()

    else:
        print("Nope")

 

간단하게 3번에서 flag를 추출해 냅다 2번에서 디코딩하려했지만 exit() 때문에 불가능하다.

 

그렇다면 1번과 2번을 통해 key를 구할 수 밖에 없다.

 

IV를 구하는 방법은 다음과 같다.

 

[1]. 블록사이즈가 16이므로 "00"을 16바이트만큼 채워서 encrypt하면 enc(IV)가 나올것이다.

[2]. 결과값을 디코딩할 때, 똑같은 key를 사용하므로 "00" * 16 + enc(IV)를 해준다.

[3]. [2]의 과정을 거치면 dec(0 ^ enc(IV)) = dec(enc(IV)) = IV = key가 도출된다.

 

드디어 나온 key값을 flag를 복호화 할 때 사용해주면 된다.

 

익스플로잇 코드는 다음과 같다.

 

from random import choices, randint
from Crypto.Cipher import AES

BLOCK_SIZE = 16

encrypt = lambda pt: AES.new(key, AES.MODE_CBC, key).encrypt(pad(pt, BLOCK_SIZE))
decrypt = lambda ct: unpad(AES.new(key, AES.MODE_CBC, key).decrypt(ct), BLOCK_SIZE)

r = remote("host3.dreamhack.games", 9302)

r.sendlineafter("[3] Get Flag\n", "1")
r.sendlineafter("(hex): ", "00" * 16)
b = bytes.fromhex(r.recvline(keepends=False).decode())

r.sendlineafter("[3] Get Flag\n", "2")
r.sendlineafter("(hex): ", "00" * 16 + b.hex())

key = bytes.fromhex(r.recvline(keepends=False).decode())[16:32]

r.sendlineafter("[3] Get Flag\n", "3")
r.recvuntil("flag = ")
ct = bytes.fromhex(r.recvline(keepends=False).decode())

print(decrypt(ct))

 

key에서 16:32만 따로 빼준 이유는 앞에 00 또한 함께 디코딩되어 있기 때문에 key만 추출해준 것이다.

728x90