Невозможно перезаписать EIP в примере с переполнением буфера.

Вопрос или проблема

Я пытаюсь сделать простой эксплойт для переполнения буфера на примере программы, чтобы лучше понять бинарное эксплоатирование. Цель состоит в том, чтобы просто записать шеллкод в стек и выполнить его. Однако, несмотря на все ресурсы в интернете, я продолжаю сталкиваться с странными проблемами, для которых не могу найти решения. Надеюсь, кто-то сможет прояснить для меня несколько вещей 🙂

Следующая программа является целевым приложением:

#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 (адрес возврата), как ожидалось.

Описание проблемы

  1. Коррупция стека: Судя по вашему описанию, когда вы переполняете буфер, вы также затрагиваете значение указателя стека (ESP), что в конечном итоге приводит к ошибке сегментации. Ошибка Could not read memory at 0x4141413d указывает на то, что при попытке выполнить ret, стек указывает на недопустимый адрес, который вы не инициализировали, и это вызывает сбой.

  2. Неверное расположение EIP: Основная проблема заключается в том, что в конце стека остается 0x4141413d, в то время как мы ожидаем, что EIP будет перезаписан на адрес, указывающий на ваш шеллкод. Это происходит потому, что вы перезаписываете ESP, и когда программа пытается выполнить возврат, она ссылается на искаженный или недопустимый адрес.

Рекомендации по решению проблемы

  1. Сократите длину входной строки: Попробуйте использовать меньшую строку (например, 16-24 символа). Это поможет вам избежать слишком большого переполнения стека, которое может повредить указатель стека.

  2. Измените структуру программы: Переполнение буфера внутри функции, отличной от 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;
    }
  3. Используйте отладчик: Проводите отладку с помощью GDB, чтобы отслеживать изменения в регистре ESP и другой информации на стеке после ввода различных значений. Это поможет понять, на сколько байтов вы можете переполнить буфер, не затрагивая указатель стека.

  4. Имейте в виду адреса: Помните о том, где будет находиться ваш шеллкод. Он должен находиться рядом с буфером. После того как вы определите адреса, убедитесь, что вы корректно задаёте значение EIP в своем переполненном строковом буфере.

Заключение

Переполнение буфера — это процесс, требующий внимания к деталям и тщательного понимания архитектуры стека. Попробуйте реализацию, описанную выше, и следите за изменениями с помощью GDB. Это поможет вам лучше понять, как переполнение буфера в действительности воздействует на память и как добиться перезаписи EIP. Удачи в ваших экспериментах с бинарным взломом!

Оцените материал
Добавить комментарий

Капча загружается...