Переполнение буфера в функции sprintf? - PullRequest
11 голосов
/ 26 ноября 2010
{     
    char buf[8];
    sprintf(buf,"AAAA%3s","XXXXXXXX");
    printf("%s\n",buf);
}

что будет?

В буфере 8 символов и осталось только 3 свободных символа, однако длина «XXXXXXXX» составляет 8 символов.

Я беру тест с Visual Studion 2008 на Windows 7. В результате программа напечатала: AAAXXXXXXX и произошла ошибка во время выполнения.

Ответы [ 7 ]

11 голосов
/ 26 ноября 2010

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

Если ваша строка XXX... поступает из неконтролируемых источников, вы очень близки к созданию уязвимости переполнения буфера.

(1) Ваш стек обычно «увеличивается» назад, то есть чем меньше адреса, тем больше заполнен стек.

(2) В строках ожидается, что символы, принадлежащие этой строке, будут сохранены, чтобы символ n + 1 сохранялся после символа n.

(3) Когда вы вызываете функцию, адрес возврата, т. Е. Адрес инструкции, которая должна быть выполнена после возврата функции, помещается в стек (среди прочего, как правило).

Теперь рассмотрим стек стека вашей функции.

|----------------|
| buf [size 8]   |
|----------------|
| (func args)    |
|----------------|
| (other stuff)  |
|----------------|
| return address |
|----------------|

Узнав, каково смещение между buf и обратным адресом в стеке, злонамеренный пользователь может манипулировать вводом в ваше приложение таким образом, чтобы строка XXX... содержала адрес, выбранный злоумышленником, всего за точка, в которой неуправляемая функция sprintf перезапишет адрес возврата в стеке. (Примечание: лучше использовать snprintf, если оно доступно для вас). Тем самым злоумышленник осуществил атаку переполнения буфера . Он может использовать что-то вроде NOP sled техника , чтобы ваше приложение запустило для него shell . Если бы вы писали приложение, которое запускалось под учетной записью привилегированного пользователя, вы бы просто предоставили злоумышленнику первоклассную запись в систему вашего клиента, если хотите, - дыру ACE .

Обновление

Ошибка времени выполнения может возникать из-за перезаписанного адреса возврата. Поскольку вы заполнили его, в основном, gargabe, адрес, на который перескочил процессор, вероятно, содержал байтовые последовательности, которые, интерпретируемые как программный текст, вызывают недопустимый доступ к памяти (или сам адрес уже был неправильным).

Следует отметить, что некоторые компиляторы могут помочь против ошибок такого рода. GCC, например, имеет -fstack-protector. Я не знаю, насколько хороши эти функции.

8 голосов
/ 26 ноября 2010

Функция sprintf() будет записывать после массива так же, как она записывает в строку, и, следовательно, вызывает неопределенное поведение. Глядя на ваш код, он, вероятно, записывает первые несколько байтов того, что будет следующим в стеке, или вызывает ошибку во время выполнения, но такое поведение не гарантируется.

Неопределенное поведение в буквальном смысле означает все, что может произойти . Это означает, что ваш код может ничего не делать неправильно, вызывать ошибки во время выполнения или приводить к взрыву вашего компьютера, выигрывать в лотерею, делать единороги появляются на вашем заднем дворе, воскрешают Гитлера из мертвых или убивают президента Соединенных Штатов. Пожалуйста, не делайте этого.

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

6 голосов
/ 26 ноября 2010

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

5 голосов
/ 26 ноября 2010

В вашей строке формата есть ошибка / опечатка. Вместо "AAAA%3s" это должно быть "AAAA%.3s". Поле [минимальная] ширина и точность поля очень разные. Первый из них устанавливает минимальное число байтов, которое поле будет заполнять. Последний (для строк) устанавливает максимальное количество байтов, которое будет выводиться; дополнительные байты строки не проверяются и не копируются в выходные данные.

1 голос
/ 26 ноября 2010

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

После обнаружения уязвимости переполнения злоумышленники увидят, как вызов получает свой пользовательский ввод и направляется через вызов функции. Затем злоумышленники могут написать эксплойт , который заставляет программное обеспечение делать то, что он обычно не делает. Это может варьироваться от простого сбоя машины до внедрения кода, чтобы злоумышленник мог получить удаленный доступ к машине.

Многие функтины в С приводят к ошибкам, если не используются должным образом. Некоторые функции предоставляют альтернативные решения:

Avoid      prefer
sprintf    snprintf
vsprintf   vsnprintf
strcat     strlcat
strcpy     strlcpy
strncat    strlcat
strncpy    strlcpy

Источник: ECSP-Secure Programmer.

1 голос
/ 26 ноября 2010

«In silico» совершенно правильно, но, вероятно, поскольку ядра компьютеров намного умнее, чем раньше, они не позволят вам писать о том, что следует за char buf[4];, и убьют вашу программу и выдадут сигнал об ошибке сегментации..

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

А также, как он сказал НИКОГДА сделать это.

0 голосов
/ 12 сентября 2014

что будет? ...

{     
    char buf[8];
    sprintf(buf,"AAAA%3s","XXXXXXXX");
    printf("%s\n",buf);
}

В Windows вы должны использовать sprintf_s. Код должен провалить аудит, поэтому он не должен превращаться в рабочий. Для справки см. Microsoft Написание безопасного кода (Best Practices для разработчиков) . В частности, см. Главу 5.


В Linux, если компилятор и платформа предоставляют FORTIFY_SOURCE, приведенный выше код должен привести к вызову abort(). Многие современные платформы Linux поддерживают его, поэтому я ожидаю этого.

FORTIFY_SOURCE использует «более безопасные» варианты функций высокого риска, такие как memcpy, strcpy и sprintf. Компилятор использует более безопасные варианты, когда он может определить размер буфера назначения. Если копия превысит размер буфера назначения, программа вызывает abort().

Чтобы отключить FORTIFY_SOURCE для тестирования, вы должны скомпилировать программу с -U_FORTIFY_SOURCE или -D_FORTIFY_SOURCE=0.


Чтобы ответить на комментарий @ prng относительно переносимости, strcpy_s, printf_s, sprintf_s и друзья являются стандартными C. См. ISO / IEC TR 24731-1 .

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

...