Lazy CBC

문제

나는 게으른 개발자일이고, 내 CBC 암호화가 작동하기를 원합니다.
초기화 벡터에 대한 이 모든 이야기는 무엇인가요?
중요하게 않은 것 같아요.

풀이

더보기

먼저 제공된 사이트와 코드다.

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"}

 

먼저 코드를 보게 되면

키는 AES-CBC 암호화, 해독을 위해 키와 IV 모두에 사용 되었고

get_flag는 플래그를 가져오기위해선 키가 필요하다.

그리고 주어진 암호 텍스트의 암호해독을 하고,

실패시 암호해독된 메시지가 반환된다.

 

이를 위해 일단 CBC를 보게 되면

평문의 블록은 xor연산을 통해 이전 암호문과 연산되고

첫번째 암호문에 대해서는 IV가 암호문 대신 사용된다.

iv = d(e0)^p0
p0 = d(e0)^iv
p1 = d(e1)^c0 
p2 = d(e2)^c1

먼저 p는 평문, e는 암호문, d는 복호화일때 이러한 형태인데 

이를 복호화 시켜 보자면 이러한 형태인데

p0 = d(e0) ^ iv
p1 = d(0) ^ e0
p2 = d(e0) ^ 0

만약에 e1가 0, c2가 c0이면, 이러한 형태이다.

p0^p2 = d(e0)^iv^d(e0)=iv

p0와 p2를 xor연산을 시키게 되면 iv값을 얻을 수 있다.

p0=(b'a'*(16*3)).hex()
print(p0)

그래서 세 블록을 사용하는 일반텍스트를 만들었다.

 

{"ciphertext":"5afa4a5595e4399f0848191b6ef47378e44f1a1b81fde8ce7e19f544b3efa496f2798f81611b27904b0977c5c0df78c3"}

이를 제공된 사이트에 대입을 하여 암호문을 얻었다.

c='5afa4a5595e4399f0848191b6ef47378e44f1a1b81fde8ce7e19f544b3efa496f2798f81611b27904b0977c5c0df78c3'
fake = c[:32] + '0'*32 + c[:32]
print(fake)
5afa4a5595e4399f0848191b6ef47378000000000000000000000000000000005afa4a5595e4399f0848191b6ef47378

그리고 가짜 암호 텍스트를 만들었더니

두번째 블록에 0으로 채우고 세 번째 블록이 동일하게 암호텍스트를 변경했다.

 

{"error":"Invalid plaintext: 6161616161616161616161616161616160f344aa47cc93d3047b17408d856a2c8458e32c8b4238e1db7f91faeaf7bdf9"}

 이를 대입 하였더니 오류가 뜨며 시도한 서버에서 해독된 평문과 함께 오류가 나왔다.

이를 통해 첫번째 블록인

61616161616161616161616161616161

세번째 블록으로 xor연산을 하였다.

8458e32c8b4238e1db7f91faeaf7bdf9

 키는 ''e539824dea235980ba1ef09b8b96dc98"이다.

그리고 키를 사용하여 get_flag를 요청하면 플래그가 16진수로 반환되는데

이를 해독하면 

플래그는 crypto{50m3_p30pl3_d0n7_7h1nk_IV_15_1mp0r74n7_?}이다.

 

복사했습니다!