понимание опасностей спринта (...) - PullRequest
20 голосов
/ 08 сентября 2010

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), если длина сообщения >>1022 * str , есть переполнение буфера

и

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

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

Ответы [ 8 ]

22 голосов
/ 08 сентября 2010

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

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

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

char buffer[128];

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

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

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

// Behaviour is different in SUSv2; see
// "conforming to" section of man 3 sprintf

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

++length;           // +1 for null terminator
char *buffer = malloc(length);

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

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

10 голосов
/ 08 сентября 2010

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

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

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

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

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

#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 может быть просто.

4 голосов
/ 08 сентября 2010

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

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

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

3 голосов
/ 08 сентября 2010

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

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

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

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

1 голос
/ 08 сентября 2010

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

1 голос
/ 08 сентября 2010

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

0 голосов
/ 31 июля 2018

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

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

0 голосов
/ 17 августа 2017

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

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

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

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

В приведенном ниже примере первый байт переменной-члена класса 'i' будет частично перезаписан 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...