ошибка форматной строки - уязвимость форматной строки - повреждение памяти

Как простая ошибка форматирования строки может привести к повреждению памяти (и как обнаружить ее в коде)

Что такое ошибка форматной строки и почему она всё ещё важна

A Ошибка строки формата возникает, когда контролируемые пользователем данные передаются как строка формата в таких функциях, как printf, fprintf или syslog, без проверки.

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

Почему это важно в современных DevSecOps? Потому что эти ошибки все еще встречаются в активных кодовых базах, особенно в:

  • Компоненты с открытым исходным кодом и устаревшим кодом C
  • Нативные привязки в Python, Go или Rust
  • Автоматически объединенный сторонний код в производстве pipelines

Влияние реально: утечки памяти, повреждение стека и даже удаленное выполнение кода (RCE). И все же многие команды доверяют статическим сканерам и пропускают эти ошибки, если только они не проверяют их специально.

Сразу к Кодексу: printf(пользовательский_ввод) и опасность

Давайте рассмотрим распространенную ошибку:

printf(пользовательский_ввод);

Эта однострочная фраза — прямой путь к беде. Если пользовательский_вход содержит что-то вроде %x %x % x% %x, он инструктирует Printf для чтения значений из стека, открывая доступ к памяти. Хуже, если %n включен, злоумышленник может записать произвольные значения в память.

Этот шаблон не просто приводит к утечке данных стека; он также может повредить память и перерасти в удаленное выполнение кода (RCE). Именно так выглядит уязвимость форматной строки на практике. Эти уязвимости не вызывают ошибок компилятора или предупреждений, если не включены определённые флаги или санитайзеры. И во многих случаях они скрыты в оболочках или служебных функциях, что делает их невидимыми для случайного анализа кода.

Повреждение памяти 101: что на самом деле поставлено на карту

Когда fЕсли эксплуатируется ошибка форматирования строки, злоумышленники могут:

  • Дамп адресов памяти и значений стека с помощью %x or %s
  • Перезапишите переменные стека или адреса возврата с помощью %n
  • Вызвать ошибки сегментации или логические ошибки из-за повреждения памяти
  • Переход к полному RCE, особенно если средства защиты, такие как ASLR или стековые «канареечные» функции, настроены неправильно.

Здесь поможет понимание структуры стека. Printf не знает, сколько аргументов ожидать; он полностью полагается на строку формата. Вот почему %x Проходит по стеку, раскрывая или изменяя данные. Результат варьируется от незначительной утечки до полного контроля над указателем инструкций.

Где скрываются ошибки форматирования строк в современных кодовых базах

Эти уязвимости присущи не только устаревшему коду на языке C. Они таятся и в современных средах:

  • Python (ctypes), Rust (FFI) и Go (cgo) Привязки, взаимодействующие с собственными библиотеками, часто действуют как тонкие обертки, передавая параметры напрямую уязвимым функциям C.
  • Сторонние инструменты CLI и демоны интегрируют старый код C без достаточного анализа
  • Оболочки для регистрации, такие как debug_log(пользовательский_ввод) которые внутренне направляют к функциям в стиле printf
  • Автоматически объединенные вклады OSS, содержащие устаревшие шаблоны или минимальную проверку

Ошибка в исходном слое C не остаётся там, она распространяется вверх. Если функция C, например, log_event(char *msg) небезопасно, вызов его из Python через cтипы, Rust via небезопасный внешний, или Перейти через что переносит уязвимость в среды более высокого уровня.

В чём проблема? Эти интеграции широко распространены, и DevSecOps часто предполагает, что привязки — это безопасные абстракции. Это не так. Уязвимость форматной строки на одном уровне стека может незаметно распространяться на другие интерфейсы, особенно если нативные модули обёрнуты без строгого контроля типов или очистки входных данных. Если базовая функция C уязвима, код более высокого уровня наследует уязвимость форматной строки.

CI/CD Pipelines: Как это обходит ваши проверки безопасности

Современные pipelineсозданы для скорости, но эта скорость создает слепые зоны:

  • SAST инструменты редко отслеживает использование строки динамического формата, если только специально не настроено отслеживание испорченных данных
  • Рецензенты PR сосредотачиваются на логике или стиле, а не на базовом поведении функции C
  • Слияния CI затягивают уязвимые пакеты, которые на первый взгляд кажутся безобидными
  • Сканеры зависимостей часто игнорируют собственный код или небезопасную логику регистрации

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

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

  • Блокировка кода, в котором ненадежные входные данные достигают функций форматирования
  • Отметка строк динамического формата во время статического анализа
  • Применение этих политик в рамках процесса слияния CI

Без этого ваш pipeline не видит класс уязвимостей, которые могут привести к повреждению памяти и RCE задолго до того, как код попадет в эксплуатацию.

Обнаружение ошибки: практическое обнаружение с помощью GDB и статических инструментов

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

Ручной анализ с помощью GDB:

GDB особенно полезен, когда вы подозреваете неправильное использование строки формата, но хотите проверить, как она ведет себя во время выполнения:

  • Перерыв на Printf или связанные функции для проверки стека вызовов и аргументов.
  • Ищите аномалии, неожиданные чтения памяти, сбои во время форматирования или странные значения в стеке.
  • Абстрактный ввод, как повторяющийся %x or %s может помочь определить, насколько глубоко строка формата проходит по стеку.

