переполнение буфера функции sprintf?

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

что будет?

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

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

7 ответов


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

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

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

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

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

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

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

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

обновление

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

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


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

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

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


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

snprintf гарантированно не пишет больше, чем размер байтов в str, поэтому используйте это может помочь избежать риска переполнение буфера Wiki


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


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

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

и как он сказал никогда сделать это.


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

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

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

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

источник: ECSP-Безопасный программист.


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

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

на Windows, вы должны использовать sprintf_s. Код должен провалить аудит, поэтому он не должен входить в производство. Для справки см. Microsoft Написание Защищенного Кода (Рекомендации Разработчика). В частности, см. Главу 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.