Использование Xposed для обхода SSL Pinning на Android
Есть различные подходы к анализу защищенности приложений, но рано или поздно все упирается в изучение взаимодействия приложения с API. Именно этот этап дает больше всего информации о работе программы, об используемых функциях и собираемых данных. Но что делать, если приложение защищено SSL Pinning? Давайте посмотрим, что можно сделать в такой ситуации.
Перед началом
SSL Pinning в мобильных приложениях реализуют, внедряя сертификат SSL в саму программу. При открытии защищенного соединения приложение не обращается в хранилище устройства, а использует свой сертификат. Это сразу же устраняет возможность направить трафик на Burp и анализировать его. Ведь для того, чтобы увидеть трафик SSL, нужно внедрить на устройство Burp CA, который бы подтвердил, что созданный Burp сервер валиден и ему можно доверять.
SSL Pinning — это не панацея, очень часто появляются заметки про обход защиты на банковских приложениях или вообще про уязвимости в самом SSL. Но если защита построена правильно, то это создает огромные проблемы для исследователя.
Для работы Xposed нам понадобится рутованный девайс. Для демонстрации атаки возьмем простое мобильное приложение, которое использует одну из самых часто встречающихся сетевых библиотек. В этом приложении отсутствует защита от SSLUnpinning, так как описанная мной атака не пытается атаковать сертификат и сетевое общение, а нацелена на перехват данных до покрытия их SSL. Для демонстрации серверной стороны атаки и бэкенда мобильного приложения используем быстрое решение в виде Python и Flask. Все исходники вы можете найти на GitHub.
Вернемся к проблеме перехвата трафика SSL. С помощью Xposed можно попытаться отключить проверку сертификата, например «затереть» его для программы. Но предположим, атакуемое приложение хорошо защищено, бэкенд проверяет валидность защиты, детектирует попытки перехвата или проксирования трафика. Что делать в таком случае? Сдаться и заняться другим приложением? А что, если перехватить трафик еще до того, как он станет сетевым?
С этого вопроса началось мое исследование.
В статье я буду работать с Android и Xposed, но подобного результата можно добиться с помощью фреймворка Frida, который доступен и на других ОС.
Сбор информации
Для начала попробуем запустить приложение. Видим на экране кнопку SEND и текстовое приглашение ее нажать. Нажимаем — надпись меняется сначала на «Wait…», а после отображается «Sorry, not today». Скорее всего, отправляется запрос, который не проходит проверку на стороне сервера. Давайте посмотрим, что происходит внутри приложения.
Реверс APK
Попробуем отреверсить приложение, чтобы понять, какие библиотеки используются внутри.
Открываем проект в Android Studio и смотрим, что есть в smali. Сразу видим okhttp3.
Я специально использовал OkHttp, так как эта библиотека лежит в основе других библиотек для работы с API, например Retrofit 2.
Найдем класс Main, в данном случае это com.loony.mitmdemo.Demo. В onCreate видим создание OnClickListener. Выше от него, через v1, передается как аргумент Demo$1. Посмотрим, что реализует этот класс.
В конце функции onClick вызывается асинхронная задача, которая носит очевидное имя SendRequest. Перейдем в com/loony/mitmdemo/Demo$SendRequest. Здесь мы видим множество обращений к okhttp3. Значит, мы не ошиблись в предположении.
Здесь важный этап — это определение цели для перехвата. Выгодно выбрав функцию или класс, мы можем получить больше возможностей, чем при перехвате какого-либо другого объекта. Зачем, к примеру, перехватывать экземпляр публичного ключа, если можно перехватить все хранилище?
Посмотрим на стандартное применение okhttp3 в проектах Android.
Наиболее выгодным здесь будет перехват execute. Почему? Эта функция возвращает Response и, очевидно, отправляет Request. Это значит, что, перехватив эту функцию, мы получим возможность изменить Request до отправки и получить Response до возвращения в основную функцию.
Атака
Итак, мы знаем, как реализуется общение, у нас есть представление о том, что мы хотим перехватить и что получить. Кроме того, мы хотим как-то просматривать эти данные и иметь возможность их изменить до отправки или получения. Для реализации этого я написал свой API, но можно было пойти дальше и подключиться к Burp API.
Базовый процесс работы с Xposed и создания модулей уже был описан на множестве ресурсов. Если вам интересно, можете ознакомиться со статьей Xposed Development tutorial.
MultiDex
Прежде чем приступить, я хочу показать интересный трюк — перехват функций в приложении с MultiDex. Не зная о нем, я потратил несколько дней впустую. Дело в том, что приложение сразу не загружает весь код в память, вместо этого он разбивается на файлы dex. Если вы пытаетесь перехватить функцию, которая находится во втором или даже в третьем dex, то ее нужно сначала подгрузить в память. Сделать это можно таким образом.
Мы перехватываем запуск приложения и получаем экземпляр ClassLoader. Дальше, перед тем как поставить хук на функцию, нужно загрузить класс, в котором она находится.
Проверка вектора атаки
Для начала посмотрим, что мы не ошиблись с выводами, и добавим хук, который просто что-то выведет в консоль. Здесь мы перехватываем не Call, а RealCall, так как Call — это interface и у нас нет возможности перехватить его, но мы можем перехватить его наследников. Наследника Call я нашел по полученному исходному коду, после apktool.
При нажатии на кнопку в консоли печатается Yeah — мы на верном пути. Функция execute возвращает Response, поэтому добавим перехват afterHookedMethod. До вызова функции есть возможность перехватить Request, а после вызова функции — Response.
Получение данных
Мы нашли функцию, которая отправляет данные, что дальше? Для начала откроем исходники okhttp3 на GitHub и посмотрим, как выглядит RealCall.java. Видим, что есть возможность получить Request, если из функции execute вызвать код this.request(). Добавим эту строку в beforeHookedMethod.
Здесь param.thisObject — обращение к this класса, в котором находится перехваченная нами функция. Второй параметр — это имя функции, которую мы вызываем, в нашем случае это Request. Дальше следуют аргументы функции, но у нас их нет, так как функция ничего не принимает. Чуть позже я покажу вызов с использованием аргументов.
Итак, мы получили экземпляр объекта Request, и теперь хотелось бы сделать преобразование с Object в экземпляр Request в коде хука.
К сожалению, мы не можем просто преобразовать его в экземпляр объекта, потому что он создан в другой среде и наш okhttp3.Request отличается от того, который есть в приложении. Придется получать данные при помощи вызова функций и чтения значений переменных через Xposed. Открываем Request.java на GitHub и ищем функции и переменные, которые могут содержать интересную для нас информацию: тип запроса, хедер, путь, данные.
В моем случае код не обфусцирован, поэтому я могу использовать названия функций, как на GitHub. В противоположном случае необходимо в коде, прошедшем реверс, найти соответствующую функцию, так как некоторые имена могут отличаться из-за обфускации. Я решил сразу складывать данные в JSONObject, это понадобится дальше. Также я кодирую данные от body в Base64, потому что функция возвращает массив байтов. В таком виде мы не можем корректно выводить информацию в терминал.
Обрати внимание, что в коде есть преобразование Object в String[]. Это можно делать для всех простых типов: String,int,long и других.
Проделаем похожую процедуру с Response.
Теперь у нас есть два JSONObject, которые содержат данные для анализа. Выведем в консоль то, что получилось собрать.
Посмотрим, что содержится в body, распаковав его с помощью Base64.
Минуточку, здесь явно ошибка! Особо внимательные читатели заметят, что имя ресурса написано неправильно. Но как быть, если нам нужно что-то поменять перед отправкой?
Подмена данных
Для подмены я буду отправлять собранные данные на свой API. Можно было бы обойтись без этого, но каждый раз, когда вы вносите изменение в модуль Xposed, вам необходимо перезагружать устройство. Если же написать выполнение команд от C&C-центра, то можно избежать этих проблем.
Добавим отправку собранных данных на наш endpoint. Код отправки использует стандартный POST Request. На сервере данные меняются и отправляются обратно. Теперь нужно их вставить в оригинальный Request.
Теперь попробуем запустить приложение и отправить запрос. В консоли мы видим, что наш запрос поменялся и на экране отображается «Congratulations!».
Итого
Мне эта идея уже не раз помогала найти проблемы и обойти некоторые уровни защиты. Можно ли от нее защититься? Безусловно, да. Можно добавить блокировку приложения или клиента, если был найден Xposed, добавлять подписи к данным, которые отправляются. Можно не доверять любым запросам от клиента, даже если они идут по защищенному соединению.
Тем не менее, имея в своем наборе такой мощный инструмент, как Xposed или Frida, вы легко обойдете все возможные методы защиты на стороне клиента.
Обход SSL Pinning в iOS-приложении
Привет, меня зовут Андрей Батутин, я Senior iOS Developer в DataArt. В предыдущей статье мы говорили, как можно сниффить трафик нашего мобильного приложения с помощью HTTPS-прокси. В этой обсудим, как обходить SSL Pinning. На всякий случай, рекомендую прочитать первую статью, если вы ее еще не читали: это понадобится для понимания приведенного ниже текста.
Собственно, на практике SSL Pinning применяют, чтобы описанный способ инспекции и модификации трафика мобильного приложения не был доступен плохим парням или любопытному шефу.
Что такое SSL Pinning
В предыдущей статье мы установили на мобильное устройство Charles Root Certificate, что позволило нашему Charles Proxy принимать, расшифровывать, показывать нам трафик, зашифровывать его обратно и отправлять на Dropbox.
Если я как разработчик мобильного приложения хочу, чтобы мой трафик мог инспектировать только мой сервер и никто другой, даже если этот другой установил на устройство свой SSL-сертификат, я могу воспользоваться SSL Pinning.
Его суть сводится к тому, что во время SSL-хендшейка клиент проверяет полученный от сервера сертификат.
В этой статье рассматривается самый простой в реализации способ SSL Pinning с помощью разрешенного списка сертификатов, зашитых в приложение (whitelisting).
Больше о типах SSL Pinning можно почитать здесь.
Реализация SSL Pinning в FoodSniffer
Полный код проекта лежит здесь. Вначале нам надо получить два сертификата в формате DER для двух хостов:
Чтобы получить сертификаты в нужном формате, я использовал Mozila Firefox.
Открываем в браузере dropbox.com.
Нажимаем на символ замка в адресной строке.
Затем выбираем Details и находим конечный сертификат в Certificate Hierarchy.
Нажимаем Export и сохраняем в формате DER.
Повторяем ту же процедуру для uc9b17f7c7fce374f5e5efd0a422.dl.dropboxusercontent.com.
Примечание
Для контент-сервера Dropbox (*.dl.dropboxusercontent.com) используется wildcard-сертификат. Значит, сертификат, который вы извлекли для uc9b17f7c7fce374f5e5efd0a422 сервера, будет подходить и для любых других *.dl.dropboxusercontent.com серверов Dropbox.
В результате у меня получилось два файла с сертификатами:
которые я добавил в проект iOS-приложения FoodSniffer.
Затем я добавил extention для FoodListAPIConsumer-класса, в котором и проверяю полученный от сервера сертификат. Для этого я ищу его в списке разрешенных сертификатов, обрабатывая Authentication Challenge-делегат NSURLSessionDelegate-протокола:
В массиве certificates у меня хранятся Data представления моих разрешенных сертификатов.
Теперь при работающем Charles Proxy приложение будет разрывать связь с ним по причине того, что Charles-сертификат не входит в список разрешенных. Пользователь будет видеть следующую ошибку:
Но теперь есть одна маленькая проблема — как мне-разработчику мониторить HTTPS-трафик своего же приложения?
Frida
Один из вариантов — отключить SSL Pinning с помощью dynamic code injection фреймворка Frida.
Идея в том, чтобы в процессе разработки приложения метод validateTrustCertificateList всегда возвращал true.
Этого, конечно, можно добится и без dynamic code injection, например, используя #if targetEnvironment(simulator) условие для отключения SSL Pinning на симуляторе, но это слишком просто.
С помощью Frida мы сможем написать скрипт на JavaScript (ловко, правда?), в котором подменим имплементацию validateTrustCertificateList на такую, что всегда возвращает true.
И этот скрипт будет впрыскиваться в приложение уже на этапе исполнения.
Как работает Frida на iOS, вы можете почитать здесь.
Установка Frida (взято отсюда).
sudo pip install frida-tools
Frida-скрипт
Непосредственный скрипт для подмены validateTrustCertificateList функции выглядит так:
Одина из проблем — как было получено полное имя функции _T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF
Для этого я использовал следующую технику.
Почему такое «странное» имя вышло у функции в конечном итоге и что значат все эти T016 и 0A15, можно посмотреть здесь.
Убийство SSL Pinning
Теперь наконец запустим FoodSniffer с отключенным SSL Pinnig!
Запустим Charles Proxy.
Запустим таргет FoodSnifferFrida в Xcode-проекте в симуляторе. Мы должны увидеть просто белый экран. Приложение ждет, пока к нему подключится Frida.
Дождемся подключения к iOS-приложению:
Продолжим работу приложения с помощью команды %resume:
Теперь мы должны увидеть список продовольствия в приложении:
И JSON в Charles Proxy:
Вывод
Frida — это как Wireshark для бинарников. Она работает на iOS, Android, Linux, Windows-платформах. Этот фреймворк позволяет отслеживать вызовы методов и функций — и системных, и пользовательских. А еще подменять значения параметров, возвращаемых значений и имплементации функций.
Обход SSL Pinning в условиях процесса разработки с помощью Frida может показаться немного overkill. Меня он привлекает тем, что мне не надо иметь в самом приложении специфичной логики для отладки и разработки приложения. Такая логика загромождает код и при некорректной имплементации может просочиться в релизную версию сборки (макросы, привет вам!).
Кроме того, Frida применима и для Android. Что дает мне возможность облегчить жизнь всей своей команде и обеспечить плавный процесс разработки всей линейки продукта.
Frida позиционирует себя как black box process code injection tool. С ней возможно, не меняя непосредственный код iOS-приложения, добавлять в runtime логирование вызовов методов, что может быть незаменимо при отладке сложных и редких багов.
Анализ взаимодействия мобильных Android-приложений с API социальных сетей Facebook, Instagram, ВКонтакте

