[CryptoHack] Lazy CBC

💡 나는 게으른 개발자이고 CBC 암호화가 작동하기를 원합니다. 초기화 벡터에 대한 이 모든 이야기는 무엇입니까? 중요하게 들리지 않습니다.

음,, iv와 관련해서 문제가 있을 것 같은데.. 일단 문제를 보자.

from Crypto.Cipher import AES


KEY = ?
FLAG = ?


@chal.route('/lazy_cbc/encrypt/<plaintext>/')
def encrypt(plaintext):
    plaintext = bytes.fromhex(plaintext)
    if len(plaintext) % 16 != 0:
        return {"error": "Data length must be multiple of 16"}

    cipher = AES.new(KEY, AES.MODE_CBC, KEY)
    encrypted = cipher.encrypt(plaintext)

    return {"ciphertext": encrypted.hex()}


@chal.route('/lazy_cbc/get_flag/<key>/')
def get_flag(key):
    key = bytes.fromhex(key)

    if key == KEY:
        return {"plaintext": FLAG.encode().hex()}
    else:
        return {"error": "invalid key"}


@chal.route('/lazy_cbc/receive/<ciphertext>/')
def receive(ciphertext):
    ciphertext = bytes.fromhex(ciphertext)
    if len(ciphertext) % 16 != 0:
        return {"error": "Data length must be multiple of 16"}

    cipher = AES.new(KEY, AES.MODE_CBC, KEY)
    decrypted = cipher.decrypt(ciphertext)

    try:
        decrypted.decode() # ensure plaintext is valid ascii
    except UnicodeDecodeError:
        return {"error": "Invalid plaintext: " + decrypted.hex()}

    return {"success": "Your message has been received"}

 

소스코드이다. 하나씩 살펴보자.

먼저 encrypt 부분은 입력이 16byte의 배수면 그대로 암호화 해준다.

get_flag 부분은 입력한 key가 실제 KEY와 같다면 flag를 보여준다.

receive 부분은 encrypt와 비슷하게 입력이 16byte의 배수면 그대로 복호화 해준다.

 

그런데 암호화, 복호화하는 부분에서 눈여겨볼 부분이 있다. 바로 iv에 다른 값을 안 쓰고 Key를 그대로 쓴다는 점이다.

 

이전 문제를 풀다가 key와 iv가 같을 때 발생할 수 있는 취약점을 다룬 문제를 본 적이 있었다.

 

 

위의 블로그를 정리하자면 다음과 같다.

  1. 최소 3블록 길이의 평문을 만든다.
  2. 평문을 암호화하고 결과 암호문을 얻습니다.
  3. 0만 포함하도록 암호문의 두 번째 블록을 수정합니다.
  4. 암호문의 세 번째 블록을 첫 번째 블록과 동일하게 수정합니다.
  5. 수정한 암호문을 해독하고 유효하지 않은 평문을 얻습니다.
  6. 유효하지 않은 평문의 첫 번째와 세 번째 블록을 XOR합니다.
  7. 결과는 iv(key)와 동일합니다.

 

위의 블로그를 따라해줬다.

먼저 48byte만큼의 평문을 만들어서 encrypt해준다.

암호문은 15d6ec4f9bfddc3e6af50a9e531037d3926d4dc474a59e386c0002f7e9273aa0379ba03f3ea2ba57f51b89dcb280e480 이다.

해당 암호문의 두번째 블록은 모두 0으로, 세번째 블록은 첫번째 블록으로 만들어준다. 수정하면 다음과 같다.

15d6ec4f9bfddc3e6af50a9e531037d30000000000000000000000000000000015d6ec4f9bfddc3e6af50a9e531037d3

이를 Receive에 넣어서 복호화해준다.

위와 같이 유효하지 않은 평문이라고 나온다.

위의 유효하지 않은 평문에서 첫번째 블록과 세번째 블록을 xor 해준다.

xor 값은 iv이기에 Get_flag에 넣어주면 flag가 나온다!

성공! 어떻게 이것이 가능할까?

먼저 첫번째 블록과 iv를 xor하게 되고, 세번째 블록과 두번째 블록의 암호문을 xor하게 되는데 두번째 암호문이 모두 0이었다. 따라서 이 상태에서 첫번째 블록과 세번째 블록은 같았기에 둘을 xor하면 iv가 나오게 되는 것이다.

따라서 iv는 key와 동일하게 하지 않도록 주의해야 한다.

🚩 flag: crypto{50m3_p30pl3_d0n7_7h1nk_IV_15_1mp0r74n7_?}

 

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