Всем привет! Меня зовут Дмитрий Терёшин, в СберМаркете я занимаюсь Application Security — безопасностью веб- и мобильных приложений.
Во время аудитов мобильных приложений я часто натыкался на плавающие уязвимости: они появляются только в конечной сборке, которая отправляется в стор. Я разработал утилиту, которая автоматически их отлавливает. Она бесплатна и свободна для распространения. Вот ссылки на GitHub и DockerHub с кодом и готовым образом:
Тем, кому интересно, как утилита работает, и кто хочет сделать аналогичную, я расскажу о самих уязвимостях и где их искать.
Что такое плавающие уязвимости и зачем нужна утилита для поиска
Плавающие уязвимости — те, которых в коде приложения может не быть, но в итоговой сборке они иногда появляются. Я насчитал 5 типов:
Некорректная сетевая конфигурация.
Небезопасные URL.
Скомпрометированные токены, ключи, пароли и креды.
Небезопасные настройки WebView.
Небезопасные настройки из public-компонентов.
Мне хотелось автоматизировать отлов плавающих уязвимостей, но ни одно из Open source tools-решений мне не подошло:
mobsfscan — статический анализатор для телефонов. Он проверяет исходники системным анализом. Итоговый дистрибутив он проверять не умеет.
MobSF — фреймворк для проверки мобильных приложений. Он много чего умеет, но плохо встраивается в pipeline. Выдает много мусора, аналитической информации вроде «вот я нашел здесь кучу строк». И непонятно, что это всё значит.
ApkUrlGrep — утилита, которая грепает в сборе только URL. Анализирует только Android-приложения, работать с iOS не умеет.
apk_api_key_extractor — утилита, которая грепает API-ключи регулярными выражениями. Тоже работает только с Android-приложениями.
Поэтому я создал свою утилиту — CheckKarlMarx. Это скрипт на Python, завернутый в Docker. Архив с итоговой сборкой приложения распаковывается, и в декомпилированных исходниках ищутся выражения, характерные для плавающих уязвимостей. В результате проверки получаем отчёт с уязвимостями.
Как работает утилита CheckKarlMarx
Для запуска достаточно спулить образ с DockerHub и примонтировать папку со сборкой. Утилита отдаёт отчёт в HTML или в sarif-формате.
Формат sarif — унифицированный формат для статических анализаторов. Sarif-отчёт можно отправлять в vulnerability-management-систему, например в DefectDojo.
Когда мы встраивали утилиту в pipeline, у нас использовался TeamCity для CI. Отчёт HTML мы вставляли в отдельную вкладку с отчётами от других линтеров прямо в TeamCity.
В рамке отчёта основная информация о сборке. Для Android проверяются сетевые мисконфиги, URL, ключи, токены, компоненты и WebView. По каждому из разделов указаны уязвимости и детали по ним. Для iOS-проверок меньше: network, URL и ключи.
Можно выводить результат в файл или консоль и кастомизировать отчёт. Есть коды выхода: когда не нашлось ни одной уязвимости, возвращается 0, когда есть уязвимость — 1.
В текущей конфигурации exit code = 1, только когда нашлись уязвимости уровня major и normal. Например, если ключ утёк в сборку или сетевая конфигурация некорректна.
Если хочется проверять плавающие уязвимости автоматически, то можно использовать мою утилиту. Дальше я хочу подробнее рассказать об уязвимостях, которые она ловит. Это пригодится тем, кто хочет понять, как работает мой инструмент, или написать свой.
Уязвимость: некорректная сетевая конфигурация
Это неправильные настройки сети, которые открывают некоторые возможные вектора атак. Выставить неправильные настройки можно и в Android, и в iOS. Расскажу о некоторых из множества способов.
В Android-приложениях есть файл AndroidManifest.xml. Он описывает главные свойства, компоненты приложения и различные опции.
В теге application этого файла можно подключить опцию usesCleartextTraffic = ”true”. Она разрешает незашифрованный HTTP-трафик на уровне всего приложения. Такой трафик небезопасен.
Можно подключить Network Security Config — xml-файл, который лежит по пути res/values/network_security_config.xml. В нём прописывают конфигурации приложения и сети. Там тоже можно разрешить незашифрованный трафик на уровне всего приложения или для каких-то конкретных доменов.
Через конфиг можно разрешить доверие к пользовательским сертификатам. Если пользователь нечаянно установит вредоносный сертификат с помощью фишинга, то злоумышленник сможет провести MITM-атаку. Но для этого надо включить доверие к пользовательским сертификатам в настройках Android.
Android собирает все файлы манифестов из сторонних или из собственных core-библиотек и соединяет в один. Поэтому, если в сторонней библиотеке есть конфиг со включенным Network Security, он же появится в смёрженном файле. Поэтому настройка может попасть в релизную сборку, даже если её нет в исходниках.
На iOS доверие к сертификатам и незашифрованный трафик включаюется настройкой NS App в файле info.plist. Её можно включить только для конкретных доменов. Например, для example.com — разрешить соединение по небезопасной версии TLS 1.1.
С iOS-приложением был похожий случай: в исходниках всё было нормально, но Info.plist подменялся на CI. Из-за этого небезопасный мисконфиг вываливался в релизную сборку.
Уязвимость: небезопасные URL
Если пользователь будет ходить по незащищённым ссылкам, злоумышленник сможет перехватить трафик или же вытянуть данные для авторизации из самой ссылки. Уязвимость работает и в Android-, и в iOS-приложениях.
URL по небезопасному протоколу
Примеры:
Сама уязвимость достаточно минорная, но небольшой риск она всё же даёт. Например, можно подменить картинку, и пользователь получит информацию со сторонней рекламой или ссылками, на которые он перейдёт. Так возникнет простор для фишинга.
В моей практике случилась такая история — по небезопасному протоколу HTTP приложение получало статику: картинки, PDF с соглашениями и с информацией о тарифах.
Небезопасных URL не было в репозитории и в исходниках приложения, но они появились в релизной сборке. Оказалось, что конфиги со ссылками подтягивались из стороннего сервиса, который наполнялся не разработчиками, а аналитиками.
Тестовые URL
Пример: https://example-qa.com.
На тестовые стенды любят заливать продовые данные. И название у теста обычно как у прода, но с суффиксом qa, qa2, dev, stage, test, uat и так далее. Если злоумышленник найдёт тестовый урл в конечной сборке и сможет авторизоваться в базе, то получит данные аналогичные продовым. Лучше такого не допускать.
URL с Basic-кредами
Пример: https://username:password@example.com.
Если URL имеет формат «юзернейм: пароль @ хост», значит, на хосте есть Basic-авторизация. Когда креды передают в URL и он попадает в сборку, креды компрометируются. Любой желающий получит доступ к хосту сайта, авторизовавшись через Basic-креды, например, под админским пользователем.
Уязвимость: скомпрометированные токены, ключи, пароли и креды
Утекают и в Andriod, и в iOS. Оставлять их в коде категорически нельзя. Иначе кто захочет — возьмёт ключ и украдёт три магнитофона, три кинокамеры заграничных данные или получит доступ к функционалу приложения.
Basic Auth и Bearer-токены |
Basic — YmlsbHk6c2VjcmV0cGFzc3dvcmQ= Bearer — <token> |
API-ключи для доступа к тестовым стендам |
Auth-Token: YWQG5pyYPq… |
Закрытые ключи |
-----BEGIN PRIVATE KEY-----\nprivate-key\n-----END PRIVATE KEY-----\n" |
Legacy FCM Server Key |
AAAA[A-Za-z0-9_-]{7}:[A-Za-z0-9_-]{140} |
Google API-ключ |
AIzaSy[0-9A-Za-z_-]{33} |
Уязвимость: небезопасные настройки WebView
Мини-браузер WebView встраивается в приложения. Моя утилита проверяет мисконфиги WebView только для Android, потому что в iOS их не так много и сложнее сделать их небезопасными. Вот какие мисконфиги проверяются:
EnableSafeBrowsing — настройка в AndroidManifest. Если выставить ей false, то пользователь не узнает о том, что перешёл по небезопасной ссылке.
SetJavaScriptEnabled — включение JavaScript. С включенной настройкой злоумышленник может в скрипте использовать вредоносный код.
SetAllowAccess — доступ к файлам в песочнице приложения. Если ему присвоить true или оставить по дефолту, доступ останется. В файлах песочницы могут быть данные авторизации, и злоумышленник сможет подключить сторонний сайт к приложению.
ShouldOverrideURLLoading — в значении true позволяет пользователю переходить по всем ссылкам на странице, загруженной в WebView. Внутри WebView пользователь ходит по ссылкам и открывает другие страницы, например фишинговые.
Уязвимость: небезопасные настройки из public-компонентов
Это экспортированные activity, ресиверы, сервисы и провайдеры, которые обеспечивают межпроцессное взаимодействие приложений на устройстве. В моём инструменте я проверяю их только для Android. При сборке происходит мержинг манифестов, и компоненты могут подтягиваться из других библиотек.
Экспортированные компоненты из других приложений, которые не защищены разрешениями, открыты для взаимодействия всему миру. Потенциально это несёт проблемы. Допустим, можно отправить в Broadcast Receiver неожиданные параметры из другого приложения и он упадет с ошибкой. Приложение аварийно прекратит работу.
Получится локальный DOS: одно приложение будет держать в дауне другое. Пользователь не сможет воспользоваться приложением, когда оно в дауне.
Итог
CheckKarlMarx можно использовать также в составе опенсорс-пайплайнов от компании Whitespots для автоматизации сканирования уязвимостей.
Если у вас есть опыт автоматизации сканирования и встраивания его в pipeline, буду рад о нём узнать. Поделитесь им и своими проектами в комментариях.