Python Padding Oracle прерывания скрипта кажется неопределенным?

Я привязываю написать очень простой скрипт, который просто расшифрует один байт текста по этой формуле P′2[K] = Pn[K] ⊕ Cn-1[K] ⊕ C′[K]oracle - это простая функция, которая расшифровывает, а затем проверяет последний байт, чтобы он был равен заполнению 0x15. с p'2[k] просто 0x15(размер заполнения)

def decrypt(cipher):
    dec = aes_context.decryptor()
    text = dec.update(cipher)
    if text[-1] == 0x15:
        return True, "Padding Match"
    else:
        return False, "No Match"

но поведение кажется неопределенным. Цикл представляет собой простой цикл от 0 до 255(количество попыток расшифровать один блок)

number = 0x01
index = 0
while index < 255:
    try_this_block = 0x0.to_bytes(7, "big") + number.to_bytes(1, "big")
    mod_ciphertext = try_this_block + c1
    state, error_text = decrypt(mod_ciphertext)
    if state:
        byte = try_this_block[-1] ^ 0x15 ^ c1[-1]
        text_back += byte.to_bytes(1, "big")
        break
    else:
        number += 1
    index += 1

Зашифрованное сообщение представляет собой всего лишь 8-байтовую строку + 8-байтовое заполнение и каждый раз расшифровывается с использованием одного и того же ключа и IV. где c1, c2 соответствуют зашифрованному тексту m1, m2

m1 = b"khaled G"
m2 = 0x00.to_bytes(7, "big") + 0x015.to_bytes(1, "big")

ВЕСЬ ИСХОДНЫЙ КОД ЗДЕСЬ:

from cryptography.hazmat.primitives.ciphers import algorithms, modes, Cipher
from cryptography.hazmat.backends import default_backend
import os

m1 = b"khaled G"
m2 = 0x00.to_bytes(7, "big") + 0x015.to_bytes(1, "big")

aes_context = Cipher(algorithms.AES(os.urandom(16)), modes.CBC(os.urandom(16)), default_backend())
enc = aes_context.encryptor()

c1 = enc.update(m1)
c2 = enc.update(m2)
c1, c2 = c2[0:8], c2[8:]

def decrypt(cipher):
    dec = aes_context.decryptor()
    text = dec.update(cipher)
    if text[-1] == 0x15:
        return True, "Padding Match"
    else:
        return False, "No Match"

text_back = b""
number = 0x01
index = 0
while index < 255:
    try_this_block = 0x0.to_bytes(7, "big") + number.to_bytes(1, "big")
    mod_ciphertext = try_this_block + c1
    state, error_text = decrypt(mod_ciphertext)
    if state:
        byte = try_this_block[-1] ^ 0x15 ^ c1[-1]
        text_back += byte.to_bytes(1, "big")
        break
    else:
        number += 1
    index += 1
print("text is {}".format(text_back))

1 ответ

Решение

Я думаю, что в коде есть две проблемы:

  • Во-первых, AES используется для генерации зашифрованного текста длиной один блок или 16 байтов. Затем этот 16-байтовый блок зашифрованного текста разделяется на два 8-байтовых блока, которые рассматриваются как 2 отдельных блока зашифрованного текста для атаки оракула заполнения. Однако это не допускается в контексте отношений.P2 = C1 xor (C1' xor P2')поскольку предполагаемых отношений между двумя блоками не существует, см., например, здесь. На этом этапе я не хочу исключать возможность того, что для вашего случая существует алгоритм, но он будет другим.
    Проблему можно решить, используя размер блока, равный размеру алгоритма. Например, для размера блока 8 байтов можно использовать TripleDES. В качестве альтернативы при использовании AES необходимо использовать размер блока в 16 байт. В первом случае необходимо отрегулировать ключ и размер IV и, кроме того, линиюc1, c2 = c2[0:8], c2[8:] устарело:

    aes_context = Cipher(algorithms.TripleDES(os.urandom(24)), modes.CBC(os.urandom(8)), default_backend())
    ...
    c1 = enc.update(m1)
    c2 = enc.update(m2)
    #c1, c2 = c2[0:8], c2[8:]
    ...
    
  • Во-вторых, при расшифровке не учитывается IV. Если первый блок (c1) подлежит расшифровке, предыдущий тестовый блок не соответствует c1 (как в текущем коде), но чтобы c0а это IV. Для решения проблемы необходимы следующие изменения:

    iv = os.urandom(8)
    aes_context = Cipher(algorithms.TripleDES(os.urandom(24)), modes.CBC(iv), default_backend())
    ...
    c1 = enc.update(m1)
    c2 = enc.update(m2)
    #c1, c2 = c2[0:8], c2[8:]
    ...
    byte = try_this_block[-1] ^ 0x15 ^ iv[-1]
    

    С этими изменениями ожидаемый результат (G) получается.

При использовании AES и размере блока 16 байт данные должны быть соответственно дополнительно изменены.

  • Например, следующие изменения также дают ожидаемый результат (G):

    m1 = b"khaled Gkhaled G"
    m2 = 0x00.to_bytes(15, "big") + 0x015.to_bytes(1, "big")
    ...
    iv = os.urandom(16)
    aes_context = Cipher(algorithms.AES(os.urandom(16)), modes.CBC(iv), default_backend())
    ...
    c1 = enc.update(m1)
    c2 = enc.update(m2)
    #c1, c2 = c2[0:8], c2[8:]
    ...
    try_this_block = 0x0.to_bytes(15, "big") + number.to_bytes(1, "big")
    ...
    byte = try_this_block[-1] ^ 0x15 ^ iv[-1]
    

Редактировать:

  • Если алгоритм / размер блока (AES, 16 байт) и тестовые данные не должны быть изменены, то тестовые данные будут формировать только один блок открытого текста (состоящий изm1 а также m2) вместо двух. В этом случае,0x15возвращается как результат, поскольку байт заполнения теперь является последним байтом блока. Что касается необходимых изменений кода, следует отметить, что при шифровании только одного блока открытого текста создается только один блок зашифрованного текста (c1):

    iv = os.urandom(16)                                                                     
    aes_context = Cipher(algorithms.AES(os.urandom(16)), modes.CBC(iv), default_backend())  
    ...
    c1 = enc.update(m1)  # empty
    c1 += enc.update(m2) # provides full block
    #c1, c2 = c2[0:8], c2[8:]
    ...
    try_this_block = 0x0.to_bytes(15, "big") + number.to_bytes(1, "big")
    ...
    byte = try_this_block[-1] ^ 0x15 ^ iv[-1]
    

    Это возвращается как результат 0x15.

Другие вопросы по тегам