Когда 2+2=5: чем страшны ошибки бизнес-логики приложений и почему их легко не заметить при разработке

М/Ф М/Ф «В стране невыученных уроков»

Мы как-то писали про SSRF-атаку, которая входит в список наиболее распространенных уязвимостей OWASP Top 10. Однако мир уязвимостей намного разнообразнее и, конечно же, не ограничивается этим списком. Сегодня мы хотим рассказать про уязвимости, связанные с бизнес-логикой. Что в них необычного? Это как доказать, что 2+2=5. Последовательность действий кажется правильной, все операции разрешенными, а результат совсем не тот, который закладывался при разработке. Но мы же знаем, что в доказательстве есть ошибки! Рассмотрим, как подобные задачки решаются при анализе защищенности и какие неожиданные результаты можно получить, используя обычную функциональность приложений.

Бизнес-логика и какие уязвимости в ней могут быть

Для начала немного теории. Бизнес-логика обеспечивает выполнение задач, которые закладывались при проектировании приложения, то есть определяет его поведение при всех возможных сценариях использования. В свою очередь, уязвимости бизнес-логики связаны с недостатками в архитектуре или ошибками, допущенными при реализации логики приложения. Это может в итоге привести к взаимодействию с приложением по непредусмотренному разработчиками сценарию.

Вот простой пример. Для защиты от атак подбора пароля в приложении реализован механизм CAPTCHA. При работе через веб-интерфейс после трех неудачных попыток ввода требуется решить типичную задачу с картинками. Однако при прямой отправке запросов на сервер никаких ограничений не предусмотрено. То есть фактически CAPTCHA никак не защищает от атак подбора. Как так получилось, нам остается только догадываться. Возможно, разработчики решили, что никто не будет напрямую обращаются к API. А зря!

50100c21b875b7e8cad693e50cb8cb33.png

Уязвимости бизнес-логики отличаются от большинства других уязвимостей:

  • они разнообразны, можно сказать, уникальны для каждого приложения. Это связано с особенностями работы и направленностью каждого отдельного приложения;

  • их трудно обнаружить автоматизированными сканерами. Отсутствие общих схем классификации и способов обнаружения, а также необходимость понимания предметной области делает уязвимости бизнес-логики недоступной целью для автоматизированных сканеров. Например, заметив в корзине покупок товар с отрицательной стоимостью, человек понимает: что-то пошло не так. Но для сканера — это просто числа;

  • они предполагают манипуляции легитимной функциональностью приложений. В примере выше, для обхода CAPTCHA не было использовано никаких дополнительных инструментов и действий, а только запрос, который использует само приложение.

Нет единой причины появления ошибок бизнес-логики, как нет и единого шаблона их обнаружения. Это и делает поиск подобных уязвимостей сложным и интересным одновременно.

af3d6b5c9a914d463524a6ec296c1028.png

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

Теперь, понимая, что это за уязвимости бизнес-логики и в чем их особенности, перейдем к обещанным «задачкам».

Не простой перебор идентификаторов

Обычное веб-приложение, проходим аутентификацию, открываем профиль пользователя: имя, замаскированное поле пароля, изображение, инфо о себе и т.д. С первого взгляда все выглядит неплохо, но… Оказалось, в этом приложении можно собрать пароли всех пользователей, да еще и необязательно аутентификацию проходить!

Посмотрим на запросы, которые отправляются на сервер при обычной работе приложения:

Запрос для получения информации о пользователеЗапрос для получения информации о пользователе

Обратим внимание на параметр id и поле password, которое содержит пароль пользователя (!). Пробуем заменить идентификатор на любое другое число — в ответе получаем данные другого пользователя. Осталось дело за малым, автоматизируем перебор идентификаторов и собираем список пользователей и их паролей.

Представленный пример содержит сразу две уязвимости:

  • небезопасное управление паролями — их хранение в открытом виде и передача пользователям;

  • небезопасные прямые ссылки на объекты — обращение к объектам на основе полученного от пользователя идентификатора.

Управление паролями — это отдельная тема, которой мы не будем касаться в рамках текущей статьи. А вот на небезопасных прямых ссылках на объекты остановимся подробно. Безусловно, подобные уязвимости также относятся к недостаткам контроля доступа (OWASP Top 10, A01:2021 — Broken Access Control) и могут быть расценены как пропущенные проверки прав. Однако посмотрим на проблему именно со стороны бизнес-логики.

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

