С переполнением в один байт «сорвать стек» невозможно, поскольку чтобы «дотянуться» до адреса возврата в большинстве случаев требуется «пересечь» сохраненное значение регистра EBP [320], занимающее четыре байта. Но ведь именно этот факт и можно использовать для атаки! Потом, переполняющийся буфер не всегда располагается на вершине стека. Скорее всего, за ним следуют некие локальные переменные, искажение значения которых может привести к нарушению нормальной работоспособности программы: от зависания до возможности несанкционированного вторжения в систему.
В примере, приведенном ниже (на диске, прилагаемом к книге, он находится в файле “/SRC/buff.var.c”), используется переменная-флаг noguest, нулевое значение которой открывает доступ в систему всем желающим:
Дизассемблирование позволяет установить, что переменная “noguest” расположена в «хвосте» буфера buff и может быть искажена при его переполнении. Поскольку, при проверке длины строки допущена ошибка «if (strlen(argv[a])»16)…», завершающий ноль шестнадцатисимвольной строки обнулит значение переменной “noguest” и откроет злоумышленнику путь в систему. Это демонстрирует следующий эксперимент:
· buff.var.exe 1234567890123456
· Password ok
Но если увеличить длину строки хотя бы на один байт, программа отбросит ее как неправильную:
· buff.var.exe 12345678901234567
· Too long arg: 12345678901234567
· Wrong password
Конечно, вероятность возникновения подобной ситуации на практике очень мала. Для атаки необходимо неблагоприятное стечение многих маловероятных обстоятельств. Размер буфера должен быть кратен величие выравнивания, иначе переполняющий байт запишется в «черную дыру» [321] и ничего не произойдет. Следующая за буфером переменная должна быть критична к обнулению, т.е. если программист открывал бы доступ на машину при ненулевом значении флага guest, опять бы ничего не произошло. Поэтому, в большинстве случаев несанкционированного доступа к машине получить не удастся, а вот «завесить» ее гораздо вероятнее.
Например, следующий код (на, диске, прилагаемом к книге, он находится в файле “/SRC/buff.var.2.c”), в отличие от предыдущего, трудно назвать искусственным и «притянутым за уши»:
Переполнение буфера вызовет запись нуля в счетчик цикла ‘a’, в результате чего цикл никогда не достигнет своего конца, а программа «зависнет». А если буфер окажется расположенным в вершине стека, то «вылетевший» за его пределы ноль исказит значение регистра EBP. Большинство компиляторов генерируют код, использующий для адресации локальных переменных регистр EBP, поэтому искажение его значения приведет к нарушению работы вызывающей процедуры.
Такую ситуацию демонстрирует следующий пример (на диске, прилагаемом к книге, он расположен в файле “/SRC/buff.ebp.c”):