От ручного к автоматизированному:

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

  • Используйте grep 'printf(' src/ для поиска необработанных вызовов форматирования.
  • Объедините это со скриптами, чтобы отмечать любое использование Printf( где первый аргумент - не буквальная строка.
  • Используйте инструменты на основе AST для отслеживания значений строк формата, динамически определяя нелитеральные пути.
  • Переводите часто выявляемые вручную ошибки, например, пересылку недоверенных входных данных функциями-оболочками, в правила непрерывной интеграции, которые автоматически блокируют такие случаи.

Совет по интеграции CI: Настройка pipeline для прерывания сборки, если какая-либо функция форматирования получает динамические входные данные в качестве строки форматирования. Эти проверки действуют как брандмауэр, обеспечивающий соблюдение знаний, полученных вами при работе с GDB и отладке во время выполнения.

Укрепление кода: проверка входных данных и более безопасные шаблоны

Предотвращение уязвимости строки формата начинается с внедрения более безопасных привычек кодирования и их повсеместного применения:

  • Всегда используйте строки фиксированного формата: printf("%s", пользовательский_ввод);, никогда не передавайте необработанные входные данные в качестве формата.
  • Предпочитайте более безопасные варианты: snprintf, vsnprintf, и аналогичные функции помогают контролировать размеры буфера и обеспечивать структуру вывода.
  • Проверяйте все введенные пользователем данные которые могут войти в логику журналирования или форматирования, даже в функциях-оболочках.

Автоматизированные меры смягчения, которые следует включить:

  • АдресSanitizer (ASan): Обнаруживает повреждения памяти в режиме реального времени, включая переполнение буфера и нарушения стека, часто вызванные неверно сформированными строками формата.
  • UndefineBehaviorSanitizer (UBSan): Сообщает о неопределенном поведении, таком как передача несовпадающих или отсутствующих аргументов функциям форматирования.
  • -D_FORTIFY_SOURCE=2: Добавляет облегченные проверки к функциям libc во время компиляции, помогая выявлять неправильное использование форматной строки или переполнение буфера с минимальными затратами на производительность.

Эти инструменты должны быть включены как в средах разработки, так и в средах непрерывной интеграции (CI), чтобы выявлять проблемы до их выпуска. В сочетании со статическим анализом они образуют надёжную систему безопасности, предупреждая о ненадлежащем использовании, которое в противном случае могло бы остаться незамеченным до момента запуска или после эксплуатации.

Наконечник: Сделайте эти дезинфицирующие средства частью вашего проекта pipeline С политиками сбоя при предупреждении. Любое нарушение форматной строки следует рассматривать как провал теста.

Как Xygeni устраняет ошибки форматирования строк перед отправкой

Ксигени укрепляет CI/CD путем обеспечения безопасности строки формата с помощью предотвращения в реальном времени, а не только обнаружения:

  • Выявляет опасные закономерности , такие как printf(пользовательский_ввод) до того, как код попадет в производство
  • Применяет статический анализ заражений для отслеживания ненадежных входных данных в функциях форматирования, даже между несколькими слоями или вызовами оболочек
  • Автоматически блокирует небезопасные слияния в GitHub, GitLab, Bitbucket и Jenkins
  • Обеспечивает четкую обратную связь с трассировками вызовов, источником ввода и предварительнымcisпредложения по исправлению

Пример в действии: iразработчик fa commitэто линия log_debug(пользовательский_ввод) и log_debug() внутренне обволакивает уязвимое PrintfXygeni отслеживает граф вызовов, распознаёт динамический путь ввода и блокирует слияние. Разработчик сразу видит сообщение в своём запросе на слияние:

⚠️ Обнаружена уязвимость форматной строки: пользовательский_вход впадает в printf() в src/logger.c:42. Используйте фиксированную строку формата и проверяйте входные данные.

Эта обратная связь доставляется непосредственно в GitHub, GitLab, Jenkins или Bitbucket в рамках процесса MR/PR. Разработчики не могут её пропустить и получают практические рекомендации по устранению проблемы, а не просто расплывчатое предупреждение.

Интеграция проходит гладко:

  • Настройте правила политики для каждого репозитория, ветви или проекта
  • Применять условия блокировки для использования небезопасного формата
  • Автоматически отслеживать небезопасные входные данные в C/C++, Python, Go, Rust и их собственных привязках

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

Заключение: ошибки форматирования строк не исчезли

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

Проверьте свой код. Укрепи свой CI/CD. Внедрите правила обнаружения и обеспечивайте их соблюдение. Это не просто унаследованный багаж, это активная угроза, скрывающаяся на виду. Используйте автоматизированные инструменты, статический анализ и средства очистки среды выполнения для раннего выявления проблем. Не думайте, что вы в безопасности только потому, что код компилируется.

sca-инструменты-программное обеспечение-композиция-анализ-инструменты
Расставьте приоритеты, устраните и защитите риски, связанные с программным обеспечением
Получите бесплатный аккаунт.
Нет необходимости кредитную карту.

Защитите свою разработку и доставку программного обеспечения

с пакетом продуктов Xygeni