LIMB
Вход Регистрация

тема

Padding Oracle: дешифруем AES-CBC без ключа

pikines1337 · · 3 ответов · 371 просмотров

Привет, не знаю зайдет ли такое, но го попробуем, разберём атаку которую обожают CTF-авторы и которую до сих пор встречают в проде - Padding Oracle.

Суть в том, что если сервер по-разному реагирует на "правильный паддинг" и "неправильный паддинг" - мы можем вытащить открытый текст из шифртекста вообще без знания ключа.

Если кто-то не знает, то вот краткая теория:

AES-CBC шифрует данные блоками по 16 байт. Чтобы данные всегда делились ровно на 16, используется PKCS#7 паддинг: если не хватает N байт - добиваем N байтами со значением N.
Code:
"hello" → "hello\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
                       ↑ 11 байт паддинга, каждый = 0x0b

При расшифровке сервер проверяет этот паддинг. И вот тут появляется проблема.



Не удалось прогрузить медиа


Расшифровка в CBC работает так:

Code:
plaintext[i] = AES_decrypt(ct_block[i]) XOR ct_block[i-1]

Промежуточное значение AES_decrypt(ct_block) - назовём его intermediate -нам неизвестно. Но мы можем его вычислить, если можем задавать ct_block[i-1] и смотреть что отвечает сервер.
Не удалось прогрузить медиа



Я написал уязвимый сервер и скрипт атаки на этот сервер
server.py - HTTP сервер с двумя эндпоинтами:


GET /token - выдаёт зашифрованную куку admin=false;user=guest
GET /check?token=HEX - проверяет куку и вот здесь живёт оракул
Python:
def decrypt_and_check(token_hex):
    try:
        raw = bytes.fromhex(token_hex)
        iv  = raw[:16]
        ct  = raw[16:]
        pt  = unpad(AES.new(KEY, AES.MODE_CBC, iv).decrypt(ct), 16)
        return True, pt.decode()
    except ValueError:
        return False, "Invalid padding"  # <- утечка информации

Не удалось прогрузить медиа


Как работает атака​


Атакуем один блок шифртекста. Подбираем последний байт:


  1. Берём предыдущий блок (prev_block), делаем его копию crafted_prev
  2. Меняем последний байт crafted_prev[15] перебором от 0 до 255
  3. Отправляем на сервер crafted_prev + ct_block
  4. Когда сервер вернёт 200 - паддинг \x01 валидный
  5. Значит: crafted_prev[15] XOR intermediate[15] == 0x01
  6. Раскрываем: intermediate[15] = crafted_prev[15] XOR 0x01
  7. Открытый текст: plaintext[15] = intermediate[15] XOR original_prev[15]

Повторяем для каждого байта, меняя целевой паддинг (для второго байта с конца добиваемся \x02\x02, и так далее).

Не удалось прогрузить медиа


После запуска нашего скрипта атаки все прошло как надо

Не удалось прогрузить медиа


Ключ мы так и не узнали. Но открытый текст - наш.

ЧТо имеем в скрипте атаки

Не удалось прогрузить медиа

Максимум запросов: 256 × 16 × N_блоков. Для двух блоков  ~8192 запроса. На локалке занимает секунды.

Что имеем в реальности, где это еще можно увидеть
Классика - ASP.NET ViewState, ну раньше так точно было, щас уже врятли актуально

Там был тот же принцип: сервер отдавал разные ошибки на кривой паддинг и нормальный, и исследователи Rizzo/Duong написали тул POET который автоматизировал атаку.

В CTF встречается регулярно - ищите задачи где есть "зашифрованная сессия" и сервер отвечает по-разному на плохой токен.

Защита

Одна строчка решает проблему - используйте AES-GCM вместо AES-CBC:
Python:
# Уязвимо
AES.new(key, AES.MODE_CBC, iv).encrypt(pad(data, 16))

# Безопасно — GCM аутентифицирует шифртекст, оракула нет
cipher = AES.new(key, AES.MODE_GCM)
ct, tag = cipher.encrypt_and_digest(data)

GCM проверяет целостность до расшифровки. Если шифртекст тронут - ошибка одна и та же, без утечки о паддинге.

З.Ы. Тапками сильно не кидайте, опыт написания статей ~0
👍23 😮6 💯4
Не кидаем). Хорошее описание, практическое и эффективное, как простой пример - сойдет. Единственное что, насколько я помню, в такой своеобразной задаче последний байт может и не подходить к сценарию, и профи фиксят это за счет манипуляций не только с последним, но и с предпоследним байтом, чтобы ну уж точно получить валидку, иначе получится кисель полнейший, а в дальнейшем - ложное срабатывание.
Еще посоветую в последующих статьях упоминать прежде всего практическое применение "в дикой природе", тобишь актуальное применение твоего знания. Такие ошибки в своих вариациях часты у мамкиных криптографов, которые отказываются от чужих фреймворков и грамотных решений, и изобретают свои велосипеды с каким-нибудь pycrypto. Практическую часть лучше показывать именно на таких примерах.

Успехов в дальнейшем творчестве!
👍3
ОтветleakinN_nname

Не кидаем). Хорошее описание, практическое и эффективное, как простой пример - сойдет. Единственное что, насколько я помню, в такой своеобразной задаче последний байт может и не подходить к сценарию, и профи фиксят это…

Спасибо за развернутый фидбек
Да, про фолсы на \x02\x02  абсолютно верное замечание, в скрипте для простоты понимания базы я опустил этап валидации, чтобы не перегружать код (он и так новичкам мозг ломает на этапе XOR'ов :))
Учту в дальнейшем
👍1
Ответpikines1337

Спасибо за развернутый фидбек Да, про фолсы на \x02\x02 абсолютно верное замечание, в скрипте для простоты понимания базы я опустил этап валидации, чтобы не перегружать код (он и так новичкам мозг ломает на этапе XOR'ов…

Просто момент довольно важный, особенно в атаках криптографии, вроде фейл-опенов, вариаций предсказуемой криптографии и так далее. И многие из таких могут ответить мерзеньким фоллз-позитивом).
👍5 ❤️1 🔥1

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.