Flipping Cookie

문제

내 웹사이트에서 쿠키를 구할 수는 있지만, 플래그값을 읽는 데는 도움이 되지 않을 거라고...생각합니다.

https://aes.cryptohack.org/flipping_cookie/

 

풀이

더보기
from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad, unpad
from datetime import datetime, timedelta


KEY = ?
FLAG = ?


@chal.route('/flipping_cookie/check_admin/<cookie>/<iv>/')
def check_admin(cookie, iv):
    cookie = bytes.fromhex(cookie)
    iv = bytes.fromhex(iv)

    try:
        cipher = AES.new(KEY, AES.MODE_CBC, iv)
        decrypted = cipher.decrypt(cookie)
        unpadded = unpad(decrypted, 16)
    except ValueError as e:
        return {"error": str(e)}

    if b"admin=True" in unpadded.split(b";"):
        return {"flag": FLAG}
    else:
        return {"error": "Only admin can read the flag"}


@chal.route('/flipping_cookie/get_cookie/')
def get_cookie():
    expires_at = (datetime.today() + timedelta(days=1)).strftime("%s")
    cookie = f"admin=False;expiry={expires_at}".encode()

    iv = os.urandom(16)
    padded = pad(cookie, 16)
    cipher = AES.new(KEY, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(padded)
    ciphertext = iv.hex() + encrypted.hex()

    return {"cookie": ciphertext}

먼저 코드를 보면

check_admin의  iv값과 cookie 값을 가져오고 진수화를 시키고,

cookie 값을 복호화를 시키고, 패딩을 풀어준 후, unpadded에 다.

그리고 unpadded변수에서 ";"로 나눠준 후, "admin=true"인지 확인 후

플래그값을 준다.

 

get_cookie에선

expires_at변수에 현재 날짜와 시간에 대한 정보를 저장한다.

cookie변수에는 함수를 호출할 수 있는 문자열에 "admin=false"과 expired_at변수를 넣고, 암호화를 한다.

 

iv변수는 16바이트의 임의값을 넣고

cookie변수에 16바이트의 패딩을 하고,

CBC암호화를 통해

encypted변수에 padded변수를 암호화시켜

ciphertext에 iv값과 암호화한 값을 붙여

반환한다.

https://resources.infosecinstitute.com/topic/cbc-byte-flipping-attack-101-approach/

 

이 문제는 바이트 플리핑 공격을 사용하게 되는데

바이트 플리핑 공격은  암호문의 바이트를 손상시켜 일반 텍스트의 바이트를 변경한다.

 

위 그림을 보면 두 번째 블록의 복호화를 할때

두 번째 암호블록과 키와 함께 복호화를 시키고, 두 번째 암호문과 xor연산을 하여 평문을 보여준다.

첫번째 암호문의 중간의 비트를 바꿔주어 평문에 다른 값을 바꿀 수 있다.

.

p0 =iv ^ d(c0)
d(c0) =p0 ^ iv

p`0 = iv` ^ d(c0)
iv` = p`0 ^ p0 ^ iv

 

첫번째 암호문의 경우 iv중 일부를 바꾸거나 변형시켜 이를 원하는 평문을 나타낼 수 있고

이에 대한 식을 보여준다면

 

p0는 평문, d는 복호화, c0는 암호문,

p`0는 원하는 평문으로 가정하면

 

p0 = iv ^ d(c0) 인데,

p`0= iv' ^ d(c0)인데,

 

이때 iv`값은, 

iv`= p`0 ^ d(c0)이다.

 

이때 d(c0)값은

d(c0)p0 ^ iv값이기 때문에

iv` = p`0 ^ p0 ^ iv

 

즉 평문인p0"admin=False"

우리가 원하는 평문인p'0"admin=True"라고 한다면

iv`값은 "admin=True" ^  "admin=False" ^ iv 이므로

 

 

get_cookie를 실행해

쿠키값인  "3cb187419c45c338adaa5f0019c398511020494c6588460121765cab4cf83950c7d09992dba4200a5d708807dd439399"

을 얻고

이때 앞 16바이트 부분이 iv 값이기에

3cb187419c45c338adaa5f0019c39851iv값이다.

그래서 iv값과 "admin=False", "admin=True" 를 xor연산을 하면 변형된 iv값이 나올 것이다.

from Crypto.Util.number import *
from pwn import xor

c = '3cb187419c45c338adaa5f0019c398511020494c6588460121765cab4cf83950c7d09992dba4200a5d708807dd439399'
iv = bytes.fromhex((c[:32]))
c_some = c[32:]
other_iv = xor(iv,b'admin=False;',b'admin=True;').hex() #check_admin()함수에서 ;로 나누기 때문에 넣음
print("cookie : "+c_some)
print("other_iv : "+other_iv)

 

쿠키값과

변형 시킨 iv값3cb187419c45d12bb4bc015a1cca9c56이다.

그래서 쿠키값과 변형 시킨 iv값을 대입하였더니

 

플래그값이 나왔다.

플래그값은 crypto{4u7h3n71c4710n_15_3553n714l}이다.

복사했습니다!