Если результат работы функции, использующей форматирующую строку, общедоступен, то злоумышленник может воспользоваться уязвимостью форматирующей строки для чтения памяти программы. Это является серьезной проблемой и может привести к раскрытию важной информации. Например, если программа получает от клиентов данные аутентификации и сразу же после получения не уничтожает их, то для ознакомления с ними можно воспользоваться уязвимостью форматирующей строки. Для злоумышленника наиболее простой способ прочесть содержимое памяти, воспользовавшись уязвимостью форматирующей строки, заключается в использовании переменных памяти. Переменные памяти - это переменные, которым присвоены адреса интересующих злоумышленника областей памяти и которые соответствуют заданным им спецификациям формата. Функция семейства printf, обрабатывая переданную ей форматирующую строку и обнаружив в ней очередную спецификацию преобразования, читает из стека значение следующей переменной. Например, для каждой спецификации вывода шестнадцатеричного целого числа без знака %х можно извлечь из памяти одно четырехбайтовое слово. Недостатком данного способа является то, что могут быть извлечены только данные из стека.

Но, используя спецификацию вывода строки %s, злоумышленник сможет прочитать данные из произвольной области памяти. Как уже говорилось ранее, спецификации формата %s соответствует строка, оканчивающаяся нулевым байтом. Эта строка передается функции по ссылке. Злоумышленник может прочитать содержимое любых участков памяти, добавляя в форматирующую строку спецификации вывода строки %s и указывая соответствующие им указатели на интересующие его участки памяти. Адрес начала интересующей злоумышленника области данных должен быть помещен в стек в том же формате, что и адрес памяти, соответствующей спецификации преобразования %п. При наличии в форматирующей строке спецификации вывода строки %s будет выведено содержимое области памяти, адрес начала которой указан злоумышленником, а адрес конца определяется по первому нулевому байту, встретившемуся при выводе.

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

Запись в память

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

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

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

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

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

В версиях утилиты Screen до 3.9.5 включительно содержится уязвимость форматирующей строки, которая проявляется при выводе строки визуализации звукового сигнала (visual bell). Эта строка определяется данными файла конфигурации пользователя с расширением . screenrc. Строка визуализации звукового сигнала выводится на терминал пользователя и интерпретирует символ звукового сигнала в кодировке ASCII. При выводе на терминал определяемые пользователем данные из конфигурационного файла передаются функции printfQ как часть форматирующей строки - первого параметра функции.

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

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

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

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

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

Принципы работы программ атаки, использующих ошибки форматирующих строк

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

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

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

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

Примечание

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

Например, если злоумышленник захочет использовать адрес из стека, хранящийся в 32 байтах от первого параметра функции printfQ, то он должен определить в своей форматирующей строке 8 спецификаций преобразования %х. Спецификация преобразования %х - это спецификация вывода шестнадцатеричного целого числа без знака длиной в слово. На 32-разряд ной платформе Intel длина слова равна 4 байтам. Каждый раз, встречая в форматирующей строке спецификацию преобразования %х, функция printfQ считывает из стека очередные четыре байта, в которых хранится переменная, соответствующая найденной спецификации. Кроме рассмотренной, для чтения данных из стека можно воспользоваться и другими спецификациями преобразования, а для записи данных в нужную область памяти существует спецификация преобразования %п.

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

Построение величин

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

// test.c
printf(“start: %10i end\n”,10);
}

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

[dma@victim server] $ ./test start: 10 end

Для десятичного представления числа 10 не требуется десяти символов, поэтому по умолчанию оставшаяся часть поля заполняется пробелами. Этим свойством функции printfQ может воспользоваться злоумышленник для увеличения значения величины, записываемой в указанную область памяти при помощи спецификации преобразования %п без фактического увеличения размера отформатированной строки. Хотя при использовании в спецификации преобразования ширины поля можно записать в память достаточно большое число, тем не менее злоумышленнику для записи могут потребоваться еще большие числа. Применив способ многократной записи с несколькими спецификациями преобразования %п, злоумышленник может использовать младшие значащие разряды получающихся по спецификации %п целых величин для раздельной записи каждого байта нужного ему числа. Этот прием позволяет получить нужные адреса памяти при относительно небольшом количестве спецификаций преобразования %п. Для реализации способа следует указать для каждой операции записи адрес, куда записывать, причем каждый последующий адрес смещается относительно первого на 1 байт.

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

