Найти в Дзене

Простейший пример атаки через переполнение буфера

Посмотрим на конкретном примере, как работает взлом программы через так называемое "переполнение буфера". Возьмём простейшую программу на Си, которая запрашивает у пользователя пароль и выполняет те или иные действия в зависимости от того, правильный ли пароль был введён: Компилируем и проверяем, что программа работает корректно: Итак, наш crackme выдаёт сообщение "Access allowed" только в случае, если был указан суперсекретный пароль Qwerty123. Но что будет, если введённая строка окажется слишком длинной? Вот те раз! Программа вылетела с ошибкой "stack smashing detected". А что самое интересное, сравнение происходило не со строкой Qwerty123, а с чередой единиц. Дело в том, что scanf() записывает в память введённую строку целиком. Если буфер под неё окажется короче самой строки, будут переписаны соседние ячейки памяти. Этот эффект и называется "переполнением буфера". Опытным путём установим минимальное количество символов, которое нужно ввести для получения непредсказуемого поведения.

Посмотрим на конкретном примере, как работает взлом программы через так называемое "переполнение буфера".

Возьмём простейшую программу на Си, которая запрашивает у пользователя пароль и выполняет те или иные действия в зависимости от того, правильный ли пароль был введён:

Компилируем и проверяем, что программа работает корректно:

доступ разрешается только в случае ввода правильного пароля
доступ разрешается только в случае ввода правильного пароля

Итак, наш crackme выдаёт сообщение "Access allowed" только в случае, если был указан суперсекретный пароль Qwerty123. Но что будет, если введённая строка окажется слишком длинной?

программа падает с ошибкой
программа падает с ошибкой

Вот те раз! Программа вылетела с ошибкой "stack smashing detected". А что самое интересное, сравнение происходило не со строкой Qwerty123, а с чередой единиц.

Дело в том, что scanf() записывает в память введённую строку целиком. Если буфер под неё окажется короче самой строки, будут переписаны соседние ячейки памяти. Этот эффект и называется "переполнением буфера".

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

всё ещё доступ запрещён, но вместо пароля пустая строка
всё ещё доступ запрещён, но вместо пароля пустая строка

Теперь заменим первый символ нашей строки на символ с кодом x00:

опаньки!
опаньки!

Что же мы видим? Не зная пароля, мы добились от crackme сообщения о разрешении доступа. Поздравляю, мы взломали программу!

Подобрать нужную для взлома длину строки — это быстрее, чем подобрать сам пароль или найти его дизассемблированием. К тому же эта атака продолжит работать, даже если пароль будет изменён.

Меры противодействия

Как же бороться с мамкиным хакером, чтобы он не смог так просто взломать нашу программу? К счастью, достаточно заменить "%s" на "%20s" в параметрах scanf. Это наложит ограничение (20 символов в нашем случае) на длину вводимого буфера. Компилируем исправленную программу и проверяем эксплойт:

ну вот теперь другое дело
ну вот теперь другое дело

Теперь доступ запрещён, как и должно быть. Кроме того, исправленная программа не будет падать с ошибкой при получении слишком длинных строк.

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

Темы: #программирование #взлом #безопасность

Есть вопросы или дополнения к статье? Напишите их в комментариях!