Привет, уважаемый читатель!
В рамках данной статьи мы узнаем:
-
Какие API уязвимости есть в VAmPI?
-
Из-за чего эти уязвимости существуют и эксплуатируются?
-
Какие есть способы защитить веб-приложение?
-
Какой есть дополнительный материал для самостоятельного изучения?
Ссылка на GitHub VAmPI: https://github.com/erev0s/VAmPI
Настоятельно рекомендую сначала попробовать самим провести пентест данного веб-приложения!
Пользовался следующими виртуальными машинами:
-
Ubuntu (192.168.1.199) – там развернуто веб-приложение.
-
Kali Linux – машина для проведения атак.
Начинаем наше погружение!
Разработчик данной лабораторной работы нам выделил список имеющихся API уязвимостей:
-
SQL Injection
-
Unauthorized Password Change
-
Broken Object Level Authorization
-
Mass Assignment
-
Excessive Data Exposure through debug endpoint
-
User and Password Enumeration
-
(дополнится в будущем) RegexDOS (Denial of Service)
-
(дополнится в будущем) Lack of Resources & Rate Limiting
-
JWT authentication bypass via weak signing key
-
+ Дополнительные выявленные уязвимости
-
Раскрытие чувствительной информации о сервере
-
Пароли в БД в открытом виде
-
Начнем все разбирать в деталях по порядку!
SQL injection
Уровень критичности: Критическая
Сначала давайте сформируем понимание, что же из себя представляет сама SQL инъекция!
SQL Injection (SQL-инъекция) – это один из наиболее распространенных типов атак на веб-приложения, который позволяет злоумышленнику вмешиваться в запросы, отправляемые к базе данных. Эта уязвимость возникает, когда приложение не корректно обрабатывает входные данные, позволяя пользователю вставлять произвольные SQL-коды в запросы.
Вместо ожидаемого значения (например, имени пользователя) злоумышленник вводит специальный SQL-код. Например:
' OR '1'='1
Если приложение не экранирует или не валидирует входные данные, введенное значение может быть вставлено в SQL-запрос. В результате запрос может выглядеть так:
SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1';
Этот запрос всегда будет возвращать данные, так как условие '1'='1'
всегда истинно. OR накладывает условие, при котором обязательное значение password заменяется на true ('1'='1'
), из-за чего в результате пароль у нас будет истиным, и мы получим доступ к аккаунту admin.
Желтое выделение – кавычки от оригинальной SQL структуры.
Бордовое выделение – кавычки, которые поменяли свое значение и положение из-за вредоносного ввода.
Красное выделение маркером – сам вредоносный ввод ' OR '1'='1
.
На основании этой инфорации давайте попробуем проэксплуатировать SQLi в веб-приложении VAmPI.
Первым делом попробуем запустить автоматический инструмент по поиску SQL инъекций – SQLmap (https://github.com/sqlmapproject/sqlmap), чтобы не тратить время на ручной поиск. Стараемся сократить время и автоматизировать процессы!
sqlmap -u http://ip:5001/users/v1/test --dbs
test
– зарегистрированный пользователь (вставляйте username любого существующего)
-dbs
– перечисление баз данных.
Как мы видим, по какой-то причине кавычка после username приводит работу веб-приложения к ошибке. Давайте вместе выясним почему.
Для этого можем ввести URL с кавычкой в поиск браузера или перехватить запрос в Burp Suite (сделаем так).
Перехватываем запрос и отправляем его в Repeater.
Видим, что сервер возвращает 500-ую ошибку, что очень плохо. Разберем подробнее.
На основе этой информации мы можем понять причину ошибки: нарушается структура SQL-запроса!
Что нам это дает:
-
500-ая ошибка может нагружать сервер.
-
Получаем структуру SQL-запроса (узнали название таблицы – users).
-
Выводятся чувствительные данные при 500-ой ошибке сервера (версия Merkzeug и Python), а также SECRET – вернемся к этому в конце статьи.
Также вы могли заметить, что SQLmap предлагает ввести опцию --table
Связано это с тем, что SQLite – файловая база данных, которая хранит всю свою структуру и данные в одном файле на диске. Соответственно, нам остается лишь получить все таблицы БД SQLite.
sqlmap -u http://ip:5001/users/v1/test --tables
В итоге узнаем про еще одну таблицу – books. И название самой БД.
Теперь мы можем получить содержимое этих таблиц.
Попробуем вывести дамп этих таблиц (их содержимое) с помощью опции --dump
.
Подытожим нашу разведку:
-
Узнали структуру SQL-запроса.
-
Узнали название СУБД – SQLite.
-
Узнали название БД – SQLite_masterdb.
-
Узнали названия двух таблиц в БД: books, users.
-
Получили содержимое таблицы users и books.
Какие же есть способы защитить веб-приложение?
-
Использовать параметризованные запросы (Prepared Statements), чтобы изолировать пользовательский ввод от SQL-структуры.
-
Реализовать достойную валидацию вводимых данных как на стороне клиента, так и сервера.
-
Следует убедиться, что учетная запись БД, используюемая веб-приложением, имеет минимально необходимые привилегии. Это поможет ограничить потенциальный ущерб в случае успешной атаки.
Unauthorized password change
Уровень критичности: Критическая
Unauthorized password change (неавторизированная смена пароля) нам позволяет изменять пароли других пользователей без должной авторизации/валидации на стороне сервера. Думаю, тут на словах и так все понятно, следует разобрать практику.
Переходим к эксплуатации данной уязвимости в VAmPI.
Первым делом нам нужно создать новый аккаунт или же авторизоваться уже под каким-то существующим пользователем. От его имени будем пытаться сменить пароль другого юзера.
Включаем Burp Suite в режим перехвата запросов и переходим по следующему адресу: http://ip:5002/users/v1/пользователь-жертва/password
Пользователь-жертва – вставляем username того пользователя, пароль которого хотим изменить.
После чего данный запрос отправляем в Repeater и начинаем разбор.
На скриншоте пользователь-жертва – test. В JSON формате мы отправляем ключ password
со значением testnew
на сервер, при этом не забыв подставить Authorization: Bearer …
(если сам не вставился) своего аккаунта. Сервер, видимо, надеется, что все пользователи будут честными и менять пароли лишь на свои аккаунты, ведь после отправки такого запроса с новым паролем мы сменим учетные данные другого пользователя!
Способы обезопасить веб-приложение:
-
Необходимо убедиться, что пользователь аутентифицирован и имеет право изменять пароль исключительно своего законного аккаунта. Реализовать механизмы проверки прав доступа.
-
Перед изменением пароля требуйте подтверждения текущего пароля. Дополнительной мерой безопасности (в том числе, если пользователь забудет свой прошлый пароль) является отправка кода подтверждения на зарегистрированный адрес электронной почты или телефон.
Broken Object Level Authorization
Уровень критичности: Критическая
Broken Object Level Authorization (Нарушенная авторизация на уровне объектов) – с помощью данной уязвимости злоумышленник может легко раскрыть персональные данные пользователей, что может привести к их потере или манипуляции. При определенных обстоятельствах несанкционированный доступ к объектам может привести к полному захвату учетной записи.
В контексте информационных систем “объект” – любой ресурс или элемент, с которым взаимодействует система. Например:
-
Файл
-
Запись в базе данных
-
Сервис
-
Страница на сайте
Когда говорят о механизмах авторизации на уровне объектов, имеется в виду проверка прав доступа к конкретным ресурсам. Например, пользователю может быть разрешено редактировать свой собственный профиль, но не разрешено изменять профили других пользователей.
Для начала следует разобраться, что из себя представляет эта уязвимость BOLA (сокращ.)
Авторизация на уровне объекта — это механизм контроля доступа, который обычно реализуется на уровне кода для подтверждения того, что пользователь может получить доступ только к тем объектам, на которые у него должны быть разрешения.
Каждая конечная точка API, получающая идентификатор объекта и выполняющая какие-либо действия над ним, должна реализовывать проверки авторизации на объектном уровне. Проверки должны подтверждать, что вошедший в систему пользователь имеет право на выполнение запрашиваемого действия над запрошенным объектом. В противном случае злоумышленники могут получить доступ к объектам, к которым они не должны иметь доступа, что может привести к утечке конфиденциальной информации и серьезным последствиям для безопасности системы.
Вывод: BOLA происходит в тех случаях, когда механизмы авторизации не обеспечивают достаточную проверку прав доступа на уровне конкретных объектов. Это может случиться, если система полагается на ненадежные источники данных, такие как идентификаторы, передаваемые в запросах, или если проверки авторизации реализованы не последовательно и недостаточно строго.
Переходим к практике!
Для самой эксплуатации BOLA достаточно перейти на http://ip:5002/users/v1/_debug, после чего мы увидим вывод всех пользователей с их правами (admin false/true), почтами, паролями и никнеймами.
Сервер не проверяет права пользователей, которые пытаются просмотреть эндпоинт /users/v1/_debug
.
Рекомендации по устранению уязвимости:
-
Надлежащим образом внедрить механизм авторизации, основывающийся на иерархии и политиках пользователей.
-
Использовать механизм авторизации для проверки того, что текущий аутентифицированный пользователь имеет право доступа к запрошенному действию над записью в базе данных в каждой функции, использующей пользовательский ввод для доступа к записи.
-
Использовать случайно сгенерированные значения, например GUID в качестве идентификаторов записей в базе данных.
-
Использовать тесты, проверяющие корректность работы механизма авторизации. Не пропускать уязвимые изменения, которые не проходят тесты.
Mass Assignment
Уровень критичности: Критическая
Mass Assignment (Массовое назначение) – это уязвимость, которая возникает, когда веб-приложение позволяет пользователю отправлять данные на сервер в виде массивов или объектов, без должной проверки и фильтрации. Это может привести к тому, что злоумышленник сможет изменить значения полей, которые не должны быть доступны для редактирования, например, изменяя роль пользователя или другие критически важные параметры.
Принцип чем-то схож с позапрошлым кейсом (Broken Object Level Authorization), который мы только что разбирали, так что не будем зацикливаться.
Сразу разберем на практике!
Наша цель: создать нового пользователя с правами администратора. Логично то, что у нас таких прав быть не должно, но мы же находчивые. Попытаемся воспользоваться этой уязвимостью.
Переходим в форму регистрации нового аккаунта, вписываем никнейм и пароль. Перед отправкой формы включаем Burp Suite в режим перехвата запросов, только после этого отправляем форму.
Получаем в JSON-формате запроса следующее:
{
“email”: “email@email.com”,
“password”: “password”,
“username”: “newUser”
}
Очевидно то, что именно такой формат запроса сервер ожидает получить. Но он никак не будет ожидать то, если мы попробуем добавить еще один ключ admin
со значением true
. Давайте попробуем и посмотрим результат в /users/v1/_debug
.
После успешной авторизации посмотрим /users/v1/_debug
, чтобы убедиться в том, что у нас новый пользователь с админ-правами.
Поздравляю, теперь у нас есть пользователь с админ-правами! Это на реальной практике безопаснее проворачивать, т.к. мы не отправляем в систему многочисленные запросы, например на перебор пароля у действующего админ-аккаунта. К тому же это наш личный аккаунт и к нему никто не будет иметь доступ.
Подытожим: отсутствие должных валидаций на сервере привели нас к возможности регистрировать новые аккаунты сразу с админ-правами.
Как же избавиться от этой уязвимости?
-
Использовать белые списки (whitelist) для указания только тех полей, которые могут быть массово переназначены.
-
Всегда следует проверять и валидировать входные данные перед их обработкой.
-
Использовать встроенный функционал по добавлению в черный список свойств, к которым клиенты не могут иметь доступ.
-
Если возможно, избегайте использования функций, которые автоматически присваивают переменным и внутренним объектам соответствующие значения из пользовательского ввода.
Excessive Data Exposure through debug endpoint
Уровень критичности: Критическая
Excessive Data Exposure through debug endpoint (Чрезмерное количество данных, передаваемых через конечную точку) - это уязвимость, которая возникает, когда веб-приложение предоставляет доступ к отладочным или диагностическим данным без надлежащей аутентификации и авторизации. Такие данные могут включать конфиденциальную информацию, такую как учетные данные, внутренние структуры данных или детали конфигурации системы.
Приступаем к пентесту в VAmPI.
В этом веб-приложении реализована система с книгами. Если мы создадим свою книгу и посмотрим ее содержание через конечную точку /books/v1/книга, то получим следующее содержание:
{
“book_title”:”…”,
“owner”:”…”,
“secret”:”…”,
}
Первые два ключа (book_title, owner) нас не интересуют, ведь это явно открытая информация. Наш глаз падает на secret – какой-то секрет, который публично демонстрироваться не должен. Злоумышленник бы захотел раскрыть secret других пользователей для своих темных дел. Давайте попробуем в роли атакующего достать secret другого пользователя!
Для начала авторизовываемся в любой из аккаунтов (/users/v1/login
) или создаем новый (/users/v1/register
), после чего создаем свою книгу (/books/v1
).
Включаем Burp Suite в режим перехвата запросов и переходим на конечную точку /books/v1/
книга (подставляем название своей книжки). Видим вывод своей книги – вывод информации работает. Теперь нам нужно получить secret другого пользователя, например Администратора.
Для этого нам достаточно в путь эндпоинта прописать название книги админа (/books/v1/bookTitle28
). Не забудьте подставить Authorization: Bearer, если он автоматически не вставляется.
Вот мы и получили секрет книжки админа. Что нам это дает? Как минимум нарушенная конфиденциальность, практического применения этому в данном веб-приложении не нашел.
Рекомендации по устранению данной уязвимости:
-
Требуется реализовать строгую проверку прав доступа для всех запросов. Убедитесь, что пользователи могут получать доступ только к своим данным.
-
В запросе необходимо предоставлять лишь нужную информацию без конфиденциальных данных. Передачу секрета от книги можно реализовать подтверждением пароля или кодом из смс (для этого сделать отдельную систему).
User and Password Enumeration
Уровень критичности: Средняя
User and Password Enumeration (Перечисление пользователей и паролей) — уязвимость, которую легко пофиксить, но которая также может нанести достаточно вреда. В частности, упростит злоумышленнику последующую задачу — brute force. Перечислением пользователей и паролей злоумышленник может убедиться, а точно ли существует такой пользователь (сервер может выдавать сообщение «Такого пользователя нет» или пароль «Введен неверный пароль от данного аккаунта»). Верным решением будет выводить нейтральное сообщение «Введен неверный логин или пароль» в любых случаях: существует данный пользователь или нет.
Рассмотрим VAmPI на наличие данной уязвимости.
Заходим на эндпоинт /users/v1/login
, в форму вводим любой логин и пароль. Включаем Burp Suite в режим перехвата запросов и отправляем форму. Если нам не повезло, выведется следующее:
Ответ нам говорит, что данного пользователя нет. Мы в роли атакующего можем сразу понять, что нам получится перечислять существующих пользователей и несуществующих.
Попробуем ввести валидный никнейм, но неверный пароль. Выдаст такой ответ:
«Неверный пароль для данного юзернейма». Теперь мы понимаем, что логин верный (а значит такой пользователь существует), но неверный пароль. Это нам уже дает намек на то, что можно попробовать запустить перебор пароля (brute force).
В итоге можно сказать, что перечисление пользователей/паролей нам дает возможность определить, а существует ли данный пользователь? Стоит ли на него тратить время при проведении brute force атаки? Если не брать в счет цель входа в чей-либо аккаунт, список валидных юзернеймов позволит злоумышленнику, например, рассылать вредоносные сообщения в личные сообщения пользователям в каком-то мессенджере.
Рекомендации по нейтрализации уязвимости:
-
Не сообщать пользователям, что введенный логин и пароль является действительным. Следует выводить нейтральные сообщения в обоих случаях: существует пользователь или нет.
-
Использовать капчу для проверки на робота. Например, reCAPTCHA от Google.
-
Реализовать лимит запросов за определенное время.
-
Запрет на регистрацию с не криптостойким паролем. Система перед регистрацией пользователя должна удостовериться, что он ввел безопасный пароль, включающий в себя различный регистр букв, знаки, цифры в хаотичном порядке. Также ввести запрет на регистрацию аккаунта с паролем, который находится в популярных списках утекших паролей. Это поможет от перечисления пароля.
JWT authentication bypass via week signing key
Уровень критичности: Критическая
JWT authentication bypass via weak signing key (Обход аутентификации JWT через слабый ключ подписи) – это очень серьезная уязвимость, которая ставит под угрозу все существование конфиденциальности в веб-приложении. Злоумышленник может перебрать слабый ключ подписи для создания новых JWT-токенов в плохих намерениях: обходить аутентификацию в любые аккаунты, подделывать запросы и так далее.
Сначала разберемся, что такое JWT и зачем он нам вообще нужен.
JSON Web Token (JWT) – это стандартный формат для безопасной передачи информации между сторонами в виде JSON-объекта. Он часто используется в аутентификации и авторизации пользователей в веб-приложениях.
JWT состоит из трех частей: заголовка, полезной нагрузки (payload) и подписи.
1) Заголовок Header
Заголовок обычно состоит из двух частей:
• Тип токена: в большинстве случаев это будет "JWT".
• Алгоритм подписи: это алгоритм, который используется для подписи токена. Наиболее распространенные алгоритмы включают HMAC SHA256 (например, HS256) и RSA (например, RS256).
Заголовок затем кодируется в Base64Url, чтобы получить первую часть JWT.
2) Полезная нагрузка (Payload)
Полезная нагрузка содержит утверждения (claims). Утверждения – это информация, которую вы хотите передать. Они могут быть стандартными или пользовательскими.
Стандартные утверждения – это предопределенные ключи, которые имеют особое значение. В него могут входить:
• iss
(issuer) – кто выдал токен.
• sub
(subject) – субъект токена (обычно это идентификатор пользователя).
• aud
(audience) – для кого предназначен токен.
• exp
(expiration time) – время истечения срока действия токена.
• nbf
(not before) – время, когда токен начинает действовать.
• iat
(issued at) – время, когда токен был выдан.
Пользовательские утверждения – это любые другие данные, которые вы хотите включить. Например, имя пользователя, роль и т.д.
Полезная нагрузка также кодируется в Base64Url, чтобы получить вторую часть JWT.
3) Подпись (Signature)
Чтобы создать подпись, необходимо взять закодированный заголовок и полезную нагрузку, объединить их с секретным ключом и применить указанный в заголовке алгоритм.
Подпись позволяет получателю проверить, что отправитель токена является тем, за кого он себя выдает, и что сообщение не было изменено.
Итоговая структура JWT выглядит следующим образом (с алгоритмом HS256):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Такую запись вы уже встречали в Authorization: Bearer …
, это как раз и был JWT-токен.
Проблема безопасности возникает в том (в случае с HS256), что если ключ подписи (которым был подписан токен) попадет в чужие руки, то злоумышленник сможет подписывать любые токены, и они будут валидны для сервера.
Попробуем на практике
Для выполнения практики воспользуемся hashcat (инструмент для brute force всякой всячины). Но для начала авторизуемся в любой аккаунт или создадим новый, после чего перехватываем запрос и ловим JWT-токен в Authorization: Bearer …
. Копируем его и куда-нибудь сохраняем, дабы не потерять.
hashcat -a 0 -m 16500 <jwt> <wordlist>
-a 0
– режим атаки (перебор по словарю).
-m 16500
– тип хэша (в данном случае JWT).
<jwt>
– вставляем скопированный JWT-токен.
<wordlist>
– подключаем словарь, по которому будет перебираться токен. Я использовал rockyou.txt.
После запуска утилиты и недлительного ожидания получаем ключ подписи: random
Теперь нам поможет инструмент https://jwt.io. Переходим на сайт.
В Verify signature вставляем наш найденный ключ подписи random.
Теперь мы можем осуществлять вход bypass в любой аккаунт! Достаточно изменять значение “sub”:”username”
. Попробуем name1
(в моем случае) заменить на admin
. Теперь если вставить новый токен в какой-нибудь запрос, мы будем выполнять действия от имени администратора.
Если тебе интересно автоматизировать эксплуатацию, можешь самостоятельно изучить инструмент JWT_Tool. Никакой магии, только автоматизация!
Рекомендации по устранению уязвимости:
-
Требуется использовать криптостойкий ключ для подписи JWT: секрет должен быть длинным с хаотичным набором символов, цифр, букв различного регистра. Ключ не должен быть предсказуемым.
-
Всегда проверять подпись JWT на стороне сервера перед тем, как использовать данные из токена, это поможет предотвратить подмену токенов.
-
Периодически обновлять ключи подписи.
-
Рассмотреть возможность использования решения облачных провайдеров для управления секретами (в данном случае ключом подписи).
Раскрытие чувствительной информации о сервере
Уровень критичности: Средняя
Раскрытие чувствительной информации о сервере – с помощью данной уязвимости злоумышленник может получить больше чувствительной информации о сервере (версии Python и Werkzeug), которая может подлежать различным атакам из-за устаревшей версии или новых 0-day уязвимостей.
Если вы заметили, в каждом ответе от сервера мы получали версию Python и Werkzeug. Эта лакомая информация для злоумышленников, поскольку с ней у них может увеличиться вектор атак на веб-приложение (или сразу же на сервер).
Куда хуже обстоит ситуация, если мы путем манипуляций получим 500-ую ошибку. Там же нам еще выводится некий SECRET:
Мне не удалось выяснить, зачем этот секрет, но явно он не просто так называется.
Можем сделать вывод, что сервер в ответах выводит чувствительную информацию, а при 500-ой ошибке – конфиденциальную. Плюс 500-ые ошибки могут нагружать сервер.
Способы перекрыть уязвимость:
-
Настроить сервер таким образом, чтобы он не отображал сообщения, содержащие чувствительную информацию о сервере.
И последняя уязвимость, которую мы рассмотрим:
Пароли в БД в открытом виде
Уровень критичности: Критическая
Пароли в БД в открытом виде – критическая уязвимость, которая ставит под угрозу безопасность пользовательских данных в веб-приложении. Когда пароли хранятся в открытом виде, злоумышленник, получивший доступ к базе данных, может легко извлечь и использовать эти пароли для несанкционированного доступа к аккаунтам пользователей.
Хранение паролей в открытом виде может привести к серьезным последствиям: утечке конфиденциальной информации, финансовым потерям и сильному повреждению репутации компании.
Кроме того, если пользователи используют одинаковые пароли на разных платформах, компрометация одного аккаунта может привести к взлому других!
В приложении VAmPI можем убедиться о наличии данной уязвимости, перейдя на конечную точку /users/v1/_debug
.
Если бы пароли хранились в хэше, то он бы и выводился из базы данных.
Чтобы избавиться от данной уязвимости, нужно:
-
Использовать надежные алгоритмы хэширования (Argon2, bcrypt и т.п.).
-
Добавлять уникальную и надежную соль к хэшу.
Вот мы и закончили проведение тестирования на проникновение в веб-приложение VAmPI.
Можем подвести финальные итоги:
В VAmPI было найдено большое количество критических уязвимостей, которые ставят существование компании под угрозу. Не мало случаев, когда компании закрывались из-за банкротства после тщательной хакерской атаки.
Бизнес-риски:
-
Утечка персональных данных.
-
Потеря репутации.
-
Огромные штрафы.
-
Несанкционированный доступ к системе.
Помните, что для безопасного функционирования с веб-приложением, оно должно содержать без нарушений следующее:
-
Конфиденциальность
-
Доступность
-
Целостность
Без данных пунктов (или одного из) приложение нельзя считать безопасным решением, а компания подвержена различным бизнес-рискам, в зависимости от уязвимости и ущерба от хакерской атаки.
Дополнительный материал для самостоятельного изучения:
-
Какие еще есть виды SQL инъекций? Изучить sqlmap.
-
Подробно разобрать OWASP top 10 API (2019, 2023).
-
Какие еще есть алгоритмы подписи JWT? Как они работают?
-
Как безопасно хранить ключ подписи JWT?
-
Безопасные решения хранения паролей в БД.
Благодарю за прочтение моей первой статьи.
Дальше – больше!
Telegram канал ПК "РАД КОП": https://t.me/radcop_online
С уважением, DAFFIER.
Автор: DAFFIER