На некоторых платформах, например платформах с архитектурой RISK (архитектура RISK (Reduced Instruction Set Computer) - архитектура с сокращенным набором команд. Тип архитектуры микропроцессора, ориентированный на быстрое и эффективное выполнение относительно небольшого набора встроенных команд), запрещено при записи использовать адрес, не выровненный на границу двух байт. Во многих случаях это ограничение удается снять, используя запись коротких целых чисел при помощи спецификации преобразования %hn.

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

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

Имея возможность использовать любые значения почти в любом месте памяти, перед злоумышленником встает вопрос: «Что перезаписывать?» Обладая возможностью использовать практически любой адрес памяти, у злоумышленника большой выбор. Он может изменить адрес точки возврата функции аналогично тому, как это делается при переполнении буфера. С помощью перезаписи адресов возврата может быть выполнен управляющий программный код злоумышленника. Но в отличие от переполнения буфера злоумышленник не ограничивает себя только подменой адресов возврата. Перезапись адресов возврата Большинство атак переполнения буфера сводятся к перезаписи адреса возврата какой-либо функции на адрес управляющего кода злоумышленника. Модифицированная подобным образом функция в конце своей работы передает управление не в точку возврата, а по подмененному злоумышленником адресу. При переполнении буфера перезаписывается адрес возврата из-за того, что фактически больше нечего перезаписывать. При атаках переполнения буфера злоумышленник не указывает точного адреса перезаписываемой области памяти. Запись ведется в области памяти, находящейся рядом с атакуемым буфером. В отличие от атак переполнения буфера при использовании уязвимости форматирующей строки данные злоумышленника записываются в указанную им область. Адрес области задается переменной, соответствующей спецификации преобразования %п. При завершении функции управление будет передано по адресу, записанному злоумышленником в результате многократной записи с использованием нескольких спецификаций преобразования %п.

При перезаписи адресов возврата злоумышленник может столкнуться с двумя проблемами. Одна из них состоит в том, что функция, у которой делается попытка подменить адрес возврата, в конце своей работы не возвращает управление вызвавшей ее функции. При использовании уязвимости форматирующей строки это обычное явление, потому что в большинстве случаев атакуемая функция выдает диагностическое сообщение и после выдачи диагностики инициирует аварийное завершение программы. Другими словами, выводится сообщение об ошибке, возможно, с использованием передаваемых программе данных, которые используются как параметры форматирующей строки, а затем вызывается функция завершения программы exit(). В этом случае перезапись адреса возврата для любой функции, кроме printfQ, ничего не даст. Второй проблемой является то, что перезапись адресов возврата может пресекаться специальными средствами, например StarckGard.

Перезапись указателей таблицы глобальных смещений и указателей на функции Таблица глобальных смещений (GOT - Global Offset Table) - это секция в скомпонованной в формате ELF (executable and linkable format - формат исполняемых и компонуемых модулей) программе, в которой хранятся указатели на используемые программой библиотечные функции. Если заменить содержащийся в таблице GOT указатель так, чтобы вместо библиотечной функции он указывал на программный код злоумышленника, то при обращении к библиотечной функции с подмененным указателем будет вызван код злоумышленника.

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

Пример уязвимой программы

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

В примере рассматривается программа, получившая название rwhoisd. Программа rwhoisd, или демон RWHOIS, является одной из реализаций сервиса RWHOIS. Научно-исследовательское отделение компании Network Solutions, Inc., в настоящее время поддерживает программу rwhoisd сервера RWHOIS, исходные тексты которой доступны по лицензии GNU PublicLicense.

Приоткрывая завесу

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


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



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

  • Июнь
    2019
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс