Вопрос или проблема
Я пытаюсь сделать простой эксплойт для переполнения буфера на примере программы, чтобы лучше понять бинарное эксплоатирование. Цель состоит в том, чтобы просто записать шеллкод в стек и выполнить его. Однако, несмотря на все ресурсы в интернете, я продолжаю сталкиваться с странными проблемами, для которых не могу найти решения. Надеюсь, кто-то сможет прояснить для меня несколько вещей 🙂
Следующая программа является целевым приложением:
#include <string.h>
#include <stdio.h>
int main (int argc, char** argv)
{
char buffer[8];
if (argv[1] == NULL) return 0;
strcpy(buffer, argv[1]);
printf("Текст: %s\n", buffer);
return 0;
}
Чтобы упростить задачу, я отключил несколько защитных механизмов.
Бинарник скомпилирован так: gcc app.c -o app -z execstack -fno-stack-protector -m32
.
Также ASLR в данный момент отключен: (/proc/sys/kernel/randomize_va_space == 0
).
Кроме того, я использую gdb-peda, потому что, насколько я слышал, с ним немного удобнее работать при бинарном эксплоатировании.
В настоящее время я испытываю следующую проблему:
Я не могу перезаписать EIP, независимо от того, насколько длинной является строка ввода:
Обратите внимание, что EIP всегда указывает на адрес возврата из main? Почему, он должен быть перезаписан. Увеличение количества A ничего не меняет. Также еще более странно, что ESP никогда не заполняется полностью A, он всегда заканчивается на 0x4141413d (‘=AAA’)
Поскольку я не могу правильно перезаписать EIP, я не могу продолжать.
Кто-нибудь знает, почему это происходит и что я могу с этим сделать?
Вы получаете ошибку сегментации, потому что переполнили буфер и тем самым испортили ESP. Если вы посмотрите на ошибку, там написано:
Не удалось прочитать память по адресу 0x4141413d
, и это значение ESP. Поэтому, когда он пытается выполнить ret
, стек указывает на неинициализированную память, вызывая ошибку сегментации. Причина, по которой в конце 0x3d
, а не 0x41
, заключается в том, что ESP был уменьшен на 4, прежде чем попытаться прочитать значение из ESP. Вы можете попробовать использовать более короткую строку из A
и посмотреть, удастся ли вам оставить ESP нетронутым.
Переполнения буфера в main()
обычно более запутанные и ненадежные, так как они срабатывают только после завершения программы (возврат из main), в отличие от работы программы. Для учебных целей обычно лучше вызывать переполнение в функции, чтобы оно срабатывало при ее возврате, что-то вроде этого:
#include <string.h>
#include <stdio.h>
void hello(char* name) {
char buffer[8];
strcpy(buffer, name);
printf("Привет %s\n", buffer);
}
int main (int argc, char** argv)
{
if (argv[1] == NULL) return 0;
hello(argv[1]);
return 0;
}
Удачи!
Ответ или решение
Конечно, давайте разберёмся с вашей проблемой.
Ваша цель — понять, как работает переполнение буфера и как можно перезаписать адрес возврата для выполнения собственного кода (например, шеллкода). Вы привели пример простой программы на C, которая уязвима к переполнению буфера, но столкнулись с проблемой, когда не удаётся перезаписать EIP (адрес возврата), как ожидалось.
Описание проблемы
-
Коррупция стека: Судя по вашему описанию, когда вы переполняете буфер, вы также затрагиваете значение указателя стека (ESP), что в конечном итоге приводит к ошибке сегментации. Ошибка
Could not read memory at 0x4141413d
указывает на то, что при попытке выполнитьret
, стек указывает на недопустимый адрес, который вы не инициализировали, и это вызывает сбой. -
Неверное расположение EIP: Основная проблема заключается в том, что в конце стека остается
0x4141413d
, в то время как мы ожидаем, что EIP будет перезаписан на адрес, указывающий на ваш шеллкод. Это происходит потому, что вы перезаписываете ESP, и когда программа пытается выполнить возврат, она ссылается на искаженный или недопустимый адрес.
Рекомендации по решению проблемы
-
Сократите длину входной строки: Попробуйте использовать меньшую строку (например, 16-24 символа). Это поможет вам избежать слишком большого переполнения стека, которое может повредить указатель стека.
-
Измените структуру программы: Переполнение буфера внутри функции, отличной от
main
, может помочь. Например, создайте функциюhello
(как в вашем примере), которая будет содержать буфер. Это позволит вам перехватить управление перед тем, как программа вернёт управление вmain
. Вместо этого вы "атакуетесь" на возврат изhello
.Пример кода:
#include <string.h> #include <stdio.h> void hello(char* name) { char buffer[8]; strcpy(buffer, name); printf("Hello %s\n", buffer); } int main (int argc, char** argv) { if (argv[1] == NULL) return 0; hello(argv[1]); return 0; }
-
Используйте отладчик: Проводите отладку с помощью GDB, чтобы отслеживать изменения в регистре ESP и другой информации на стеке после ввода различных значений. Это поможет понять, на сколько байтов вы можете переполнить буфер, не затрагивая указатель стека.
-
Имейте в виду адреса: Помните о том, где будет находиться ваш шеллкод. Он должен находиться рядом с буфером. После того как вы определите адреса, убедитесь, что вы корректно задаёте значение EIP в своем переполненном строковом буфере.
Заключение
Переполнение буфера — это процесс, требующий внимания к деталям и тщательного понимания архитектуры стека. Попробуйте реализацию, описанную выше, и следите за изменениями с помощью GDB. Это поможет вам лучше понять, как переполнение буфера в действительности воздействует на память и как добиться перезаписи EIP. Удачи в ваших экспериментах с бинарным взломом!