Пример переполнения буфера, приводящего к утечке безопасности - PullRequest
15 голосов
/ 20 июля 2010

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

Я понимаю проблему переполнения буфера. Возьмите этот пример кода:

int main() {
   char buffer[1];
   int var = 0;

   scan("%s", &buffer);
   printf("var = 0x%x\n", var);
   return 0;
}

Когда я выполняю программу и набираю «abcde», программа выводит 0x65646362, который представляет собой «edcb» в шестнадцатеричном + little-endian. Однако я прочитал, что вы можете изменить значение eip, помещенное в стек, чтобы программа выполняла некоторый нежелательный код (например, прямо перед вызовом функции system ()).

Однако сборка функции начинается следующим образом:

push %ebp
mov %ebp, %esp
and $0xfffffff0, %esp
sub $0x20, %esp

Так как значение% esp является случайным в начале функции, и из-за этого "и", похоже, нет надежного способа записать точное значение в значение push eip.

Более того, я читал, что можно было выполнить код, который вы написали в буфере (здесь размер буфера составляет всего 1 байт, но на самом деле он был бы достаточно большим для хранения некоторого кода), но какое значение вы бы дали eip для того, чтобы сделать это (учитывая, что расположение буфера является случайным)?

Так почему же разработчики так обеспокоены проблемами безопасности (за исключением того, что программа может аварийно завершить работу)? У вас есть пример уязвимой программы и как ее взломать для выполнения нежелательного кода? Я попробовал это на Linux, Windows менее безопасна?

Ответы [ 6 ]

15 голосов
/ 20 июля 2010

Прочитайте отличную статью Алефа Первого: Уничтожение стека ради удовольствия и прибыли .

4 голосов
/ 20 июля 2010

Кроме того, вам не нужно перезаписывать EIP указателем на что-то в вашей строке. Например, вы можете перезаписать его указателем на system() и перезаписать следующее слово указателем на /bin/sh в фиксированном месте в образе программы.

Edit: обратите внимание, что system использует PATH (на самом деле он запускает команду через оболочку), поэтому "sh" будет так же хорошо; таким образом, любое английское слово, оканчивающееся на «sh» в конце строки, дает необходимый аргумент.

4 голосов
/ 20 июля 2010

Ну, во-первых, не стоит недооценивать опасности, связанные с возможностью ненадежно поместить значение в EIP. Если эксплойт работает один из 16 раз, а служба, на которую он нападает, автоматически перезапускается, как и многие веб-приложения, то злоумышленник, которому не удается получить доступ, всегда может попытаться повторить попытку.

Также во многих случаях значение ESP является менее случайным, чем вы думаете. Для начинающих в 32-битной системе это почти всегда кратно четырем. Это означает, что дополнительный отступ, предлагаемый инструкцией and $0xfffffff0, %esp, будет 0, 4, 8 или 12 байтов. Это означает, что можно просто повторить значение, которое должно быть записано в возвращаемом EIP четыре раза, чтобы покрыть все возможные смещения по адресу возвращаемого EIP.

На самом деле существуют более агрессивные механизмы защиты стека / переполнения буфера . Тем не менее, есть способы и средства даже вокруг них.

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

int main() {
  char buffer[1];
  int var = 0;

  var = SecurityCheck();

  scan("%s", &buffer);
  if (var != 0)
    GrantAccess();
  else
    DenyAccess()
}
1 голос
/ 20 июля 2010

Как уже упоминалось в других ответах, абсолютная надежность не всегда важна для успеха атаки.Приложения, которые перезапускаются автоматически, являются примером.Локально используемое переполнение буфера в программах suid было бы другим.И есть техника NOP-салазок, которая увеличивает шансы на успешную эксплуатацию, поместите много NOP перед вашим шеллкодом, чтобы у вас был гораздо лучший шанс правильно угадать «начало» вашего шеллкода.

Есть еще много техникдля повышения надежности атак.В Windows многие эксплойты переписывали обратный адрес с адресом «jmp% esp», расположенным где-то в программе (trampoline).

У «Небезопасного программирования на примере» был хороший прием дляLinux.Очистите свое окружение и поместите свой шеллкод в переменную окружения.В свое время это привело бы к предсказуемому адресу в верхней части стека.

И есть также варианты, такие как возврат в libc и программирование, ориентированное на возврат.

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

Подводя итог, нельзя сказать, что разработчики не являются параноиками, есть много способов использовать даже самые странные случаи, и помните:

  • Программа хорошего качества, когда она делает то, что есть
  • Программа безопасна, когда она делает то, что должна, и ничего более .
1 голос
/ 20 июля 2010

Классическим примером реального эксплойта, основанного на переполнении буфера, является Morris Worm 1988 года.

0 голосов
/ 20 июля 2010

Вот версия и учебник для Windows:

http://www.codeproject.com/KB/winsdk/CodeInject.aspx

Общий случай, о котором меня всегда предупреждали:

printf( string );

Поскольку пользователь может предоставить"%n" там, что позволяет вставлять все, что вы хотите в память.Все, что вам нужно сделать, это найти смещение памяти для системного вызова, передать несколько символов "%n" и ненужные символы и, таким образом, вставить адрес памяти в стек, где обычно находится вектор возврата.Вуаля - введите любой код, который вам нравится.

...