CSE 207B, Fall 2023
Homework 3: CBC-mode padding oracle

The web application located here is a simple password-protected file server that hosts your next homework assignment. The password is too secure to be brute-forced, but there's a flaw in how the server handles encrypted session cookies. An example cookie was recovered in a separate attack, but unfortunately it has expired and cannot be used to access the files.

oKcBPXglr2p+zvm3lxtPVVTWWLpgoBFlDDRl72sZ5L0P0L8+sT22gf32Qd+fxSV3Lp29jVrqEJzKBuya6fUGuSILMvb4pQrLtyfXoqs3UvFhOqgXeVpnmyXqs2Dg6AzfyzM+OOqNND+WJCOrTFP1qw==

The server first Base64-decodes the cookie, attempts to decrypt the binary blob using 128-bit AES in CBC mode with a fixed key, and then removes the PKCS 7 padding from the plaintext. If this succeeds, the server checks the decrypted cookie to see if the user has a valid session. It will throw an error if it encounters any problems in this process.

Your task is to use the technique described in Vaudenay's 2002 paper to recover the plaintext from the above cookie in order to reach the rest of your homework.

Please write your program in Python 3. Your program should take two command-line arguments: the base URL of the web app and a Base64-encoded encrypted cookie. Your program should carry out the padding oracle attack using the HTTP responses from our server and print out the decrypted ciphertext. Gradescope will expect the file to be named hw3-sol.py, and will handle things best if you print only the final output to sys.stdout. Any error checking prints can pe printed to stderr by adding "file=sys.stderr" to the end of the print call (that is, print(message, file=sys.stderr)). For example, your program should work with the following input:

  python hw3-sol.py https://cse207b.nh.cryptanalysis.fun/hw3/ oKcBPXglr2p+zvm3lxtPVVTWWLpgoBFlDDRl72sZ5L0P0L8+sT22gf32Qd+fxSV3Lp29jVrqEJzKBuya6fUGuSILMvb4pQrLtyfXoqs3UvFhOqgXeVpnmyXqs2Dg6AzfyzM+OOqNND+WJCOrTFP1qw==

I highly recommend writing your own encryption/decryption oracle in order to debug your attack logic before attempting to launch the attack against our server.

Please submit your code and a short description of how you solved the problem along with a PDF named hw3-solutions.pdf of your LaTeXed solutions to the other problems to Gradescope before class on Tuesday, October 18. You may discuss this assignment in small groups with classmates, but please code and write up your solutions yourself. Please credit any collaborators you discussed with and any references you used.

For reference, here is some framework code that will print out the HTTP status and decoded query string for a URL supplied on the command line, along with the encryption, decryption, and padding functions the server uses. (Note: if you're getting a ModuleNotFoundError about 'Crypto', you can pip install pycryptodome.)

#!/usr/bin/env python3

from Crypto.Cipher import AES
from Crypto import Random
import requests
import base64
import sys

def pad(msg,blocksize=16):
    n = blocksize-(len(msg)%16)
    return msg+bytes([n]*n)

def strip_padding(msg):
    padlen = msg[-1]
    assert 0 < padlen and padlen <= 16
    assert msg[-padlen:] == bytes([padlen]*padlen)
    return msg[:-padlen]

def enc(key,msg):
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(pad(msg))

def dec(key,msg):
    cipher = AES.new(key, AES.MODE_CBC, msg[:16])
    text = strip_padding(cipher.decrypt(msg[16:]))
    return text

def get_status(u: str, session: bytes):
    assert isinstance(u, str)
    assert isinstance(session, bytes)

    cookies = {'session': base64.b64encode(session).decode("ascii")}
    req = requests.get(u, cookies=cookies)
    
    print("HTTP Response:", req.status_code)

def solve(url: str, cookie_bytes: bytes) -> str:
    # This method takes a URL and encrypted cookie as input,
    # performs the padding-oracle attack, and returns the
    # decrypted cookie.
    get_status(url, cookie_bytes)
    return "TODO"

if __name__=='__main__':
    url = sys.argv[1]
    encoded_cookie = sys.argv[2]
    cookie_bytes = base64.b64decode(encoded_cookie)
    
    print("Encrypted cookie bytes", cookie_bytes)
    print(solve(url, cookie_bytes))
    exit(0)