Уязвимости из OWASP Top 10. A1: 2017 – Injections (Часть 1)
Описание уязвимостей — это одно, а вот попробовать найти уязвимость и поработать с ней — совсем другое дело. Именно для этих целей создаются и развиваются специальные приложения, в которых намеренно оставлены уязвимости. Если набрать в поисковой системе запрос «Purposely vulnerable app», вы найдете ни один десяток ссылок.
В этом цикле мы начнем разбирать уязвимости из OWASP Top 10, и в качестве полигона я буду использовать такое намеренно уязвимое приложение. В моем случае это будет OWASP Mutillidae II. Это не то, чтобы самый лучший вариант, но в нем уязвимости структурированы именно так, как нужно для образовательных целей.
Дайте вашу руку
Итак, первая уязвимость — это инъекции. В OWASP Mutillidae II представлено несколько вариантов, и начнем мы с самого простого «SQLi extract Data» > «User Info (SQL)».
Перед нами обычное окно аутентификации — то, с чем вы взаимодействуете ежедневно:
У нас нет ни логина, ни пароля — ничего. Ну что ж, давайте зарегистрируемся на этом сайте. Я создам пользователя с именем belowzero273 и паролем 123:
Отлично! Мы создали учетную запись, торжественная надпись »1 rows inserted» как бы намекает, что добавлена некая строка, судя по всему в таблицу, потому что большое количество учетных записей проще всего хранить в СУБД.
Теперь залогинимся с нашей новой учетной записью в систему. Успешно, отлично.
Когда мы работаем с веб-страницей, всегда нужно помнить, что наше взаимодействие не ограничивается содержимым страницы. Есть еще строка адреса, например. В которой мы можем заметить массу интересного:
http://127.0.0.1/mutillidae/index.php?page=user-info.php&username=belowzero273&password=123&user-info-php-submit-button=View+Account+Details
Например, мы видим, что пароль передается в открытом виде. Это плохо, так как трафик могут перехватить. Но, возможно, не такая уж катастрофа — трафик будет обернут в TLS.
Давайте попробуем заменить пароль прямо в строке, например, этот кусок:
username=belowzero273&password=123
на
username=belowzero273&password=12345
Пароль, конечно же, теперь неверный, и мы получили соответствующую ошибку: и это плохо. Плохо, потому что с помощью THC-Hydra, про которую я рассказывал тут, мы можем подобрать этот пароль, подставляя его в эту строку без всякой формы. Но это долго и не всегда может привести к положительному результату в приемлемые сроки.
У нас нет столько времени, поэтому попробуем кое-что другое. Добавим к нашему неправильному паролю знак апострофа:
username=belowzero273&password=123
на
username=belowzero273&password=12345'
Теперь мы получили полноценную ошибку:
В ошибках нет ничего плохого. Как иначе диагностировать неисправность, если мы не видим обратной связи от приложения? Но такие ошибки не должны быть видны пользователям и злоумышленникам.
Теперь мы видим, что на самом деле, когда мы нажимаем кнопку «Отправить» в фоне выполняется следующий запрос:
SELECT * FROM accounts WHERE username='belowzero273' AND password='12345'
Апострофами выделяются стринговые переменные. Наше веб-приложение не фильтрует данные, которые вводит пользователь, и своим дополнительным апострофом мы нарушили запрос. Теперь, когда мы знаем, как выглядит этот запрос в фоне, нужно придумать, как изменить его, чтобы получить нужную нам информацию из базы данных.
Обратите вниманием, что в запросе стоит функция and, то есть обе переменные должны оказаться верны, ведь это комбинация логина и пароля. Логично.
Давайте еще немного поколдуем над этим запросом:
SELECT * FROM accounts WHERE (username='belowzero273' AND password='12345') OR 1=’1
Мы добавили условие, которое выполняется всегда: 1=1. И запрос будет выполнен в двух случаях: или мы угадали комбинацию логина и пароля, или 1=1. То есть всегда будет выполняться.
Получается, что в качестве пароля можно указать следующее:
' or 1='1
Апостроф в конце строки уже не нужен, логика веб-приложения поставит его за нас. Бум! И мы получили выборку из базы со всеми аккаунтами:
Круто, теперь у нас есть логины и пароли всех, кто зарегистрирован в этом приложении. И даже хуже, пароли тут лежат в открытом виде.
Что с этим не так? А то, что большинство людей используют одни и те же логины и пароли для всех сайтов. И зарегистрировавшись таким образом на одном уязвимом сайте, они могут лишиться доступа ко всем своим сайтам.
Можно еще поэкспериментировать с этой уязвимостью, например, поискать пароль администратора. Для этого в качестве логина подставим:
admin' or 1='1
То есть мы ищем запись в базе, где логин admin, а пароль — любой.
Пароль не найден!
Пароли не всегда хранятся в открытом виде, но не значит, что аутентификация делается безопасно. Посмотрим второй пример в OWASP Mutillidae II «SQLi Bypass Authentication» > «Login».
Аутентификацию можно обойти, если сформировать соответствующий запрос. Совсем недавно мы создали учетную запись belowzero273, а теперь давайте в качестве логина укажем:
' or ('a' = 'a' and username='belowzero273') --
Здесь мы проверяем заведомо правильное условие — а=а, а логин — belowzero273, а также отсекаем часть запроса с помощью »–».
Жмем Enter и успешно входим в систему при том, что свой пароль мы нигде не указали.
Так просто?
Иногда клиенты спрашивают, неужели так просто можно обойти аутентификацию на нашем сайте? Обычно я отвечаю вопросом на вопрос: «А вы уверены, что этого уже не произошло?». Инъекции не случайно находятся в топе рейтинга OWASP, так как эти уязвимости, как правило, имеют катастрофические последствия в случае реализации.
На практике, конечно, все бывает несколько сложнее, чем в этих примерах. Но именно на таких базовых примерах начинает складываться понимание более глубоких проблем. Продолжим в следующий раз.
Прочитать блог автора статьи можно по этой ссылке.