[CryptoHack] ECB CBC WTF

💡 여기에서 CBC로 암호화할 수 있지만 ECB에서만 해독할 수 있습니다. 모드가 다르다고 해서 약점이 될 수는 없겠죠… 그렇죠?

CBC로 암호화하지만 ECB로만 해독할 수 있다고 한다.. 무슨 말일까? 문제를 보자.

페이지 기본 구성은 같으니 넘어가겠다.

CBC 모드의 구조를 보여주고 있다. 현암시간에 배웠기에 구체적인 설명은 넘어아겠다.

from Crypto.Cipher import AES


KEY = ?
FLAG = ?


@chal.route('/ecbcbcwtf/decrypt/<ciphertext>/')
def decrypt(ciphertext):
    ciphertext = bytes.fromhex(ciphertext)

    cipher = AES.new(KEY, AES.MODE_ECB)
    try:
        decrypted = cipher.decrypt(ciphertext)
    except ValueError as e:
        return {"error": str(e)}

    return {"plaintext": decrypted.hex()}


@chal.route('/ecbcbcwtf/encrypt_flag/')
def encrypt_flag():
    iv = os.urandom(16)

    cipher = AES.new(KEY, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(FLAG.encode())
    ciphertext = iv.hex() + encrypted.hex()

    return {"ciphertext": ciphertext}

 

이번 문제의 소스 코드이다. 정말 말했던 것처럼 encrypt_flag 부분은 CBC로 암호화하고 있는데 decrypt 부분은 ECB로 복호화하고 있다.

 

그런데 encrypt_flag를 보면, 출력해주는 ciphertext는 iv와 encrypted값을 단순히 합치기만 한 값이었다. 따라서 우리는 iv가 뭐였는지 알 수 있다.

그래서 처음에 했던 생각은 그냥 tool을 쓰지 않고 내가 코드를 짜서 CBC로 decrypt하면 되지 않나.. 했지만, 생각해보니 iv를 알아도 이번에는 KEY를 알지 못하기에 해당 방법은 힘들 것 같다는 생각이 들었다.

따라서 ECB와 CBC의 차이점에 대해 생각해보게 되었다. ECB는 그냥 단순히 key를 xor하지만, CBC는 여기에 iv와 이전 블록의 ciphertext를 xor한다. 따라서 CBC로 암호화된 ciphertext를 이전 블록의 ciphertext나 iv와 먼저 xor해서 새로운 ciphertext로 만든다면, 이를 그대로 ECB로 복호화하면 추가로 key가 xor되기에 평문을 얻을 수 있지 않을까?

given='38c2e6f65429c9db2e4641e61c700293fc99a8ce1434890facd9873390775a864ff798bf5bab8517610c07e88273fa70'

iv=given[0:32]
ciphertext=given[32:]
print(f'iv is {iv}, ciphertext is {ciphertext}')

c1=ciphertext[0:32]
c2=ciphertext[32:]
print(c1)
print(c2)

 

먼저 위와 같은 코드로 iv와 암호문을 나눴고, 암호문은 블록 단위로 쪼개줬다.

그 다음 c1(암호문 블록1)은 iv와, c2는 c1과 xor해줬다.

합친 결과는 다음과 같다.

이렇게 해서 tool에 돌렸는데 원하는 값을 못 가져온다.. 아무래도 내부적으로 순서를 어떻게 넣고 돌리는지 등등의 이슈가 있는 듯하다.

하지만 해결방법은 존재한다. 단순하게 순서를 바꿔주면 된다! 먼저 ECB로 복호화를 한 예사평문을 얻고, 그 후에 iv, 이전 암호문 블록과 xor해주면 된다.

ciphertext='80a4e57b7c481c30fde808cd46c748ee6b87f0adbf345546e86f675263c0d12042af7413fb3452730d8c969fb193e437'
plaintext='b27e2d72b6dc594e87fc52a38abacf64e3d69c0b082767039e8a57f833a423db34b3869d8e500a77df30467342e1f05d'

c,p=[],[]
for i in range(0,len(ciphertext),32):
    c.append(ciphertext[i:i+32])
    p.append(plaintext[i:i+32])

print(f'slicing cipher : {c}')
print(f'slicing plain  : {c}')

 

tool을 이용해서 cbc 암호문과 ecb 평문을 얻어냈다. 그 다음 둘을 각각 16byte(128bit)씩 나눠줬다.

iv ^ p1

c1 ^ p2

결과를 합쳐서 hex tool에 넣어주면

flag가 잘 나오게 된다!

🚩 flag: crypto{3cb_5uck5_4v01d_17_!!!!!}

댓글 달기

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

위로 스크롤