ACSC 2021 | CBCB

#acsc2021

#!/usr/bin/env python3

import base64
import json
import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from flag import flag

key = os.urandom(16)
iv1 = os.urandom(16)
iv2 = os.urandom(16)

admin_username = flag[: len(flag) // 2]
secret = flag[len(flag) // 2 :]


def encrypt(msg):
    aes1 = AES.new(key, AES.MODE_CBC, iv1)
    aes2 = AES.new(key, AES.MODE_CBC, iv2)
    enc = aes2.encrypt(aes1.encrypt(pad(msg, 16)))
    return iv1 + iv2 + enc


def decrypt(msg):
    iv1, iv2, enc = msg[:16], msg[16:32], msg[32:]
    aes1 = AES.new(key, AES.MODE_CBC, iv1)
    aes2 = AES.new(key, AES.MODE_CBC, iv2)
    msg = unpad(aes1.decrypt(aes2.decrypt(enc)), 16)
    return msg


def create_user():
    username = input("Your username: ")
    if username:
        data = {"username": username, "is_admin": False}
    else:
        # Default token
        data = {"username": admin_username, "is_admin": True}
    token = encrypt(json.dumps(data).encode())
    print("Your token: ")
    print(base64.b64encode(token).decode())


def login():
    username = input("Your username: ")
    token = input("Your token: ").encode()
    try:
        data_raw = decrypt(base64.b64decode(token))
    except:
        print("Failed to login! Check your token again")
        return None
    
    try:
        data = json.loads(data_raw.decode())
    except:
        print("Failed to login! Your token is malformed")
        return None

    if "username" not in data or data["username"] != username:
        print("Failed to login! Check your username again")
        return None

    return data


def none_menu():
    print("1. Create user")
    print("2. Log in")
    print("3. Exit")

    try:
        inp = int(input("> "))
    except ValueError:
        print("Wrong choice!")
        return None

    if inp == 1:
        create_user()
        return None
    elif inp == 2:
        return login()
    elif inp == 3:
        exit(0)
    else:
        print("Wrong choice!")
        return None


def user_menu(user):
    print("1. Show secret")
    print("2. Log out")
    print("3. Exit")

    try:
        inp = int(input("> "))
    except ValueError:
        print("Wrong choice!")
        return None

    if inp == 1:
        if 'is_admin' in user and user["is_admin"]:
            print(secret)
        else:
            print("_this_is_secret_really}")
        return user
    elif inp == 2:
        return None
    elif inp == 3:
        exit(0)
    else:
        print("Wrong choice!")
        return None


def main():
    user = None

    print("Welcome to CBCBC flag sharing service!")
    print("You can get the flag free!")
    print("This is super-duper safe from padding oracle attacks,")
    print("because it's using CBC twice!")
    print("=====================================================")

    while True:
        if user:
            user = user_menu(user)
        else:
            user = none_menu()


if __name__ == "__main__":
    main()

CBCモードが2段重なっているPadding Oracle Attack

ズレが2ブロックになるだけ

from ptrlib import Socket, chunks
from base64 import b64encode, b64decode


sock = Socket("localhost", 8888)
sock.sendlineafter("> ", "1")
sock.sendlineafter("username: ", "")
token = sock.recvlineafter("token: \n")
blocks = chunks(b64decode(token), 16)

def oracle(msg):
    sock.sendlineafter("> ", "2")
    sock.sendlineafter("username: ", "uouo")
    sock.sendlineafter("token: ", b64encode(msg))
    if "again" in sock.recvlineafter("login!").decode():
        return False
    return True

known_bytes = b""
for i in range(2, len(blocks)):
    known = [0 for _ in range(16)]
    for j in range(16)[::-1]:
        block = [a^b for a, b in zip(blocks[i-2], known)]
        for k in range(j+1, 16):
            block[k] ^= (16 - j)

        for b in range(256):
            block[j] = b
            if oracle( b"".join(blocks[:i-2]) + bytes(block) + blocks[i-1] + blocks[i] ):
                known[j] = b ^ (16 - j) ^ blocks[i-2][j]
                break
    known_bytes += bytes(known)

print(known_bytes)
user = input("[+] username: ")

sock.sendlineafter("> ", "2")
sock.sendlineafter("username: ", user)
sock.sendlineafter("token: ", token)

sock.interactive()