[CryptoHack] ECB Oracle

💡 ECB는 각 일반 텍스트 블록이 완전히 독립적으로 암호화되는 가장 간단한 모드입니다. 이 경우 입력 내용이 비밀 플래그 앞에 추가되고 암호화됩니다. 암호 해독 기능도 제공하지 않습니다. 아마도 "ECB 오라클"이 있을 때 패딩 오라클이 필요하지 않을까요?

이번에는 입력에 뭐가 붙고 암호화되는 것 같다. decrypt도 안해준단다. 근데 ECB Oracle이 있다고? 일단 문제를 보자.

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad


KEY = ?
FLAG = ?


@chal.route('/ecb_oracle/encrypt/<plaintext>/')
def encrypt(plaintext):
    plaintext = bytes.fromhex(plaintext)

    padded = pad(plaintext + FLAG.encode(), 16)
    cipher = AES.new(KEY, AES.MODE_ECB)
    try:
        encrypted = cipher.encrypt(padded)
    except ValueError as e:
        return {"error": str(e)}

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

 

내가 입력하는 plaintextdp FLAG를 붙인 다음 블록크기에 맞게 패딩을 해주고 ECB로 암호화하고 있다.

제일 먼저 input에 1byte씩 늘려가면서 FLAG의 크기가 몇인지 가늠했다.

입력이 7byte가 되었을 때 output의 길이가 16byte 더 길어졌기 때문에 FLAG는 26byte임을 알 수 있다.

그 다음 출력을 그대로 입력에 넣으면 뭐가 되지 않을까 했지만,, decrypt와 encrypt는 다르기에 별다른 효과는 없었다.

구글링을 하다가 아래의 블로그를 찾았다.

padding oracle attack이다. 간단하게 말하자면 패딩을 통해 한 글자씩 알아내는 것이다.

위의 공격을 보여주는 간단한 예시이다. A를 15byte만 보냈을 때와 A 15byte + c 1byte를 보냈을 때 첫 번째 블록의 암호문이 동일하다. 이는 FLAG의 첫 글자 역시 c이기 때문에 그렇다.

따라서 나도 A를 31byte 보냈다가 점차 줄여나가면서 flag를 한 글자씩 알아내면 될 것 같다.

이를 위해서는 먼저 웹과 통신을 해야한다.

개발자도구를 통해 살펴보니 url은 위와 같이 전송되고, 응답은 json 형태로 전달된다.

따라서 request로 받은 것을 json()으로 바꿔주고, 내가 원하는 두 블록 크기인 32byte만큼(64) 잘라주면 된다.

import requests
from tqdm import tqdm

flag=b''
url = 'https://aes.cryptohack.org/ecb_oracle/encrypt/'

for i in range(0,32):
    plaintext=b'x41' * (31-i)
    print(f'for oracle {plaintext}')
    test=url+plaintext.hex()+'/'
    rsp=requests.get(test)
    js=rsp.json()
    check=js['ciphertext'][:64]
    plaintext+=flag

    for j in tqdm(range(33,127)):
        j=chr(j)
        flag_test=plaintext+j.encode()
        print(f'for test {flag_test}')
        test=url+flag_test.hex()+'/'
        rsp=requests.get(test)
        js=rsp.json()
        flag_check=js['ciphertext'][:64]

        if(check==flag_check):
            flag+=j.encode()
            print(flag)
            break

print(flag)

 

코드는 다음과 같다. check가 input을 하나씩 줄이고 실제 flag를 하나씩 더 추가시키는 부분이며, flag_check는 추가된 실제 flag를 brute forcing을 통해 알아내는 부분이다. 하나씩 보내다가 맞으면 반복문을 끝내고 flag에 추가한다.

코드 실행결과는 다음과 같다.

🚩 flag: crypto{p3n6u1n5_h473_3cb}

 

댓글 달기

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

위로 스크롤