Эксплоит под wu-ftpd

kas1e

Xakep, номер #052, стр. 052-054-1

Или format string уязвимости

Сегодня мы продолжим знакомство с различными уязвимостями и рассмотрим ошибку типа format string. Format string появился на свет в июне 1999 года, а уже в июне 2000 количество эксплоитов, основанных на этой уязвимости, достигло критической отметки. Они проявлялись как в маленьких утилитах, так и в огромных серверных приложениях. В течение года производители софта пытались закрывать на это глаза, но в конце концов опомнились. Как ты догадываешься, большинство ошибок в программном обеспечении объясняется кривым программированием или ленью. Format string не исключение, как станет понятно при более близком знакомстве. Итак, поехали.

Работа у программиста довольно напряженная, необходимо следить за каждым байтом, оценивать ситуацию, представлять различные возможности поведения программы и т.д. К примеру, часто приходится использовать в программе 'C' строки, оканчивающиеся нулевым байтом, и не всегда есть возможность уследить за таким кодом. Отсюда возникают разные проблемы. Так и в случае с format string. Скажем, программист решает сэкономить на размере и не указывает дополнительных 'форматных' аргументов:

printf(string); // выводим строку (без указания каким способом выводить)

вместо:

printf(%s, string); // выводим строку (с указанием способа)

Видимо не все программисты знают, что если форматный аргумент не указан, то их поиск будет производиться в любом случае. И если какой-то из них будет найден, то в стеке произойдут соответствующие преобразования (из предыдущих статей по buffer overflow можно представить, чего добиваются люди, имея доступ к стеку). Однако нас больше интересует конкретный символ форматирования. Это %n. Как ни странно, при описании символов форматирования он почти никогда не упоминается. А вот что говорится о нем в манах:

"Число символов, выведенных до этого момента, сохраняется по адресу целого числа, указанному аргументом-указателем типа int * (или variant). Преобразование аргументов не происходит".

Что же это значит? А то, что данный аргумент позволяет производить запись в переменную-указатель, даже если она используется в функции для вывода на экран! Т.е. можно записывать данные по адресу, на который указывает второй аргумент. Кроме этого, он подсчитывает еще каждый символ, появляющийся в самой строке форматирования. Возьмем простейший пример:

#include <stdio.h>

void main()

{

int a; // целая переменная 'a'

char *buff = "1111111111"; // символьный буфер 'buff' с десятью единицами

printf("%s%nn", buff, &a); // выводим строку "1111111111",

// которая содержит 10 символов

printf("a = %dn", a); // выводим записанное значение (число символов)

// записанное в целую переменную 'a'

}

Откомпилим и запустим:

# gcc printf_test.c -o printf_test

# ./printf_test

1111111111

a = 10

Да, %n действительно подсчитывает символы. Но есть еще один нюанс - подсчет не всегда бывает таким точным :). Сразу скажу, что %n считает количество символов, которые будут выведены предположительно. Т.е. если ты, скажем, через snprintf ограничишь буфер 100 символами, а потом повторишь строки из предыдущего примера, то переменная 'a' будет показывать вместо 10 - 100. Вот более детальное объяснение: сначала %n подсчитывает количество символов, потом записывает по адресу, указанному во втором аргументе, и только потом строка уменьшается при копировании в буфер. Т.е. сначала строка расширяется, потом считывается, а затем уменьшается ;).

Содержание  Вперед на стр. 052-054-2
ttfb: 3.3259391784668 ms