Содержание статьи
В любой новости про зловредные приложения для платформы Android непременно упоминается механизм, с помощью которого Google удаленно удаляет нежелательное ПО со всех устройств разом. Как работает эта система, и не может ли она сама стать самым большим каналом распространения малвари?
Начну со статистики. Первый девайс на базе Android — HTC Dream/G1 — был запущен в США и Великобритании в октябре 2008 года. Начало стремительного распространения платформы связывают с появлением смартфона Motorola Droid в ноябре 2009 года. С тех пор количество девайсов, которые ежедневно активируются, растет феноменальным образом. На последней конференции Google I/O была заявлена баснословная цифра: 400 000 активаций каждый день (на конец июня эта цифра уже превысила 500 000)! Только подумай: это половина населения Кипра или, к примеру, целый Бруней. Всего на данный момент активировано более 100 миллионов устройств на Android. Неплохо. А теперь представь, что каждым из них Google может хоть немного, но управлять. Возможно, я слегка утрирую и слово "управлять" тут не самое подходящее. Достоверно известно, что Google может устанавливать и удалять произвольные приложения через механизм GTalkService. Связывая это с впечатляющей статистикой активаций новых устройств, волей-неволей задумываешься о потенциально самом большом ботнете в мире, в который пользователи сотнями тысяч входят добровольно. И хотя идея попахивает научной фантастикой, мне было интересно разобраться во внутренностях механизма GTalkService. Как он устроен? В каком виде на устройства приходят сообщения от сервера? Насколько защищен канал передачи? Нет ли опасности, что сообщение с управляющими командами на мое устройство может отправить кто-то еще?
Все дело в GTalkService
Любой девайс на Android поддерживает постоянное TCP/SSL/XMPP-соединение с серверами GTalk. Все время, когда у него есть доступ в Сеть. Соединением управляет специальный сервис GTalkSerive. Он постоянно отправляет пинги (так называемые "heartbeat-сообщения") на сервера Google, чтобы проверить активность соединения, а в случае обрыва связи автоматически переподключается. Этот канал связи позволяет Google осуществлять удаленное управление устройствами. Отправленное через GTalkService сообщение непременно попадает на каждый смартфон. С помощью этого механизма в части работает сервис C2DM (Cloud to Device Messaging Framework), с помощью которого разработчики могут отправлять приложениям, установленным на смартфонах пользователей, специальные команды (к примеру, на загрузку обновлений). Правда, пока C2DM ограниченно доступен лишь для некоторых разработчиков, которые оставили специальную заявку. Известно, что ОС Android поддерживает как минимум две команды: REMOVE_ASSET и INSTALL_ASSET, позволяющие Google удалять и устанавливать произвольные приложения. Таким образом, обнаружив в Android Market’е малварь, инженеры компании могут отправить сообщение REMOVE_ASSET через GTalkService, и зловредная программа будет разом удалена со всех подключенных к инету устройств. Этим, как мы знаем из новостей, компания не раз пользовалась. И обратная ситуация. Как только Google отдает команду INSTALL_ASSET, каждый получивший сообщение смартфон на Android скачивает APK-дистрибутив с программой и инсталлирует ее. Такая возможность с одной стороны хороша: Google может оперативно удалить всю появляющуюся малварь. Но с другой стороны, пугает. А что если кто-то сможет реализовать MITM-атаку на SSL-соединение конкретного телефона до GTalkService и проспуфить сообщение INSTALL_ASSET, чтобы залить на телефон какую-нибудь заразу? Безопасна ли система?
Надежность канала данных
Не будем рассматривать вариант с захватом инфраструктуры Google, через которую теоретически можно было бы загрузить произвольное приложение сразу на все Android-девайсы. На данном этапе будем считать это фантастикой. А вот что кажется более реальным, так это отправка фейковых команд на конкретный девайс. Да, подключение GTalkService, как я заметил выше, является защищенным: все данные передаются через SSL. Таким образом, базовая безопасность и целостность сообщений гарантируется уже самим протоколом. Но мы-то знаем цену этим гарантиям (читай материал "Вскрываем SSL" в 125 номере ][). При желании SSL-соединение можно скомпрометировать, перехватить и отреверсить пакеты, передаваемые между девайсом и серверами Google. Если разобраться в их структуре и правильно подделать сообщение INSTALL_ASSET, то мы можем принудительно установить на устройство произвольное приложение. Теоретически. Вопрос в том, есть ли еще какой-нибудь уровень защиты, например, цифровая подпись?
Чтобы исследовать поступающие от Google’а сообщения, их надо получить. Ты сейчас можешь подумать, что придется включить снифер и ждать, пока инженеры компании вновь отправят через GTalkService команду INSTALL_ASSET. Но когда это будет? На самом деле все проще. Забегая чуть вперед, скажу, что эта же самая служба используется всякий раз, когда пользователь устанавливает сообщения через Android Market. Нажатие кнопки "Install" приводит к тому, что через GTalkService отправляется INSTALL_ASSET, которая инициирует на устройстве процесс загрузки нужного APK-пакета программы и ее установки. Мы об этом еще поговорим, сейчас же важно одно: получить для исследования сообщение INSTALL_ASSET — не проблема. В общем-то, для просмотра трафика (несмотря на то, что он передается через SSL-соединение) нужно совсем немногое:
- Достать образ Android-эмулятора, в котором включена возможность работы с Android Market’ом.
- Добавить свой CA-сертификат в хранилище /system/etc/security/cacerts.bk, используя keytool или portecle.
- Реализовать MITM-атаку, заюзав sslsnif с CA-сертификатом.
Теперь, когда GTalkService захочет установить соединение, мы сможем перехватить трафик, поскольку устройство доверяет CA-сертификату, который мы создали. Если попробовать установить какое-нибудь приложение из Android Market’а на эмуляторе, то мы, соответственно, отснифаем сообщение INSTALL_ASSET. Оно будет выглядеть примерно так:
tickle_id: 1277687266074
assetid: -155863831473120556
asset_name: Replica Island
asset_type: GAME
asset_package: com.replica.replicaisland
asset_blob_url: http://android.clients.google.com/market/download/ Download?assetId=-155863831473120556&userId=986032118775&
deviceId=1094117203906638597
asset_signature: Ayn2bWDqckQkKsBY4JurvCFpYN0
asset_size: 5144485
Большинство параметров описывают приложение, которое пользователь запросил из Android Market’а. Интерес представляет атрибут asset_signature. Можно предположить, что это криптографическая подпись сообщения INSTALL_ASSET, с помощью которого дополнительно гарантируется его целостность. Увы, это не так. Энтузиастами давно установлено, что это не что иное, как кодированный в base64 хэш APK-файла (т.е. дистрибутива программы), который пользователь запросил из Android Market’а. Мы лишь можем в этот раз убедиться, скачав APK-пакет и выполнив соответствующие преобразования с его контрольной суммой. Становится понятно, что никакой дополнительной защиты для сообщения INSTALL_ASSET (и, стало быть, любых других) нет. Если атакующий сможет перехватить SSL-соединение между GTalkService, то теоретически сможет передать на телефон произвольные сообщения, в том числе, для установки приложений! Конечно, есть много "но", и представить в деле такую атаку довольно сложно. Для этого, по меньшей мере, нужно находиться с жертвой в одной сети, чтобы иметь возможность реализовать MITM-атаку. Не может идти и речи о каком-то массовом заражении пользователей. Это не умаляет недостаток защищенности канала связи, но перспективы к эксплуатации недоработок тут, откровенно говоря, слабые. Поэтому не будем на этом больше останавливаться, а посмотрим, какие еще сюрпризы таит в себе платформа Android и механизм GTalkService.
Взаимодействие с Android Market
Выше я сказал, что GTalkService вовлечен в процесс установки приложений из Android Market’а. Это вообще интересная история. Тут тоже есть интересные нюансы. Рассмотрим для начала этапы, которые проходит пользователь для установки программы из маркета:
- Запуск Android Market.
- Поиск нужной приложения для установки.
- Нажатие на кнопку "Install".
- Подтверждение необходимых для приложения привилегий.
- Закачка и установка приложения.
После этого у пользователя в панели оповещений выводятся сообщения о загрузке и установке приложения. Все просто и прозрачно, но… Если посмотреть на процесс изнутри, то все происходит несколько сложнее. Если первые четыре шага выполняются приложением Android Market, то за пятый (самый важный) этап отвечает совершенно другой компонент системы, а именно уже знакомый GTalkService. Схема работы (см. иллюстрацию для большего понимания) выглядит следующим образом:
- Пользователь кликает на кнопку установки приложения в Android Market’е.
- Приложение отправляет POST-запрос на серверы Android Market.
- Серверы Android Market отправляют информацию о запросе на установку приложения системе C2DM.
- Серверы C2DM отправляют смартфону пользователя сообщение INSTALL_ASSET через подключение GTalkService.
- Компонент GTalkService принимает сообщение INSTALL_ASSET и активирует Vending-компонент.
- Vending-компонент скачивает APK-пакет приложения и, в конце концов, устанавливает приложение.
Первое, что вызывает интерес — это, конечно же, POST-запрос, который передается на сервер. Раз его может отправить Android Market, то, возможно, это сможем сделать и мы с помощью специального приложения?
Попробуем разобраться. Перехваченный запрос выглядит следующим образом:
POST /market/api/ApiRequest HTTP/1.1
Content-Length: 524
Content-Type: application/x-www-form-urlencoded
Host: android.clients.google.com
Connection: Keep-Alive
User-Agent: Android-Market/2 (dream DRC83); gzip
version=2&request=CuACCvYBRFFBQUFLOEFBQUJvZWVEVGo4eGV4OVRJaW9...
Все банально, кроме параметра request, в котором очевидно и прячется все интересное. Зная Google, легко можно предположить, что это данные, упакованные в фирменную структуру protobuf и кодированные после этого в base64. Так и есть. Декодировав хэш и распаковав структуру, получаем данные запроса:
1 {
1: "DQAAAK8AAABoeeDTj8xex9TIio . . ."
2: 0
[... вырезано ...]
13: "-606db3000d480d63"
}
2 {
10
{
1: "353999319718585473"
}
}
Уже что-то, но есть проблема: мы не знаем, за что отвечает тот или иной параметр. Скорее всего, это некоторые идентификаторы девайса, информация о платформе, некоторые данные для авторизации и, конечно же, идентификатор запрашиваемого из Market’а приложения. Тут надо сказать, что для многих структур protobuf, которые участвуют в работе с Android Market, доступно описание, составленное энтузиастами в результате реверсинга. А на сайте code.google.com/p/android-market-api даже выложен коллективно написанный API, позволяющий запрашивать различные данные из маркета (описание, иконки программ и т.д.). Правда, данных о структуре запроса на установку приложений там нет. Зато реверсинг выполнил известный исследователь Android-платформы Джон Оберхейд, который впоследствии реализовал интересную атаку, о которой я и хочу тебе рассказать.
Итак, структура запроса в сокращенном виде выглядит так:
[.. вырезано ..]
message InstallRequest
{
optional string appId = 1;
}
message RequestContext
{
required string authToken = 1;
[.. вырезано ..]
required string androidId = 4;
optional string deviceAndSdkVersion = 5;
[.. вырезано ..]
Большинство полей из запроса могут быть извлечены из самого девайса (например, язык интерфейса, версия системы и т.д.). Но только не параметры appId и authToken:
- appId — является уникальным идентификатором приложения в Android Market’е. Этот идентификатор нигде не отображается, поэтому единственный способ его получить — запросить приложение из Android Market’а и отснифать трафик, вытащив из protobuf-структуры его идентификатор.
- authToken — это токен системы ClientLogin, с помощью которого серверы Android Market’а могут провести аутентификацию твоего запроса.
Если authToken остается в секрете, то проспуфить запрос не выйдет. Но ты можешь заметить, что раз он есть у девайса, значит, в каком-то месте системы он все-таки хранится.
Именно! Такое хранилище называется Account Manager и является важным компонентом платформы Android, предоставляющем данные для аутентификации. Например, если какое-то приложение хочет запостить в Twitter сообщение, то ему необязательно знать логин и пароль для Twitter-аккаунта — оно может запросить токен из AcountManager, который позволит ему отправить твит. Так вот в этом же самом месте хранится и authToken, используемый для взаимодействия с серверами Android Market. Но самое смешное из всей этой истории, что извлечь его можно буквально несколькими строчками кода:
AccountManager accountManager = AccountManager.get(getApplicationContext());
Account acct = getAccount(accountManager);
accountManager.getAuthToken(acct, "android", false, new GetAuthTokenCallback(), null);
Что это значит? Получается, что у нас есть все данные, чтобы составить protobuf-структуру, которую я привел выше, и сконструировать POST-запрос для отправки на серверы Android Market.
Если запрос будет корректным (а он будет, в чем я тебя уверяю), то на устройство через GTalkService будет, соответственно, отправлено сообщение INSTALL_ASSET, что приведет к установке указанного нами приложения! А поскольку система устроена так, что разрешение на установку пользователь отдает еще до отправки запроса (опять же смотри схему), то его вообще никто и ни о чем не будет спрашивать. И приложение установится в систему со всеми необходимыми разрешениями!
Атака через приложение
Теперь, когда мы знаем некоторые тонкости механизма установки приложений из Android Market’а, можно рассказать об изящной атаке, которую удалось провернуть Джону Оберхейду. Идея такова. Если мы можем сами конструировать запросы на установку, то ничто не мешает нам написать приложение, которое будет делать это автоматически.
Если подобную функциональность добавить во вполне невинную программу и начать распространять через Android Market (что при отсутствии контроля несложно), то всем установившим ее пользователям можно в придачу загрузить все что угодно! Сказано — сделано. Джон написал PoC-приложение и назвал его Angry Birds Bonus Levels, что должно было привлечь внимание пользователей Android Market. Простейшая социальная инженерия сработала: программу начали устанавливать пользователи. Наиболее внимательные из них наверняка замечали, что в области обновления появлялись сообщения об установке еще трех программ: для отслеживания месторасположения, осуществления звонков на платные номера и кражи контакт-листа.
Все они действительно имели вредоносную функциональность, но никак не использовались. Тут надо сказать, что исследователь сразу после тестирования PoC сообщил о проблеме в Google, и компания уже пофиксила баг.
Решение, кстати, оказалось очень простым. Система теперь отмечает для себя все запросы на установку приложений, сделанные через Android Market, и проверяет, чтобы для входящего сообщения INSTALL_ASSET был ранее сделан соответствующий запрос. Если Vending-компонент получает сообщение INSTALL_ASSET для приложения, которое он не ожидает, то команда просто игнорируется. Казалось бы, проблемы больше нет. Но! В теле сообщения может находиться специальный флаг, который позволяет отрубить проверку (в том числе для сохранения функциональности для удаленного удаления приложений), но это уже немного другая история.
Links
Статья подготовлена по материалам исследователя Джона Оберхейда.