Сценарий работы приложения при обращении неаутентифицированного пользователяСценарий работы приложения при обращении неаутентифицированного пользователяСценарий работы приложения при обращении пользователя с активной сессиейСценарий работы приложения при обращении пользователя с активной сессией

И вот уже появляется ошибка. При запросе данных (/api/person? id=22) не проверяются ни сессия, ни права пользователя. Поэтому представленный в начале запрос на получение паролей успешно реализуется. При этом изначальные требования о том, что неаутентифицированный пользователь не может просмотреть страницу профиля — выполняется.

Как так получилось? При проработке сценариев использования не было учтено возможное прямое обращение к API. Конечно, обычно мы работаем с приложением через браузер и взаимодействуем с визуальными элементами, и одной проверки сессии в этом случае достаточно. И это одна из самых распространенных ошибок бизнес-логики — неверные предположение о действиях пользователей. Чтобы ее исключить, необходимо проверять права при каждом обращении к API. А это, согласитесь, уже не самая простая задача.

Неучтенный сценарий прямого обращения к APIНеучтенный сценарий прямого обращения к API

Небезопасные прямые ссылки на объекты одна из наиболее распространенных уязвимостей в веб-приложениях на протяжении последних лет. В проектах по анализу защищенности мы сталкиваемся с разными ее проявлениями:  чтение данных, удаление и копирование объектов и многое другое. При этом подобные уязвимости редко встречаются в единичных экземплярах.

Как мы уже говорили выше, такие ошибки сложно обнаружить автоматизированными сканерами. Правда, технологии не стоят на месте, и сканеры уже умеют обнаруживать подобные уязвимости, хоть и с большим уровнем ошибок первого и второго рода. Поэтому пока лучший способ обнаружить небезопасные прямые ссылки на объекты — ручной анализ. При этом стоит помнить, что идентификаторы — это не только инкрементальные числа. Название файлов, методов, уникальные идентификаторы (GUID) — небезопасные прямые ссылки на объекты могут быть везде. Поэтому необходимо анализировать всю функциональность приложения и проверять полученные результаты по установленной матрице доступа.

Доверяй, но проверяй

Неверные предположения о действиях пользователей также часто становятся причиной избыточного доверия клиентской части приложения. Давайте разберем, почему же клиенты не заслуживают слепого доверия.

Возможно, многие знают хрестоматийный пример изменения цены товара в запросе. Это демонстрация подделки параметров (Parameter Tampering) — достаточно простой атаки, которая также направлена на бизнес-логику приложений. Основная ее идея в том, что разработчики используют скрытые поля на странице или другие зафиксированные параметры как данные из достоверных источников. Стоит отметить, что это могут быть также cookie-файлы, значения заголовков и т.д. То есть все, что может быть изменено при отправке запроса.

Атака Parameter Tampering: изменение параметра price в запросеАтака Parameter Tampering: изменение параметра price в запросе

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

Рассмотрим мобильное приложение с реализованной бонусной программой. Принцип работы такой: пользователи получают бонусы за заказы. Эти бонусы можно потратить в специальном каталоге, который полностью открыт всем. Если бонусов недостаточно, товары отображаются как недоступные.

Запрос информации о пользователе: недостаточное количество бонусов для покупокЗапрос информации о пользователе: недостаточное количество бонусов для покупок

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

Правило в Burp Suite для автоматической замены значения параметра в ответеПравило в Burp Suite для автоматической замены значения параметра в ответеЗапрос информации о пользователе: изменение количества бонусов для покупокЗапрос информации о пользователе: изменение количества бонусов для покупок

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

Все дело в отсутствии проверки баланса бонусов на стороне сервера. Она реализована только на стороне клиента, а на стороне сервера происходило списание, о чем свидетельствовал отрицательный баланс бонусов после покупки.

Описанный выше сценарий покупки товаров за бонусыОписанный выше сценарий покупки товаров за бонусы

Некорректные проверки прав также часто встречаются в одностраничных приложениях (Single Page Application, SPA), в которых в зависимости от уровня привилегий отображается тот или иной интерфейс. В совокупности с ранее описанной уязвимостью (небезопасные прямые ссылки на объекты) это представляет значительную опасность для всего приложения.

