Для понимания сути уязвимости форматирующей строки необходимо иметь четкие представления о работе функцииргШ/().

Часто программистам требуется сформировать строку из нескольких переменных разного типа во время работы программы. Зачастую при разработке программы точное количество переменных, необходимых для формирования строки, и порядок их следования неизвестны. В основном для этих целей используется семейство функций ргШ/, предоставляющее гибкие возможности создания и форматирования строк во время выполнения программы. Функции семейства ргШ/ входят в стандартную библиотеку языка С. Возможности функций семействаргШ/реализованы и в других языках, например Рег1.

Параметрами этих функций являются форматирующая строка и переменное число параметров, которые позволяют сформировать строку. Форматирующая строка может рассматриваться как некий шаблон, описывающий структуру будущей строки и содержащий спецификации преобразования, которые сообщают функции семейства ргШ/, где и какие данные должны располагаться и как они должны быть отформатированы. Часто спецификации преобразования называются спецификациями формата. В главе будут использоваться два этих понятия независимо друг от друга.

Инструментарий и ловушки Функции семейства printf

Ниже приведен список функций семейства printf, входящих в стандартную библиотеку языка С. При неправильном использовании каждая из них может привести к ошибкам форматирующей строки.

• Функция printfQ позволяет сформировать и записать отформатированную строку в стандартный поток вывода.

• Функция fprintfO позволяет сформировать и записать отформатированную строку в определяемый библиотекой libc файловый поток вывода, имя которого задается программистом.

• Функция sprintfQ позволяет сформировать и записать отформатированную строку в область памяти. Неправильное использование этой функции часто приводит к переполнению буфера.

• Функция snprintfQ позволяет сформировать и записать отформатированную строку заданной длины в область памяти. Является безопасной заменой функции sprintfQ при защите от переполнения буфера.

В стандартную библиотеку языка С также включены функции vprintfQ, vfprintfQ, vsprintf() и vsnprintfQ. Они выполняют те же функции, что и ранее перечисленные, но их входным параметром может быть структура varargs, описывающая переменное число аргументов.

Работа функции printf демонстрируется следующим примером: int main() { int integer =10;

printf(“this is the skeleton of the string, %i”,integer);
}

В этом примере вызывается функция printfQ с двумя параметрами: форматирующей строкой и переменной, которая включается в формируемую строку вывода во время выполнения программы. Первый параметр функции - форматирующая строка, которая состоит из строки символов (статического текста) и спецификации вывода целого числа со знаком %i, соответствующей переменной integer. При вызове функции printfQ значение переменной целого типа, преобразованное в символьный вид десятичного числа, будет вставлено в строку после запятой.

«this is the skeleton of the string, %i»

Ниже приведена строка, которая выводится во время выполнения программы (значение переменной равно 10).

[dma@victim server]$ ./format_example this is the skeleton of the string, 10

Функция printfQ просматривает форматирующую строку и выводит каждый символ как он есть, буквально, пока не встретит спецификацию преобразования. Поскольку функция ргт$() заранее не знает, сколько параметров будет ей передано, то каждый параметр считывается из стека по мере обработки форматирующей строки в соответствии с типом каждой спецификации преобразования. В рассматриваемом примере в форматирующей строке единственная спецификация преобразования - спецификация вывода целого числа со знаком %/', обеспечивает вставку в формируемую строку переменной целого типа. Функция ожидает, что переменная, соответствующая спецификации преобразования, будет передана функции ргШ/() вторым параметром. В архитектуре 1Ще1 (по крайней мере) параметры функций помещаются в стек до того, как будет создан стековый фрейм. Поэтому когда функция ргШ/() считывает свои параметры из стека, они ссылаются на данные в стеке, находящиеся ниже стекового фрейма.

Примечание

В этой главе термин «ниже» («под») применяется к данным, которые были помещены в стек раньше каких-то других. Для описания данных, помещенных позже, применяется термин «выше» («над»). В архитектуре Intel стек растет вниз. В архитектурах с растущим вниз стеком адрес вершины уменьшается по мере роста стека, поэтому данные, расположенные «ниже», хранятся в областях памяти с большими адресами, чем данные, расположенные «выше».

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

В примере второй аргумент функции printfQ - целое число, которое включается в формируемую строку в соответствии со спецификацией вывода целого числа со знаком %i. В результате в формируемой строке на месте спецификации формата будет помещено значение переменной integer в формате целого десятичного числа, которое равно 10.