На «очной ставке» NeoQUEST-2016 Максим Хазов рассказал о различных подходах к определению и использованию скрытого серверного API-функционала на примере таких популярных сервисов, как ВКонтакте, Instagram, Facebook.
В данной статье остановимся на основных моментах доклада и поделимся всеми электронными материалами: видеозаписью выступления, презентацией, а также демонстрациями атак для каждой рассматриваемой социальной сети (всё под катом).
Намекнём: «прогулки» по социальным сетям ещё предстоят участникам NeoQUEST-2017, регистрация на online-этап которого идет полным ходом!
Как это звучало на NeoQUEST?
Сразу же, как и обещали, делимся презентацией (тык) и докладом:
Зачем определять API?
Основных цели в данном случае две:
Как изучать API?
Есть 3 главных направления:
Примеры и демонстрации
Для демонстраций была использована контролируемая Wi-Fi точка, а также прокси для HTTP-запросов Burp Suite. В данном случае все тестируемые приложения взаимодействовали с API с помощью протокола HTTP и его модификаций.
ВАЖНО!
Исследование проводилось весной-летом 2016 года, на данный момент в архитектуре приложений и протоколов взаимодействия могут быть изменения (например, с ноября 2016 приложение ВКонтакте для Android больше не поддерживает протокол HTTP).
ВКонтакте и незащищенный протокол HTTP
Мобильное приложение ВКонтакте под Android по умолчанию использует протокол HTTP, а значит (как было показано в первой части доклада, видеозапись которого в конце статьи):
Instagram и HTTPS
Во второй части доклада было рассмотрено приложение Instagram под Android, для взаимодействия с API оно использует защищенный протокол HTTPS (HTTP + SSL). Это значит, что в общем случае (при отсутствии доступа к устройству у исследователя и достаточно современной версии TLS) протокол взаимодействия не уязвим к атакам MITM.
Но, имея доступ к телефону, можно установить на него свой корневой сертификат и легко расшифровывать трафик, подменяя сертификаты для HTTPS соединений (BurpSuite умеет это делать в автоматическом режиме). В демонстрации показаны перехват шифрованного трафика после установки на телефон контролируемого корневого сертификата и нахождение в нем запроса к API, отвечающего за лайк к фотографии.
У Instagram существует публичное документированное API для разработчиков. В нем заданы достаточно строгие лимиты для ключевых функций (лайки, подписки, постинг и т.д.). Как выяснилось, приложение Instagram использует другое, приватное API, в котором, разумеется, подобных лимитов нет.
Чем интересно это приватное API в плане “ботоведения”? Главное отличие приватного API: подпись всех важных запросов секретным ключом (уникальным для версии приложения). Так как ключ хранится внутри приложения, можно достать его с помощью реверс-инжиниринга приложения.
Facebook и SSL Pinning
В третьей части доклада было рассмотрено приложение Facebook под Android. Данное приложение для взаимодействия с API использует защищенный протокол HTTPS + надстройку под названием SSL Pinning.
SSL Pinning – внедрение в код мобильного приложения SSL-сертификата сервера API. Эта технология предназначена для защиты от перехвата трафика путем установки корневого сертификата на устройство и подмены сертификатов.
Тем не менее, SSL Pinning можно отключить (и даже разными способами):
Как выяснилось при анализе трафика, мобильное приложение Facebook использует некоторые недокументированные методы API (в том числе, для регистрации аккаунтов и входа в систему).
Также был найден недокументированный метод API, позволяющий получить ID пользователя Facebook по номеру телефона. Вот как выглядит демонстрация возможного автоматизированного использования полученных недокументированных методов:
В заключение
При должном желании всегда можно проанализировать протокол взаимодействия между мобильным приложением и сервером. Использование дополнительных средств защиты (таких, как SSL Pinning) может лишь несколько затруднить работу исследователю. Поэтому разработчикам следует избегать использования привилегированных недокументированных методов API в приложении (особенно если есть публичное и документированное API).
Кроме того, есть возможность сделать бота, который будет отправлять точно такие же запросы, как официальное мобильное приложение. Для борьбы с подобными ботами можно порекомендовать усложнять структуру запросов к API и тщательно анализировать все параметры запросов на серверной стороне.
На «очной ставке» NeoQUEST-2017, которая пройдет 29 июня в Питере, как и всегда, будут классные доклады о самых актуальных тенденциях кибербезопасности, современных механизмах защиты и способах их обхода.
Если ты не из Питера, смело планируй свой отпуск на июнь и приезжай! Белые ночи, разводные мосты, бесконечные дожди и NeoQUEST-2017 космической тематики с докладами, демонстрациями, конкурсами и призами — все это ждет тебя. Также ты можешь посетить «очную ставку» как участник hackquest и сразиться за главный приз — поездку на одну из международных конференций по информационной безопасности! Для этого — регистрируйся тут и с 1 по 10 марта проходи задания online-этапа. Возможно, именно ты окажешься лучшим? Узнаем 10 марта…
Что такое безопасность соединения или включите SSL-pinning в вашем мобильном приложении прямо сейчас
В этой статье я кратко расскажу, что означает «безопасное соединение» между клиентом и сервером, что такое SSL-pinning, для чего он нужен и когда его использовать. Так же объясню от каких угроз мы защищаемся с его помощью.
Представим, что у нас есть сайт или мобильное приложение (оба подпадают под определение клиента). При обращении сайта или мобильного приложения к бекенду (сервер) безопасность соединения обеспечивается с помощью протокола SSL (Secure Socket Layer), а если быть точнее, TLS (Transport Layer Security). TLS ставит своей целью создание между двумя узлами сети защищённого от прослушивания и подмены информации канала связи, а также проверку того, что обмен данными происходит между именно теми узлами, т.е. обеспечение конфиденциальности, целостности и аутентификации. Под узлами можно понимать сервер и мобильное приложение, либо сервер и веб-браузер.
Изначально SSL был разработан компанией Netscape для своего одноименного браузера в середине 90-х. Позднее на основании SSL 3.0 был принят RFC-стандарт TLS 1.0. Рекомендованными для применения в 2019 году являются версии TLS не ниже 1.2 (актуальная версия на сегодня — 1.3). На странице в википедии можно посмотреть поддержку версии TLS у всех популярных браузеров.
MITM (Man in the middle) — данный вид атаки направлен на «прослушку» или изменение трафика между двумя узлами (клиентом и сервером). В случае использования незащищенного HTTP это не составляет труда, тогда как в случае HTTPS (HTTP + SSL, или HTTP secure) данные зашифрованы и обычный просмотр пакетов трафика не даст никакой полезной информации злоумышленникам об их содержании. О том, каким образом шифруются данные, поговорим дальше.
Симметричное шифрование — вид шифрования, при котором для шифрования и дешифрования будет использоваться один и тот же ключ. Наиболее стойким на данный момент является шифр AES (Advanced Encryption Standard), длина ключа может быть 128, 192 или 256 бит. Из минусов симметричного шифрования — проблема передача ключа, ведь в случае его утери, им может воспользоваться злоумышленник. В случае TLS используется ассиметричное шифрование — когда для шифрования используется публичный ключ, а для дешифрования приватный. Публичный ключ передаётся от узла 1 к узлу 2, и даже в случае его перехвата злоумышленником, содержимое данных невозможно расшифровать, т.к. закрытый ключ не передается между узлами. Наиболее распространённый шифр этого типа — RSA (Rivest—Shamir—Adleman), размер ключа от 1024 до 4096 бит. Удобство ассиметричных шифров так же в том, что публичный ключ может быть передан большому количеству узлов без угрозы его утечки, как в случае ключа симметричного шифра. Из минусов — асимметричные шифры значительно медленнее симметричных. Поэтому в TLS используется смешанный подход — все данные шифруются симметрично, а набор ключей асимметрично. Но так как узлы шлют информацию в обе стороны, необходимо шифровать трафик с двух сторон. На помощь приходит протокол Диффи-Хеллмана. Первые 2 минуты видео достаточно понятно объясняют принцип его работы. Если мы хотим защитить соединение по HTTP, здесь стоит подумать о SSL-сертификате.
В качестве удостоверяющего хост документа выступает сертификат стандарта X.509. Под сертификатом можно понимать файл, в котором содержится следующая информация:
Сертификаты не существую сами по себе — их выпускают центры сертификации (Certificate Authority), в цепочке сертификатов самым первым является сертификат CA, его еще называют корневой. CA являются общеизвестными и ключи, которые они выпускают являются доверенными по умолчанию. Корневые сертификаты как правило “зашиваются” в операционную систему и обновляются при следующих обновлениях. Сертификаты выстраиваются в цепочку, можно так же назвать термин дерево: сертификаты первого уровня (выпущенные CA), так же могут выпускать сертификаты следующих уровней. В случае компрометации сертификата, он может быть отозван, и тогда автоматически отзываются все дочерние сертификаты.
Кажется, что TLS решает атаку MITM. Тем не менее, возможен вариант, когда между узлами окажется самоподписанный корневой сертификат злоумышленника и каким-то образом этот сертификат попадёт к нам на устройство. Например, если пользователь недостаточно технически образован и ему предлагается установить сертификат для пользования сетью Wi-Fi. После этого данные шифруются сертификатом злоумышленника (которые он легко расшифровывает) и мы не подозреваем о подмене. В данном случае злоумышленник выступает неким прокси-сервером между нашими узлами.
Если мы реализуем проверку сертификата на клиенте, то можем отловить ситуацию, когда сертификат между узлами неподлинный. Технически это выглядит следующим образом: при установлении соединения с хостом на клиенте проверяется, соответствует ли сертификат от сервера тому сертификату, о котором знает клиент. Если нет — соединение считается небезопасным и обмен данных с хостом прекращается. Помимо прослушивания трафика, SSL-pinning так же не раскрывает спецификацию запросов API, что может пригодиться при атаках как на сервер, так и на клиент.
По тому, какой сертификат проверяем:
По тому, какие данные из сертификата проверяем:
Раз уж мы обсудили SSL-pinning в контексте мобильных приложений, почему бы не сделать тоже самое с веб-сайтами? Ответ достаточно прост — браузер посещает большое количество сайтов, ровно как и каждый день появляется много новых, и это достаточно проблематично обладать информацией обо всех них. Мобильное приложение же, как правило, обращается к конечному числу доменов, информация о сертификатах хранится внутри него. В 99% случаев не нужно придумывать свои способы шифрования, TLS + SSL-pinning обеспечивают достаточный высокий уровень безопасности для передачи чувствительных данных по сети. Примеры приложений, где SSL-pinning является по-моему мнению обязательным — это мобильные приложения банков, другие приложения из финансового и страхового сектора. Для более подробного погружения в схему работы TLS советую данный ресурс.













