Подтвердить что ты не робот

Понимая опасности sprintf (...)

OWASP говорит:

"Библиотечные функции C, такие как strcpy (), strcat(), sprintf() и vsprintf () работают с нулевыми завершенными строками и не выполняйте проверку границ."

sprintf записывает форматированные данные в строку   int sprintf (char * str, const char * format,...);

Пример:

sprintf(str, "%s", message); // assume declaration and 
                             // initialization of variables

Если я понимаю комментарий OWASP, то опасность использования sprintf заключается в том, что

1) если длина сообщение длинa > str, там переполнение буфера

и

2) если сообщение не завершает нуль с \0, то сообщение может быть скопировано в str за адресом памяти сообщение, вызывая переполнение буфера

Пожалуйста, подтвердите/отклоните. Благодаря

4b9b3361

Ответ 1

Вы правы в обеих проблемах, хотя на самом деле это одна и та же проблема (доступ к данным за пределами массива).

Решение вашей первой проблемы - вместо этого использовать std::snprintf, который принимает размер буфера в качестве аргумента.

Решением вашей второй проблемы является предоставление аргумента максимальной длины snprintf. Например:

char buffer[128];

std::snprintf(buffer, sizeof(buffer), "This is a %.4s\n", "testGARBAGE DATA");

// std::strcmp(buffer, "This is a test\n") == 0

Если вы хотите сохранить всю строку (например, в случае, если sizeof(buffer) слишком мал), дважды запустите snprintf:

int length = std::snprintf(nullptr, 0, "This is a %.4s\n", "testGARBAGE DATA");

++length;           // +1 for null terminator
char *buffer = new char[length];

std::snprintf(buffer, length, "This is a %.4s\n", "testGARBAGE DATA");

(Вы, вероятно, можете встроить это в функцию, используя va или шаблоны с переменными числами.)

Ответ 2

Оба ваших утверждения верны.

Там дополнительная проблема не упоминается. Параметры параметров не проверяются. Если вы не соответствуете строкам формата и параметрам, undefined и может возникнуть нежелательное поведение. Например:

char buf[1024] = {0};
float f = 42.0f;
sprintf(buf, "%s", f);  // `f` isn't a string.  the sun may explode here

Это может быть особенно неприятно для отладки.

Все вышеперечисленное приводит многих разработчиков С++ к выводу, что вы никогда не должны использовать sprintf и его собратьев. Действительно, есть возможности, которые вы можете использовать, чтобы избежать всех вышеперечисленных проблем. Один, потоки, встроен прямо на язык:

#include <sstream>
#include <string>

// ...

float f = 42.0f;

stringstream ss;
ss << f;
string s = ss.str();

... и еще один популярный выбор для тех, кто, как и я, по-прежнему предпочитает использовать sprintf, приходит из форматировать библиотеки формата:

#include <string>
#include <boost\format.hpp>

// ...

float f = 42.0f;
string s = (boost::format("%1%") %f).str();

Если вы примете мантру "никогда не используйте sprintf"? Решите сами. Как правило, это лучший инструмент для работы, и в зависимости от того, что вы делаете, sprintf может быть просто.

Ответ 3

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

С точки зрения OWASP, предположим, что мы пишем веб-сервер, и мы используем sprintf для синтаксического анализа ввода, который проходит через браузер.

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

Ответ 4

Ваши 2-х выводные выводы верны, но неполны.

Существует дополнительный риск:

char* format = 0;
char buf[128];
sprintf(buf, format, "hello");

Здесь format не завершается NULL. sprintf() не проверяет, что.

Ответ 5

Ваша интерпретация кажется правильной. Однако ваш случай №2 на самом деле не является переполнением буфера. Это скорее нарушение доступа к памяти. Это просто терминология, хотя это все еще большая проблема.

Ответ 6

Функция sprintf при использовании с определенными спецификаторами формата создает два типа риска безопасности: (1) запись памяти не должна; (2) чтение памяти не должно. Если snprintf используется с параметром размера, который соответствует буферу, он не будет писать ничего, что не должно. В зависимости от параметров он все равно может читать материал, который он не должен. В зависимости от операционной среды и того, что еще делает программа, опасность от неправильного чтения может быть или не быть менее серьезной, чем от неправильной записи.

Ответ 7

Очень важно помнить, что sprintf() добавляет символ ASCII 0 в качестве ограничителя строк в конце каждой строки. Поэтому буфер назначения должен иметь не менее n + 1 байт (для печати слова "HELLO" требуется 6-байтовый буфер, NOT 5)

В приведенном ниже примере это может быть не очевидно, но в 2-байтовом целевом буфере второй байт будет перезаписан символом ASCII 0. Если для буфера был выделен только 1 байт, это вызовет переполнение буфера.

char buf[3] = {'1', '2'};
int n = sprintf(buf, "A");

Также обратите внимание, что возвращаемое значение sprintf() не включает символ нулевого завершения. В приведенном выше примере было написано 2 байта, но функция возвращает "1".

В приведенном ниже примере первый байт переменной-члена класса я будет частично перезаписан sprintf() (в 32-битной системе).

struct S
{
    char buf[4];
    int i;
};


int main()
{
    struct S s = { };
    s.i = 12345;

    int num = sprintf(s.buf, "ABCD");
    // The value of s.i is NOT 12345 anymore !

    return 0;
}

Ответ 8

Я в значительной степени привел небольшой пример того, как можно избавиться от объявления размера буфера для sprintf (если вы, конечно, собирались это делать!), И без snprintf envolved....

Примечание: это пример APPEND/CONCATENATION, посмотрите здесь