[Cryptohack]Lazy CBC

0awawa0
3 min readSep 3, 2020

--

Ещё одно интересное задание с Cryptohack.org. Атака на AES в режиме CBC.

Здесь нам даны три функции:

  1. encrypt — зашифровывает переданный нами открытый текст.
  2. get_flag — проверяет переданный нами ключ, и если он совпадает с ключом шифрования, то мы получаем флаг.
  3. receive — расшифровывает переданный шифротекст и пытается декодировать байты в строку. При успешном декодировании мы получаем сообщение об успехе. Иначе — сообщение об ошибке и расшифрованный текст в хексах.

Значит здесь мы должны получить ключ шифрования и передать его в функцию get_flag. Как же нам это сделать? Здесь есть один очень важный момент — в качестве инициализирующего вектора ленивый программист использовал ключ шифрования:

Это создаёт уязвимость, с помощью которой мы легко можем получить ключ. Давайте разбираться как.

Посмотрим ещё раз на схему расшифровки:

Наше внимание здесь падает на операцию XOR. Что с ней не так? Как раз то, что вместо IV используется ключ:

Для того, чтобы понять, как нам это использовать, давайте введём переменную T — это текст после блока расшифровки, но до ксора с ключом. Тогда открытый текст P будет равен T ^ Key:

Совершенно очевидно, что Key = P ^ T. Но у нас нет значения T, а мы очень хотим его получить. Что можно сделать? Тут на помощь приходит схема CBC. Смотрите, если предыдущий блок шифротекста содержит исключительно нули, то следующий блок после расшифровки проксорится с ним, то есть останется неизменным:

Тут становится ясно, что если мы проксорим выход первого и третьего блоков, то получим ключ, который нам так нужен.

Атака будет выглядеть так:

  1. Зашифруем 3 блока текста (текст лучше создать из непечатных символов, чтобы наверняка получить ошибку в функции receive).

2. В полученном шифротексте заменяем второй блок нулями, а третий блок меняем на первый. То есть, если мы получили C1, C2, C3. То в функцию receive нам нужно передать C1, 0, C1.

3. Функция receive после расшифровки выдаст нам ошибку и расшифрованный текст (P1, P2, P3). Тут мы берём первый и третий блоки, и ксорим их, получая ключ, Key = P1 ^ P3.

4. Отдаём ключ в функцию get_flag и получаем флаг.

--

--