This is an old revision of the document!


Homework - BEAST Attack on TLS

Cipher Block Chaining (CBC)

SSLv3/TLS1.0 are protocols to encrypt/decrypt and secure your data. In our case, they both use the CBC cipher mode chaining. The plaintext is divided into block regarding the encryption algorithm (AES, DES, 3DES) and the length is a multiple of 8 or 16. If the plaintext does not fill the length, a padding is added at the end to complete the missing space. In CBC mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted. This way, each ciphertext block depends on all plaintext blocks processed up to that point.

C[i] = Ek(P[i] XOR C[i-1]), C[0] = IV

P[i] = Dk(C[i]) XOR C[i-1], P[0] = IV

To make each message unique, an initialization vector must be used in the first block. When encrypting with CBC mode, the Initialization Vector (IV) is:

  • Random
  • Unpredictable
  • Not secret

CBC encryption CBC decryption

BEAST Attack

In TLS1.0 and SSLv3 the first IV of the request is random, fine. But to gain some time and not generate a new random IV every time, the implementation of TLS1.0 and SSLv3 used the last block of the previous cipher text has an IV. In other words, the IV is now guessable. We will assume the length of each block will be 16 (AES) and the attacker can set up a MITM to retrieve all the ciphertext. Additionally, the attacker can inject malicious code in the victim's browser and can use a socket to send data in the name of the client. This is called a Chosen plaintext attack and can be seen below. Original description: BEAST paper 2011. BEAST Attack

Let's say that we want to find the first character of the COOKIE, we will instruct the victim to encrypt the following message: 'B'*15 + COOKIE. Remember that the block length is 16 bytes, therefore the first block of the message will be 'BBBBBBBBBBBBBBB?' (let's call it m1).

Because this is the first request, the IV will be random. Observing the ciphertext we can extract two informations: 'Ek(m1 XOR IV)' and 'F[-16:]' will be the IV for the second request.

For the second request we know what the IV will be. Let's send the same message as in the first request. We will get: Ek(m1 XOR F[-16:0]) as the first block. Last block of the ciphertext ('S[-16:]') will be the next IV.

For the third request, we will craft a special block P = m_guess XOR F[-16:] XOR S[-16:0], where m_guess is 'B'*15 + our guess for the first character of the cookie. Try to find what the first block of the third ciphertext should look like. Use the technique to find the entire secret cookie.

'beast_attack.py'
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
'''
    TLS BEAST attack - PoC
    Implementation of the cryptographic path behind the attack
'''
 
import random
import binascii
import sys
from Crypto.Cipher import AES
from Crypto import Random
 
SECRET_COOKIE = "ID=3ef729ccf0cc5"
 
"""
    AES-CBC
    function encrypt, decrypt, pad, unpad
    You can fix the IV in the function encrypt() because TLS 1.0 fix the IV
    for the second, third... request (to gain time)
"""
 
def pad(s):
    return s + (16 - len(s) % 16) * chr(16 - len(s) % 16)
 
def unpad(s):
    return s[:-ord(s[len(s)-1:])]
 
# we admit the handshake produce a secret key for the session
# we can use this function without having access to the secret key
def encrypt( msg, iv_p=None):
    raw = pad(msg + SECRET_COOKIE) # SECRET_COOKIE is always appended
    if iv_p is None:
        iv = Random.new().read( AES.block_size )
    else:
        iv = iv_p
    global key
    key = Random.new().read( AES.block_size )
    cipher = AES.new('V38lKILOJmtpQMHp', AES.MODE_CBC, iv )
    return cipher.encrypt( raw )
 
"""
    The PoC of BEAST attack -
    Implementation of the cryptographic path behind the attack
    - the attacker can retrieve the request send be the client 
    - but also make the client send requests with the plain text of his choice
"""
def xor_strings(xs, ys, zs):
    return "".join(chr(ord(x) ^ ord(y) ^ ord(z)) for x, y, z in zip(xs, ys, zs))
 
def xor_block(vector_init, previous_cipher, p_guess):
    xored = xor_strings(vector_init, previous_cipher, p_guess)
    return xored
 
def split_len(seq, length):
    return [seq[i:i+length] for i in range(0, len(seq), length)]
 
 
# the PoC start here        
def run_three_request():
    secret = []
    fixed_known = "ID="
 
    # the part of the request the atacker knows, can be null
    known_so_far = fixed_known
 
    # padding is the length we need to add to known_so_far to create a length of 15 bytes
    padding = 16 - len(known_so_far) - 1
    known_so_far = "a"*padding + known_so_far
    length_block = 16
    secret_length = len(SECRET_COOKIE)
 
    """
        TODO:
        while not found entire cookie:
	    for i in range(256):
                # send first request
                # send second request
                # send third request
 
                if success:
                    # update findings
                    # go to next step
 
        for sending requests use encrypt(chosen_plaintext, iv), keep in mind that SECRET_COOKIE is always appended
        first request should use iv=None to generate a fresh one, next ones should set the iv according to the protocol
    """
 
    return secret
 
 
# the attacker doesn't know the flag
secret = run_three_request()
 
found = ''.join(secret)
print "\n" + found
ic/res/tema1.1542815683.txt.gz · Last modified: 2018/11/21 17:54 by cristian.buza
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0