Проблема с переопределением адреса возврата при выполнении переполнения буфера, что я упускаю?

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

Играя с, вероятно, самым базовым методом переполнения буфера на моем Raspberry Pi, он выглядит так

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char buf[256];
    strcpy(buf, argv[1]);
    printf("%s\n", buf);
    return 0;
}

На каком этапе я застрял, так это в переопределении адреса возврата. Я открываю gdb и ищу его, выполняя

(gdb) r $(python -c "print('A'*260)")
Запуск программы: /home/user/buffer-overflow/simple $(python -c "print('A'*260)")
[Отладка потоков с использованием libthread_db включена]
Используется библиотека libthread_db хоста "/lib/arm-linux-gnueabihf/libthread_db.so.1".
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Программа получила сигнал SIGSEGV, ошибка сегментации.
0xb6e409d0 в _IO_new_file_write (f=0x11f14, data=<оптимизировано>, n=<оптимизировано>) в fileops.c:1191
1191    fileops.c: Нет такого файла или директории.
(gdb) r $(python -c "print('A'*261)")
Программа, которая отлаживается, уже была запущена.
Начать с начала? (y или n) y
Запуск программы: /home/user/buffer-overflow/simple $(python -c "print('A'*261)")
[Отладка потоков с использованием libthread_db включена]
Используется библиотека libthread_db хоста "/lib/arm-linux-gnueabihf/libthread_db.so.1".
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Программа получила сигнал SIGSEGV, ошибка сегментации.
0xb6e40040 в ?? () из /lib/arm-linux-gnueabihf/libc.so.6
(gdb) r $(python -c "print('A'*262)")
Программа, которая отлаживается, уже была запущена.
Начать с начала? (y или n) y
Запуск программы: /home/user/buffer-overflow/simple $(python -c "print('A'*262)")
[Отладка потоков с использованием libthread_db включена]
Используется библиотека libthread_db хоста "/lib/arm-linux-gnueabihf/libthread_db.so.1".
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Программа получила сигнал SIGSEGV, ошибка сегментации.
0xb6004140 в ?? ()

Как вы можете видеть, я его нашел, но посмотрите на первый байт. Когда я переопределяю его на \x41, он меняется с \xd0 на \x40, а не на \x41, затем на последнем шаге, когда я увеличиваю его на 1, я действительно уверен, что переопределяю его, но он остается \x40. У меня нет идеи, почему… Это какая-то защита от “переполнения буфера”, и мне нужно найти символ, который переводится в нужный мне байт, или что-то еще?

Редактировать: У меня есть Raspberry Pi Model B, и я установил их самую новую 32-битную ОС:

Raspberry Pi OS Lite
Дата выпуска: 15 марта 2024 года
Система: 32 бита
Версия ядра: 6.6
Версия Debian: 12 (bookworm)
Размер: 474MB
SHA256: 4fa99737265ac338a9ed0643f502246b97b928e5dfffa92939242e26e290638d

Я бы временно отключил ASLR, выполнив echo 0 | sudo tee /proc/sys/kernel/randomize_va_space.

Затем измените свой адрес возврата, перекомпилировав свою программу без защиты стека, используя gcc -fno-stack-protector -z execstack -o simple simple.c.

Кроме того, вся эта информация является общедоступной, и следующие сайты помогли мне написать мой краткий ответ:

https://resources.infosecinstitute.com/topic/buffer-overflow-attack/

И

https://owasp.org/www-community/vulnerabilities/Buffer_Overflow

Если я смог найти несколько надежных потенциальных решений с помощью конкретного поиска в Google – вы тоже сможете.

Судя по выводам отладчика, похоже, что сбой происходит в libc во время printf, а не в вашей программе, когда она возвращается. Я не совсем уверен, почему это происходит, но, возможно, вам стоит разобраться в этом, проходя через вызов printf.

Возможно, будет проще удалить printf для ваших учебных целей.

Ответ или решение

Проблема переопределения адреса возврата при выполнении атаки переполнения буфера

Ваша задача — разобраться с проблемой переопределения адреса возврата при выполнении атаки переполнения буфера на платформе Raspberry Pi. Давайте внимательно рассмотрим данную ситуацию, исходя из предоставленного вами кода и отладочной информации из gdb.

Анализ кода

Ваш исходный код выглядит так:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char buf[256];
    strcpy(buf, argv[1]);
    printf("%s\n", buf);
    return 0;
}

Здесь вы используете функцию strcpy, которая не проверяет длину копируемой строки и может привести к переполнение буфера, что и применяется в атаке.

Изучение поведения с помощью gdb

Вы использовали gdb для диагностики:

(gdb) r $(python -c "print('A'*260)")

При этом программа аварийно завершает свою работу с ошибкой сегментации (SIGSEGV). Обратите внимание на то, что вы увеличиваете размер входной строки и наблюдаете изменения в адресе, указатели которых вы пытаетесь перезаписать.

Понимание изменения значения

Вы заметили, что при переполнении буфера происходит переопределение адреса возврата, но вместо ожидаемого значения вы получаете \x40 вместо \x41. Это может быть вызвано несколькими факторами:

  1. Расположение стека и поведение компилятора: На 32-битной архитектуре Raspberry Pi адреса выравниваются по определённому принципу. Это значит, что не всегда вы сможете записать конкретный байт по желаемому адресу. Например, механизм выравнивания может привести к тому, что адрес возврата будет изменён компилятором для соблюдения выравнивания.

  2. Stack Smashing Protector (SSP): Многие компиляторы, включая GCC, могут включать защиту стека по умолчанию, чтобы предотвратить переполнение. Вам следует отключить её с помощью флага -fno-stack-protector.

  3. Address Space Layout Randomization (ASLR): Если ASLR включен, место расположения стека тоже будет произвольным при каждом запуске программы. Для отключения ASLR вы можете использовать команду:

    echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
  4. Использование флага -z execstack: Это позволит вам исполнять код, который находится в области памяти, где находится стек, что может быть необходимо для успешной эксплуатации.

Рекомендации по исправлению

Для успешного выполнения атаки вам нужно:

  1. Собрать вашу программу без защиты стека:

    gcc -fno-stack-protector -z execstack -o simple simple.c
  2. Отключить ASLR перед запуском программы, как было описано выше.

  3. Понимать и контролировать выравнивание, точно измеряя нужные позиции в памяти.

  4. Шаг за шагом отлаживать программу, используя gdb для детального понимания того, как программа управляет стеком.

Попробуйте исключить вызов printf, чтобы избежать потенциальных сбоев в библиотеке при выводе данных.

Заключение

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

Если у вас остались вопросы, или вы хотите углубиться в детали, не стесняйтесь задавать их. Успехов в вашем исследовании переполнения буфера!

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

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