[Cryptohack] CTRIME

В этом таске у нас CRIME атака на AES в режиме CTR (Counter). Интересной особенностью этого режим является то, что фактически шифрование производится по схеме OTP (one time pad), он же XOR. Таким образом мы получаем самый обычный потоковый шифр с AES в качестве генератора гаммы.

Так как это потоковый шифр, длина данных для шифрования может быть произвольной и не должа быть кратной размеру блока AES.

Теперь посмотрим на саму задачу:

Всё, что нам доступно — это функция шифрования, в которую мы можем передать произвольный текст. Этот текст конкатенируется с флагом, сжимается с помощью zlib , шифруется и мы в конце концов получаем в ответ шифротекст.

Код задачи:

Где уязвимость?

На первый взгляд шифрование выглядит достаточно надёжным: мы не знаем ни ключа, ни инциализирующего вектора (который вобщем-то и не должен быть секретным), а свойства AES позволяют генерировать криптографичесчки безопасную гамму для XORа. Всё здесь хорошо. Но стоит обратить внимание на то, что перед шифрованием данные сжимаются с помощью zlib. Эта библиотека построена вокруг алгоритма DEFLATE. Подробности алгоритма нас особо не интересуют. Более того, в принципе для существования уязвимости сам алгоритм не так важен. Важно лишь свойство алгоритмов сжатия — чем больше повторяющихся данных, тем лучше сжатие. Например:

Здесь мы сжимаем повторяющуюся букву a, и мы можем заметить, что для первых четырёх повторений цикла, то есть вплоть до aaaa размер сжатых данных увеличивается на 1, при добавлении 1 нового байта, причём размер сжатых данных больше размера сжимаемых данных — это из-за оверхеда алгоритма. Но уже последовательность aaaaa, сжимается в размер на 1 байт меньше, чем предыдущая. Более того, в дальнейшем размер данных не растёт вовсе.

Как найти флаг?

Теперь попробуем воспользоваться этим свойством сжатия для поиска ключа. Для начала рассмотрим простой пример без шифрования. Пусть флаг будет crypto{S0m3_flag}. Дальше мы можем присылать серверу какие-то данные, которые он добавит к флагу и сожмёт, а мы будем смотреть за длиной получившихся данных. Так как мы уже знаем начало флага (crypto{), мы начнём угадывать со следующей буквы.

Мы отправляем на сервер строку: crypto{n и получаем в ответ 28 байт сжатых данных. Перебирая варианты этого байта мы рано или поздно наткнёмся на S и отправим серверу значение crypto{S и получим в ответ 27 байт сжатых данных. Следует отметить, что мы также можем присылать серверу значения crypto{3, и crypto{0, но размер сжатых данных всё равно будет равен 28 — это связано с особенностью алгоритма, который ищет повторения в небольшом окне бит (не байт). Не будем углубляться в подробности алгоритма, просто отметим, что если мы заметили уменьшение размера данных, то мы нашли следующий неизвестный нам символ флага.

Дальше мы запоминаем этот байт и начинаем пребирать следующий:

И снова дойдя до символа 0 мы замечаем уменьшение размера сжатых данных.

Так перебирая символ за символом мы дойдём до значения crypto{S0m3. А дальше, какой бы символ мы не подставили, мы всегда будем получать один и тот же размер сжатых данных, даже когда мы ставим правильный символ:

Это опять связано с тем, что алгоритм оперирует сжатием на уровне бит, а не байт, и битовое сжатие может не отразится на размере данных в байтах. В данном случае нам придётся угадать следующий символ, здесь мы явно видим слово some, которое может быть как самостоятельным словом, так и частью составных слов someone, something, somewhere и т.п. Таким образом у нас есть несколько кандидатов на следующий символ: _(вместо пробела), o(или 0), w, t(или 7). Здесь нам нужно выбрать этот символ и продолжить перебор со следующего, если размер сжатых данных начнёт опять “плавать”, то мы выбрали правильный символ. Если нет — стоит попытаться с другим символом. Закрепим символ _.

Дальше перебор продолжается без проблем и мы в итоге получаем весь флаг. Мы знаем, что флаг заканчивается на символе }, значит дальше можно не перебирать.

Пишем эксплойт

Применяя ту же технику к нашему таску мы можем найти флаг. В таске добавляется шифрование, но оно не является помехой, так как оно не влияет на размер данных, а только он нас и интересует.

Скрипт выглядит следующим образом:

После запуска скрипт дойдёт до строки crypto{CRIM и остановится, не найдя следующий символ.

Это та самая ситуация, когда сжатие в битах не сказалось на размере текста в байтах. Придётся подобрать байт вручную. CRIM — очень похоже на слово CRIME. Тем более таск отностися к уязвимости CRIME, попробуем установить начальное значение флага в crypto{CRIME и запустить скрипт заново. И теперь скрипт находит полный флаг:

--

--

awawa.fun

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store