В соответствии со спецификацией вывода целого числа со знаком функция printfQ для формирования выводимой строки использует содержимое области памяти, размер которой совпадает с размером переменной целого типа и которая расположена в нужном месте стека. Сначала в соответствии со спецификацией формата двоичное представление содержимого выбранной области стека преобразуется в символьное представление, а затем включается в формируемую строку. Как будет показано позже, это происходит независимо от того, передан на самом деле второй параметр функции printfQ или нет. Если ни одного параметра, соответствующего спецификациям формата форматирующей строки, не было передано функции printfQ, то стековые данные вызывающей функции будут трактоваться как параметры, поскольку они занимают в стеке место предполагаемых параметров вызванной функции printfQ.

Вернемся к примеру. Допустим, что впоследствии было решено выводить только статическую строку, но при этом забыли указать переменную, соответствующую спецификации формата. В конечном счете функция printfQ вызывается следующим образом:

printf(“this is the skeleton of the string, %i”); /* note: no argument, only a format string. */

Во время своего выполнения функция не знает, что ей забыли указать переменную, соответствующую спецификации вывода целого числа со знаком %i. Поэтому при формировании строки функция printfQ прочтет целое число из области стека, в которую должен быть помещен второй параметр и которая занимает 4 байта под ее стековым фреймом. Если виртуальная память размещения второго параметра доступна, то программа продолжит свою работу и любые байты, оказавшиеся в области размещения второго аргумента, при работе функции будут проинтерпретированы и выведены как целое число. В результате будет напечатано следующее:

[dma@victim server]$ ,/format_example this is the skeleton of the string, -1073742952 Отметим, что хотя при вызове функции printfQ не был задан параметр функции, соответствующий спецификации вывода целого числа со знаком %i , тем не менее в формируемую строку было включено целое число. Функция прочитала из области стека, в которую был бы помещен незаданный параметр функции, какие-то данные и представила их в формате целого числа со знаком. В данном случае после представления найденных байтов в формате целого десятичного числа со знаком получилось число -1073742952. Таким образом, если дополнить форматирующую строку своими спецификациями формата, то можно воспользоваться функцией printfQ для просмотра содержимого стека, потому что функция printfQ выведет содержимое областей стека в соответствии с заданными спецификациями формата.

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

Как и почему возникают ошибки форматирующей строки?

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

Первая ошибка состоит в том, что при вызове функции printfQ ей передается единственная непроверенная строка. Например: printf(argv[l]);

При этом единственная передаваемая переменная (часто передается первый аргумент командной строки) расценивается функцией как форматирующая строка. И если в эту переменную будут включены спецификации преобразования, то функция среагирует на них. [dma@victim]$ ,/format_example %i -1073742936

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

Рассмотренный ниже пример программы использования ошибки форматирующей строки основан на подобной ошибке. Наиболее общей причиной возникновения уязвимости форматирующей строки в системе Unix является использование функции syslogQ. Функция syslogQ служит программным интерфейсом для демона системного журнала. (Демон -скрытая от пользователя служебная программа, вызываемая при выполнении какой-либо функции.) Программист использует функцию syslogQ для записи сообщений об ошибке в файлы системного журнала с указанием их приоритетов. Параметрами функции syslogQ, кроме первого, может быть как единственная строка символов, так и форматирующая строка с произвольным числом спецификаций преобразования и соответствующие им параметры. Первым параметром функции является приоритет сообщения. Многие программисты при использовании функции syslogQ забывают или не знают о том, что в форматирующую строку не должны помещаться входные данные программы. Большинство уязвимостей связано с кодированием подобно следующему фрагменту кода

error warn(errmsg);

Если в строке errmsg содержатся входные данные программы, например имя пользователя, неудачно попытавшегося войти в систему, то они могут быть использованы для типичной атаки с использованием ошибок форматирующей строки. Как устранить уязвимость форматирующей строки?

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

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

Майк Франтцен (Mike Frantzen) опубликовал методику вылизывания программ с целью максимального устранения недоделок, которая позволяет администраторам и программистам предотвращать любые атаки с использованием ошибок форматирующей строки. Его методика основана на сравнении числа параметров, передаваемых функции printfQ, с числом спецификаций формата в форматирующей строке. Эта методика под названием Format Guard реализована в Immunix, одном из безопасных дистрибутивов ОС Linux.

Заархивированную методику Майка Франтцена в списке рассылки Bugtraq можно найти по адресу www. security focus, com/archive/1/72118. Format Guard находится по адресу www.immunix.org/formatguard.html.

Способы использования ошибок форматирующей строки для атаки

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

Ошибки и защита

Ошибки форматирующей строки | Защита от хакеров корпоративных сетей | Переполнение буфера с помощью ошибок форматирующей строки


Защита от хакеров корпоративных сетей



Новости за месяц

  • Август
    2019
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс