#include <stdio.h>
int main(void){
int len;
char input[40] = "";
printf("input length : \n");
scanf("%d", &len);
if (len > 40){
return 0;
}
read(0, input, len);
printf("%s", input);
return 0;
}
Этот код уязвим для атаки переполнения буфера, и я пытаюсь выяснить, почему. Я много пробовал, но ни один код атаки не смог обойти операторы «if».
Как мне использовать этот код?
Что такое read? Я предполагаю, что это стандартная функция libc, но тогда соответствующий заголовочный файл отсутствует (даже если stdio.h включает, например, unistd.h, неясно, откуда берется read).
Обратите внимание, что в вашем примере кода нет обработки ошибок, поскольку вы не проверяете возвращаемое значение scanf. Если я введу неверный ввод, например a, программа будет вести себя случайным образом, поскольку len не инициализирована и будет иметь случайное значение.
@user694733 user694733 неопределенное значение, а не «случайное».
@Clifford Если мы избегаем терминов непрофессионала и используем слова из стандарта, я считаю, что правильный термин имеет неопределенное значение. :)
@ user694733 - да, даже лучше, «случайно», что было просто неточно (педантично).





Когда len == 40, read(0, input, len); читается 40 символов.
printf("%s", input); пытается напечатать input, ожидая, что это будет строка. Однако в input больше нет нулевого символа, поэтому input не содержит
строка.
Результат: неопределенное поведение (UB).
Вместо printf("%s", input); используйте printf("<%.40s>\n", input);, чтобы увидеть (без UB), что было прочитано.
Помимо точного числа 40, ведущего к строке, завершающейся не нулем, эта программа также принимает -1 и т. д. в качестве входных данных.
read, в свою очередь, имеет параметр без знака size_t, поэтому, если я введу -1, то len будет преобразовано в 0xFFFF... — очень большое число.
Самая большая ошибка — объявить размер буфера как целое число со знаком. Буферы не могут иметь отрицательный размер. Он должен был быть объявлен как size_t, а входные данные должны были приниматься с %zu или, еще лучше, как строка, проходящая через fgets и тому подобное, которая затем анализируется и очищается.
Согласованность типов, соответствующая длина буфера и проверка длины имеют решающее значение. Например, я бы предложил следующие изменения:
int main(void)
{
size_t len = 0 ; // *** Correct type for read() length parameter
char input[41] = "" ; // *** Include room for nul terminator
printf( "Input length : \n" );
int cvt = scanf("%zu", &len); // *** Correct format specifier for size_t
if ( cvt == 0 || // *** Check valid conversion of input
len > sizeof(input) - 1 ) // *** Leave room for nul terminator
// *** And no "magic" number
{
return 0;
}
read( 0, input, len ) ;
printf( "%s", input ) ;
return 0;
}
Ваш код вызывает неопределенное поведение, поскольку
inputне является строкой, завершающейся нулем. Вы инициализируете все 0 байтов, но если вы читаете 40 байтов из файла, нулевой терминатор заменяется тем, что поступает из файла. Но я бы не назвал это переполнением буфера.