Параметр, которому точно нельзя доверятьПараметр, которому точно нельзя доверять

Что еще не стоит доверять клиентам — это аутентификацию в сторонних сервисах. Все взаимодействие следует организовывать на стороне сервера, ведь иначе учетные данные или токены доступа могут оказаться в руках злоумышленников. А это может привести к различным потерям.

Вот так просто раскрываются токены, ключи и учетные данные в js-сценарияхВот так просто раскрываются токены, ключи и учетные данные в js-сценариях

Вокруг да около

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

Как отмечалось выше, логические уязвимости зачастую появляются в сложных приложениях. Например, онлайн-банки, которые сложны и многокомпонентны по своей природе. Интерес у злоумышленников к уязвимостям в таких приложениях подогревается возможностью кражи денег здесь и сейчас. При проведении работ по анализу защищенности мы также ищем ошибки, которые позволяют быстро получить деньги. Одной из наиболее известных уязвимостей подобного типа является ошибка округления сумм денежных средств.

В качестве простого примера рассмотрим перевод денежных средств в другую валюту. Не будем привязываться к какой-либо реальной валюте и сделаем следующее допущение:

1 солярик

=>

100 рублей

1 минисолярик

=>

0.01 солярика или 1 рубль

Итак, попробуем купить 1 солярик меньше, чем за 100 рублей.

fdd4da5e42a3ba2d7f73a43baa7f99c3.png

Проведенная схема — это пример атаки на округление. Действительно, 40 копеек — это 0.004 солярика, что по обычным правилам округления меньше 0.01 солярика, то есть минимальной единицы. А вот 57 копеек — 0.0057 солярика и по обычным правилам округления получаем 0.01 солярика или 1 минисолярик. Подобная атака известна далеко не первый год, и ей все еще подвергаются банковские приложения.

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

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

Работа с числами с плавающей точкой полна сюрпризовРабота с числами с плавающей точкой полна сюрпризов

Нет в наличии

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

Итак, проводим анализ защищенности интернет-магазина. Просмотр каталога, добавление в корзину, оформление заказа и т.д. Внимание привлек параметр со значением количества товаров на складе, возвращаемый в ответе при запросе информации о товаре и совершении различных действий в корзине.

Значение параметра stock_level при запросе информации о товареЗначение параметра stock_level при запросе информации о товареЗначение параметра stock_level после добавления товара в корзинуЗначение параметра stock_level после добавления товара в корзину

Видите разницу в единицу? Попробуем добавить в корзину максимальное количество товара, доступное на складе, и посмотрим, что изменится в каталоге.

Добавление максимального количества товара в корзинуДобавление максимального количества товара в корзину

Тем временем добавленный в корзину товар стал недоступен для оформления и покупки.

Отсутствие добавленного в корзину товараОтсутствие добавленного в корзину товара

Теперь, увидев общую картину, можно сказать, что блокировка товара для покупки происходила на моменте добавления товара в корзину, а не при непосредственном оформлении заказа. В результате, просто откладывая товары в корзину, можно заблокировать их для покупки другими пользователями. И доступным для других пользователей он становился только после того, как его удаляли из корзины. Вот так, ошибка в логике работы интернет-магазина могла привести к потерям со стороны его владельца. К счастью, приложение было протестировано еще до выхода его в продуктивное использование, поэтому негативных последствий как для покупателей, так и для продавцов удалось избежать.

Казнить, нельзя помиловать

Несмотря на то, что уязвимости бизнес-логики не входят в список наиболее распространенных уязвимостей, оставлять без внимания их нельзя. Иногда обнаружив одну подобную уязвимость, ее можно принять за единичную ошибку человека. Но подробный анализ причин ее появления может помочь улучшить процессы разработки и не допускать подобных ошибок в будущем.

Как говорится, проблему легче предотвратить, чем потом исправлять. Поэтому при разработке приложения стоит избегать неявных предположений о действиях пользователях, тщательно прорабатывать все возможные варианты взаимодействия с системой и обращать внимание на предметную область. И, главное — автоматизированные сканеры тут не помощники. Только ручной анализ функциональности, только хардкор!

Автор: Ольга Рыбакова, аналитик отдела анализа защищенности Solar JSOC

© Habrahabr.ru