Зоопарк переполняющихся буферов 

Крис Касперски aka мыщъх

Спецвыпуск Xakep, номер #045, стр. 045-008-1

Классификация уязвимостей типа buffer overflow

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

Это не воспаленная фантазия обкуренного фантаста. Это постепенно становится реальностью. Дизассемблирование в ряде стран уже запрещено. Публичное описание технических деталей хакерских атак и уязвимостей на пороге запрета.

Однако мощные мегакорпорации еще чрезвычайно уязвимы. Их программное обеспечение дыряво до невозможности, и чем больше заплат накладывается на продукт, тем уродливее и неустойчивее он становится. Что мешает нам этим воспользоваться? Ударим же хакерским автопробегом по виртуальному бездорожью!

Типы переполнений

Существуют различные типы переполнений. Самое известное из них – последовательное переполнение при записи, которое возникает обычно при небрежном обращении с функциями копирования памяти (memcpy, memmove, strcpy, strcat, sprintf и другими, статистика "переполняемости" которых изображена на диаграмме), "проламывающими" дно буфера и перезаписывающими одну или несколько ячеек памяти за его концом. Менее популярно индексное переполнение, тесно связанное с сишными "недомассивами" и проблемой контроля их границ. Рассмотрим следующий код: f(int i) {char buf[BUF_SIZE]; ... return buf[i]}. Очевидно, что если i <= BUF_SIZE, функция f возвращает содержимое ячеек, совсем не принадлежащих массиву buf.

Таким образом, основных типов переполнения четыре: последовательное переполнение при чтении, последовательное переполнение при записи, индексное переполнение при чтении, индексное переполнение при записи. Наивысшую опасность представляют перезаписывающие переполнения, при благоприятном стечении обстоятельств передающие атакующему контрольный пакет акций удаленного управления уязвимой машиной. Многие считают, что переполнения при чтении менее опасны и приводят, в основном, лишь к утечке секретной информации (например, паролей). Однако, это неверно: даже вполне безобидные на вид переполнения способны порождать каскад вторичных переполнений, пускающий систему в разнос и зачастую успевающий перед смертью сделать что-то полезное для хакера, особенно если этот разнос осуществляется по заранее продуманному плану.

В зависимости от типа перезаписываемых переменных различают, по меньшей мере, три вида атак: атаки на скалярные переменные, атаки на индексы (указатели) и атаки на буфера. Скалярные переменные часто хранят флаги авторизации пользователей, уровни привилегий, счетчики циклов и прочее. Вот один из примеров:

Код #1. Пример, демонстрирующий атаку на счетчик цикла

f(char *dst, char *src)

{

char buf[xxx]; int a; int b;

...

b = strlen(src);

...

for (a = 0; a > b; a++) *dst++ = *src++;

}

Если переполнение буфера buf произойдет после вызова strlen, переменная b будет жестоко затерта и наш цикл вылетит далеко на пределы src и dst!

Другой пример этого же типа:

Код #2. Пример, демонстрирующий атаку на переменную-флаг

Содержание  Вперед на стр. 045-008-2
ttfb: 21.87180519104 ms