Почему этот код уязвим для атак переполнения буфера?



int func(char* str)
{
char buffer[100];
unsigned short len = strlen(str);

if(len >= 100)
{
return (-1);
}

strncpy(buffer,str,strlen(str));
return 0;
}


этот код уязвим для атаки переполнения буфера, и я пытаюсь выяснить, почему. Я думаю, что это имеет отношение к len объявляется a short вместо int, но я не совсем уверен.



какие идеи?

592   5  

5 ответов:

на большинстве компиляторов максимальное значение unsigned short это 65535.

любое значение выше этого обертывается, поэтому 65536 становится 0, а 65600 становится 65.

это означает, что длинные строки правильной длины (например, 65600) пройдут проверку и переполнят буфер.


использовать size_t сохранить результат strlen(), а не unsigned short, и сравнить len к выражению, которое непосредственно кодирует размер buffer. Так пример:

char buffer[100];
size_t len = strlen(str);
if (len >= sizeof(buffer) / sizeof(buffer[0]))  return -1;
memcpy(buffer, str, len + 1);

проблема:

strncpy(buffer,str,strlen(str));
                   ^^^^^^^^^^^

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

strncpy(buffer,str, sizeof(buff) - 1);
buffer[sizeof(buff) - 1] = '';

это ограничивает количество данных, скопированных до фактического размера буфера минус один для нулевого завершающего символа. Затем мы устанавливаем последний байт в поле буфер к нулевому символу в качестве дополнительной гарантии. Причина этого заключается в том, что strncpy будет копировать до n байтов, включая завершающий null, если strlen(str)

надеюсь, что это помогает.

EDIT: после дальнейшего изучения и ввода от других, возможное кодирование для функции следует:

int func (char *str)
  {
    char buffer[100];
    unsigned short size = sizeof(buffer);
    unsigned short len = strlen(str);

    if (len > size - 1) return(-1);
    memcpy(buffer, str, len + 1);
    buffer[size - 1] = '';
    return(0);
  }

так как мы уже знаем длина строки, мы можем использовать memcpy для копирования строки из местоположения, на которое ссылается str в буфер. Обратите внимание, что на странице руководства для strlen (3) (в системе FreeBSD 9.3) указано следующее:

 The strlen() function returns the number of characters that precede the
 terminating NUL character.  The strnlen() function returns either the
 same result as strlen() or maxlen, whichever is smaller.

который я интерпретирую как то, что длина строки не включает в себя null. Вот почему я копирую LEN + 1 байт, чтобы включить null, и тест проверяет, чтобы убедиться, что длина

EDIT: оказывается, размер чего-то начинается с 1, а доступ начинается с 0, поэтому -2 раньше был неправильным, потому что он вернул бы ошибку для чего-либо > 98 байт, но он должен быть > 99 байт.

EDIT: хотя ответ о беззнаковом коротком обычно верен, поскольку максимальная длина, которая может быть представлена, составляет 65 535 символов, это действительно не имеет значения, потому что если данная строка длиннее, чем это значение будет обтекать. Это похоже на то, что вы берете 75,231 (что составляет 0x000125DF) и маскируете верхние 16 бит, давая вам 9695 (0x000025DF). Единственная проблема, которую я вижу с этим, - это первые 100 символов после 65 535, поскольку проверка длины позволит скопировать,но он будет копировать только до первых 100 символов строки во всех случаях и null завершает строку. Так что даже с проблемой wraparound буфера все равно не будет переполненный.

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

даже если вы используете strncpy, длина отсечки по-прежнему зависит от переданного строкового указателя. Вы понятия не имеете, как долго эта строка (расположение нулевого Терминатора относительно указателя, то есть). Так зовет strlen только открывает вам уязвимость. Если вы хотите быть более безопасным, используйте strnlen(str, 100).

полный исправленный код будет:

int func(char *str) {
   char buffer[100];
   unsigned short len = strnlen(str, 100); // sizeof buffer

   if (len >= 100) {
     return -1;
   }

   strcpy(buffer, str); // this is safe since null terminator is less than 100th index
   return 0;
}

ответ с оберткой прав. Но есть проблема, о которой я думаю не упоминалось если(len >= 100)

Ну, если Len будет 100, мы скопируем 100 элементов, и у нас не будет трейлинга \0. Это явно означало бы, что любая другая функция, зависящая от правильной конечной строки, выйдет за пределы исходного массива.

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

Помимо вопросов безопасности, связанных с призывом strlen более одного раза, как правило, не следует использовать строковые методы на строках, длина которых точно известна [для большинства строковых функций есть только очень узкий случай, когда они должны использоваться--на строках, для которых максимальная длина может быть гарантирована, но точная длина не известна]. Как только длина входной строки известна, а длина выходного буфера известна, следует выяснить, насколько большой должна быть область скопировано и затем используйте memcpy() фактически выполнить копию в вопросе. Хотя вполне возможно, что strcpy может превзойти memcpy() при копировании строки всего 1-3 байт или около того, на многих платформах memcpy() скорее всего, будет более чем в два раза быстрее при работе с большими строками.

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

char *strdupe(char const *src)
{
  size_t len = strlen(src);
  char *dest = malloc(len+1);
  // Calculation can't wrap if string is in valid-size memory block
  if (!dest) return (OUT_OF_MEMORY(),(char*)0); 
  // OUT_OF_MEMORY is expected to halt; the return guards if it doesn't
  memcpy(dest, src, len);      
  dest[len]=0;
  return dest;
}

обратите внимание, что последний оператор обычно может быть опущен, если memcpy обработал len+1 байт, но это другой поток, который должен был изменить исходную строку, результат может быть не завершенной нулем строкой назначения.

Comments

    Ничего не найдено.