Странное поведение при переполнении буфера

Я установил дистрибутив Linux с именем DVL (чертовски уязвимый Linux) и тренируюсь с эксплойтами переполнения буфера. Я написал две практически идентичные программы, уязвимые для bof:

//bof_n.c
#include <stdio.h>
void bof() {
  printf("BOF");
}

void foo(char* argv) {
  char buf[10];
  strcpy(buf, argv);
  prinf("foo");
}

int main(int argc, char* argv[]) {
  if (argc >= 1) {
    foo(argv[1]);
  }
  return 0;
}

и

//bof.c
#include <stdio.h>
void bof() {
  printf("BOF!\n");//this is the only change
}

void foo(char* argv) {
  char buf[10];
  strcpy(buf, argv);
  prinf("foo");
}

int main(int argc, char* argv[]) {
  if (argc >= 1) {
    foo(argv[1]);
  }
  return 0;
}

После этого я их скомпилировал и в обоих случаях получил адрес функции bof() (например, objdump -d bof.o | grep bof). Назовем такой адрес ADDR размером 4 байта.

Я также обнаружил, что если я записываю 32 байта в переменную buf, регистр EIP полностью перезаписывается (я не могу скопировать сюда вывод gdb, так как он находится на виртуальной машине).

Теперь, если я сделаю:

./bof `perl -e 'print "\x90"x28 . "ADDR"'`

Я получил:

fooBOF!
Segmentation fault

Вместо этого, если я попробую тот же подход, но с использованием bof_n, я получу только сообщение «Ошибка сегментации». Поэтому я попытался увеличить количество раз, когда значение ADDR повторяется, и обнаружил, что если оно повторяется не менее 350 раз, я получаю желаемый результат. Но вместо точного вывода выше я получаю длинный список сообщений «BOF» одно за другим. Я пытался получить только одно сообщение "BOF", но, видимо, не могу этого сделать (получил или ноль, или их длинный список). Почему это происходит? Любая идея?

Я использую DVL с gcc 3.4.6.


person badnack    schedule 29.08.2013    source источник
comment
Размер perl -e 'print "\x90"x28 . "ADDR"' › размер buf[10]. 30+ › 10.   -  person someuser    schedule 29.08.2013
comment
Также размер результата perl -e 'print "\x90"x28 . "ADDR"' может быть больше 10.   -  person someuser    schedule 29.08.2013
comment
@mbrach: Все, что делает программное обеспечение, конечно, полностью детерминировано.   -  person arul    schedule 29.08.2013


Ответы (2)


Какова ваша цель?

Вам действительно следует использовать для этого отладчик, попробуйте отладчик GDB или gdb. С его помощью вы можете увидеть память/регистры/стек и дизассемблировать то, что сейчас происходит в системе.

Я предполагаю, что в первой функции строка длиной всего 3 символа оптимизируется до \x42\x4f\x46\x00, поэтому дизассемблирование может немного отличаться.

Исходный код C не имеет значения, вам нужно либо разобрать, либо fuzz оба двоичных файла, чтобы найти подходящий размер для обоих NOP салазок.

person arul    schedule 29.08.2013
comment
Хорошо, может быть, я был не очень точен. На самом деле я использую GDB и в примере bof_n, и я, наконец, узнаю, что проблема заключается в том, что сообщение BOF должно быть напечатано. EIP корректно перезаписывается и выполнение переходит к функции bof(), проблема только в том, что (не знаю почему) printf в этом случае не работает. Возможно из-за флашинга, попробую с fprintf и STDERR. - person badnack; 29.08.2013

Я узнал решение. Проблема заключалась в печати сообщения, а не в самой уязвимости переполнения буфера. На самом деле регистр eip корректно перезаписывался и в примере с bof_n, и программный поток корректно перенаправлялся в функции bof(). Проблема заключалась в том, что, по-видимому, стандартный вывод не был очищен до ошибки сегментации, и, следовательно, сообщение не отображалось.

Вместо этого, используя fprintf(stderr, "BOF");, я наконец получаю сообщение "BOF".

person badnack    schedule 29.08.2013