Неуправляемая строка формата
Неуправляемая строка формата — разновидность уязвимости программного обеспечения класса инъекция кода, обнаруженная примерно в 1989 году и активно используемая в эксплуатации уязвимостей. Изначально считавшиеся безвредными, атаки с использованием строк формата могут привести к сбою программы или выполнению вредоносного кода. Проблема заключается в использовании непроверенного пользовательского ввода в качестве параметра строки формата при вызове определённых функций в языке C, которые выполняют форматирование, например printf(). Злоумышленник может задействовать форматные спецификаторы, такие как %s и %x, чтобы вывести данные из стека вызова или других областей памяти. Кроме того, с помощью спецификатора %n можно произвести запись произвольных данных по произвольным адресам: эта конструкция указывает функции printf() и аналогичным записать число обработанных байтов по адресу, который хранится в стеке[1].
Подробности
Типичная эксплуатация неуправляемой строки формата использует сочетание перечисленных техник для захвата управления указателем команд процесса, например, принудительно изменяя адрес библиотечной функции или возвратный адрес в стеке на указатель, ссылающийся на вредоносный шелл-код. Параметры выравнивания в спецификаторах формата позволяют управлять количеством выводимых байтов, спецификатор %x используется для последовательного «извлечения» значений из стека до начала самой строки формата, которая искусственно сконструирована так, чтобы содержать нужный адрес; затем спецификатор %n перезаписывает этот адрес ссылкой на код, который будет выполнен.
Данная уязвимость встречается часто, поскольку ошибки в использовании строк формата долгое время считались безобидными, что привело к аналогичным проблемам в многочисленных инструментах. По данным проекта CVE организации MITRE, по состоянию на июнь 2007 года насчитывалось примерно 500 уязвимых программ, а по анализу тенденций она занимала 9-е место по количеству сообщений об уязвимостях в период с 2001 по 2006 год.
Ошибки при использовании строк формата чаще всего возникают, когда разработчик хочет вывести строку с пользовательскими данными (например, в файл, буфер или напрямую пользователю) и ошибочно пишет printf(buffer) вместо printf("%s", buffer). В первом случае buffer воспринимается как строка формата, и любые содержащиеся в ней спецификаторы будут интерпретированы функцией; второй вариант просто выводит строку, как ожидал программист. Оба варианта ведут себя идентично, если в строке нет спецификаторов формата, из-за чего ошибка часто остаётся незамеченной.
Форматные ошибки возникают также из-за особенностей передачи аргументов в языке C, которая не обеспечивает контроль типов. Механизм vararg позволяет функциям принимать произвольное количество аргументов (как в printf), извлекая («вынимая») их из стека согласно тому, как указано в первых параметрах, — функция доверяет первым аргументам, что те правильно описывают число и тип остальных получаемых значений.
Ошибки строк формата встречаются и в других языках программирования, например в Perl, хотя там они, как правило, встречаются реже и обычно не позволяют выполнить произвольный код.
История
Первые сообщения об ошибках строк формата были опубликованы в 1989 году в ходе исследования методом fuzz-тестирование в Висконсинском университете; был обнаружен «эффект взаимодействия» между механизмом истории команд в C shell (csh) и обработчиком ошибок, предполагавшим безопасность строк.
Впервые использование ошибок строк формата как вектор атаки было зафиксировано в сентябре 1999 года Тиммом Твиллманом в ходе аудита безопасности демона ProFTPD. В ходе проверки была обнаружена функция snprintf, напрямую принимавшая пользовательские данные без указания строки формата. Экспериментальные атаки с контрафактными аргументами для функций семейства printf показали возможность использования уязвимости для повышения привилегий. В сентябре 1999 года была опубликована первая заметка на рассылке Bugtraq, содержащая базовый эксплойт. Однако понадобилось ещё несколько месяцев, прежде чем сообщество специалистов по ИБ осознало опасность подобных уязвимостей, поскольку эксплойты для иных программ стали появляться позже. Первые массово заметные эксплойты (дававшие удалённый доступ с правами root через выполнение кода) были опубликованы одновременно в рассылке Bugtraq в июне 2000 года Пшемыславом Фрашунеком и автором под псевдонимом tf8. Вскоре после этого автор с ником lamagra опубликовал подробное объяснение механизма. В июле 2000 года Паскаль Бушарейн (Pascal Bouchareine) также разместил сообщение о «format bugs» в Bugtraq. Классической стала статья «Format String Attacks» Тима Ньюшэма, опубликованная в сентябре 2000 года, а впоследствии появились и иные технические исследования, например, «Exploiting Format String Vulnerabilities» от группы TESO в сентябре 2001 года.
В современных языках программирования, таких как Java (c String.format()), C# (c String.Format() или $"") и C++ (c std::format()), подобные атаки на строки формата невозможны.
Предотвращение средствами компилятора
Многие компиляторы способны осуществлять статический анализ строк формата и выдавать предупреждения при подозрительных или опасных шаблонах. В составе GNU Compiler Collection соответствующие флаги компилятора: -Wall, -Wformat, -Wno-format-extra-args, -Wformat-security, -Wformat-nonliteral и -Wformat=2.
Однако большинство из них эффективны только при анализе строк формата, известных на этапе компиляции. Если же строка формата может поступать от пользователя либо из внешнего источника, приложение должно самостоятельно валидировать её перед использованием. Следует также быть внимательным, если формат строки формируется или выбирается динамически. Если применяется библиотека GNU C, параметр -D_FORTIFY_SOURCE=2 позволяет выявлять некоторые виды атак во время выполнения. Проверка -Wformat-nonliteral считается более строгой.
Выявление
В отличие от многих других уязвимостей, основную причину появления ошибок строк формата относительно просто выявить в x86-компилированных программах: для функций семейства printf корректное использование подразумевает передачу отдельного аргумента для строки формата и отдельных аргументов для значения; наличие несоответствия между количеством параметров (так называемый «argument deficiency» — дефицит аргументов) — явный признак некорректного вызова функции.
Выявление в x86-компилированных бинарных файлах
Подсчёт аргументов часто упрощается в x86 благодаря соглашению о вызовах, по которому вызывающая функция самостоятельно снимает аргументы со стека после вызова, прибавляя к указателю стека число переданных байтов; таким образом, можно напрямую определить количество параметров, переданных в функцию семейства printf.
Примечания
- ↑ Introduction to format string exploits (англ.). codearcana.com. Дата обращения: 7 ноября 2025.
Литература
- Кауэн, Криспин (август 2001). FormatGuard: Automatic Protection From printf Format String Vulnerabilities (PDF). Proceedings of the 10th USENIX Security Symposium [англ.]. Проверьте дату в
|date=(справка на английском) - Software Security for Open-Source Systems (англ.), IEEE Security & Privacy, IEEE Computer Society (январь–февраль 2003).
- Buffer Overflows und Format-String-Schwachstellen - Funktionsweisen, Exploits und Gegenmaßnahmen : [нем.]. — 1. — dpunkt.verlag, 2004. — ISBN 3-89864-192-9.
- Secure Coding in C and C++ : [англ.]. — Addison Wesley, сентябрь 2005. — ISBN 0-321-33572-4.
Ссылки
- Введение в эксплойты на строках формата (2013), Алекс Рис
- Exploiting Format String Vulnerabilities, v1.2 (2001), scut / команда TESO
- Классификация угроз WASC — Атаки на строки формата
- Стандарты безопасного программирования CERT
- Программа Secure Coding Initiative (CERT)
- Известные уязвимости в CVE-проекте MITRE
- Безопасное программирование с GCC и GLibc (2008), Марсель Хольтманн