Содержание статьи
info
Читая эту статью, держи в уме, что мы ограничены по времени, поэтому некоторые подходы анализа, которые можно было бы применить, будут упомянуты лишь вскользь. И еще: я специально оставил описание всех своих ошибок, которые допустил при анализе. Как говорится, умный учится на чужих ошибках.
Цель
Искать ошибки в приложении для Android можно по‑разному, так же как и выбирать места для этого самого поиска. В рамках этой статьи мы выберем цель пониже уровнем — разделяемые библиотеки, то есть ориентироваться будем на баги memory corruption. Код на Java рассмотрим только в случае, когда необходимо выяснить его связь с разделяемыми библиотеками.
Для анализа APK использовались стандартные инструменты из любого публичного awesome list, поэтому я не буду заострять внимание на их названии, если того не требует контекст.
Целевой APK был получен с одного из mirror-сайтов. К выбору источника APK стоит относиться серьезно, поскольку нередко сайт может хранить:
- уже устаревшие версии;
- только версии для ненужных платформ и архитектур;
- модифицированные приложения (возможно, с содержанием малвари).
На данном этапе имеет смысл провести рекогносцировку цели: изучить CVE, а затем провести binary diffing анализ 1-day-уязвимостей.
Разделяемые библиотеки
После распаковки APK в директории lib
можно обнаружить файлы библиотек: они не запакованы, не зашифрованы, не обфусцированы, не накрыты протектором, что облегчает нашу задачу в несколько раз. В защиту Viber могу сказать, что обычно мессенджеры не пытаются применять пассивные меры защиты от анализа библиотек, у WhatsApp лишь используется кастомный упаковщик, но и то — не ради защиты. Для анализа я выбрал версии библиотек для архитектуры x86_64 по следующим причинам:
- большее количество инструментов для этой архитектуры;
- лучше декомпиляция (это, конечно, спорно, так как многое зависит от выбора инструмента);
- возможность эмуляции на более высоких скоростях (моя хост‑машина имеет архитектуру x86_64);
- возможность частичного анализа на хост‑машине в обход эмулятора;
- на данном этапе нет задачи писать конкретный эксплоит для покрытия большего числа целей, соответственно, ARM-архитектуры можно отбросить, если это потребуется.
Поначалу для поверхностного анализа использовались такие инструменты, как IDA Pro, Binary Ninja и rizin (Ghidra не взял, потому что задачу следовало решить быстро): загружаешь библиотеку, смотришь экспортированные символы, находишь строки, немного читаешь код. Но затем я перешел к oneline-команде — по сути, большего мне и не требовалось: readelf
.
После идентификации JNI-функций из библиотек прохожусь rg
/grep
по smali-коду и нахожу файлы, где содержится объявление native-функций:
$ readelf -W --demangle --symbols libnativehttp.so | tail -n +4 | sort -k 7 | rg "FUNC.Java_." | less
33: 0000000000001bb5 55 FUNC GLOBAL DEFAULT 13 Java_com_viber_libnativehttp_HttpEngine_nativeCreateHttp
34: 0000000000001bec 15 FUNC GLOBAL DEFAULT 13 Java_com_viber_libnativehttp_HttpEngine_nativeDelete
38: 0000000000001bfb 622 FUNC GLOBAL DEFAULT 13 Java_com_viber_libnativehttp_HttpEngine_nativeTest
44: 00000000000018c3 109 FUNC GLOBAL DEFAULT 13 Java_com_viber_libnativehttp_NativeDownloader_nativeOnConnected
39: 00000000000015e8 366 FUNC GLOBAL DEFAULT 13 Java_com_viber_libnativehttp_NativeDownloader_nativeOnData
35: 0000000000001b0c 40 FUNC GLOBAL DEFAULT 13 Java_com_viber_libnativehttp_NativeDownloader_nativeOnDisconnected
40: 0000000000001930 476 FUNC GLOBAL DEFAULT 13 Java_com_viber_libnativehttp_NativeDownloader_nativeOnHead
$ rg "native.*nativeCreateHttp"
app/src/main/java/com/viber/libnativehttp/HttpEngine.java
9: public static native long nativeCreateHttp();
Дальне нужен поверхностный анализ, чтобы выявить, во‑первых, библиотеку‑цель, во‑вторых, компоненты open source, исследование которых предстоит сделать позднее. Анализировать можно с помощью того же readelf или в Rizin либо Binary Ninja: гуглим имя экспортированного символа и проводим поверхностный реверс‑инжиниринг, чтобы воссоздать общую картину функций библиотеки.
Предварительные результаты анализа
Ниже представлен список разделяемых библиотек с кратким описанием функций или ссылкой на проект с открытыми исходниками.
-
libc++_shared.
— C++ standard library.so -
libcrashlytics-common.
,so libcrashlytics-handler.
,so libcrashlytics-trampoline.
,so libcrashlytics.
— Firebase Crashlytics.so -
libreactnativeblob.
,so libreactnativejni.
,so libglog_init.
,so libjscexecutor.
,so libjsijniprofiler.
,so libjsinspector.
— React Native.so -
libfb.
,so libfbjni.
— fbjni.so -
libfolly_futures.
— Folly: Facebook Open-source Library.so -
libfolly_json.
— Folly: Facebook Open-source Library, double-conversion.so -
libglog.
— Google Logging Library.so -
libhermes-executor-release.
,so libhermes.
— Hermes JS Engine.so -
libicuBinder.
— ICU extension for SQLite.so -
libimage_processing_util_jni.
— androidx.camera.core.so -
libimagepipeline.
,so libnative-filters.
,so libnative-imagetranscoder.
— Fresco.so -
libgifimage.
— The GIFLIB project, Fresco.so -
libjingle_peerconnection_so.
— старый компонент (so libjingle
) из WebRTC. -
libmux.
— FFmpeg из составаso fftools
. -
libpl_droidsonroids_gif.
— android-gif-drawable.so -
librenderscript-toolkit.
— RenderScript.so -
libsigner.
— Adjust SDK for Android.so -
libspeexjni.
— Speex.so -
libsqliteX.
— SQLite for Android.so -
libtensorflowlite_gpu_jni.
,so libtensorflowlite_jni.
— TensorFlow.so -
libyoga.
— Yoga.so -
libCrossUnblocker.
,so libFlatBuffersParser.
,so liblinkparser.
,so libnativehttp.
,so libsvg.
,so libViberRTC.
,so libvideoconvert.
,so libVoipEngineNative.
— самописные библиотеки c использованием open source кода.so
В итоге у нас появляется список интересных библиотек. Составлялся он исходя всего из одного условия: как можно больше самописного кода, меньше компонентов open source. Вот этот список:
-
libCrossUnblocker.
;so -
libFlatBuffersParser.
;so -
liblinkparser.
;so -
libnativehttp.
;so -
libsvg.
;so -
libViberRTC.
;so -
libvideoconvert.
;so -
libVoipEngineNative.
.so
Функции
Прежде чем анализировать какую‑либо функцию, сначала нужно выяснить, может ли атакующий до нее добраться. Для этого отсортируем по приоритету все библиотеки и функции, а затем проверим, откуда вызываются последние. Для анализа Java-кода я буду использовать связку jadx (декомпиляция) + Android Studio (рефакторинг) + Understand (анализ графов связей переменных, функций и данных).
Дополнительно проверим в каждой библиотеке наличие JNI-функций. Может быть, есть те, что используются с помощью RegisterNatives
.
libFlatBuffersParser.so
При более детальном анализе выясняется, что это open source библиотека FlatBuffers. Оставляем ее анализ на потом.
libsvg.so
Пользуемся утилитой strings и Ghidra, чтобы получить строки из бинарного файла. По ним мы понимаем, что код написан на C++:
[...]
_ZTVN10__cxxabiv121__vmi_class_type_infoE
_ZNSt6__ndk119__shared_weak_countD2Ev
_ZTINSt6__ndk119__shared_weak_countE
__android_log_print
_ZNKSt6__ndk16locale9has_facetERNS0_2idE
_ZNKSt6__ndk16locale9use_facetERNS0_2idE
[...]
Проверим еще и наличие RTTI-информации — в нашем случае удача благоволит нам, таковая имеется. С помощью плагина для Ghidra Ghidra C++ Class and Run Time Type Information Analyzer восстановим структуру классов кода C++.
На первый взгляд, здесь используется собственная SVG-библиотека, что хорошо. В ходе дальнейшего анализа Java-кода (анализ дерева вызовов) выясняется, что функции библиотеки задействованы в основном для загрузки ассетов приложения (директория ./
в APK-файле), а это нам не подходит. Однако же цепочка вызовов нативной функции nativeParseFd
→ parseFile
используется для парсинга стикеров. А вот это уже интересно! Но я решил оставить эту функцию на потом, поскольку моя интуиция подсказала: бессмысленно писать парсер SVG с нуля. Соответственно, используется что‑то из open source.
libnativehttp.so
В ходе анализа возникает вопрос, с какой целью создавалась эта библиотека, ведь кажется, что ее возможности не очень широки: обертки над функциями обработки сетевых данных. Обработчики при возникновении событий вызывают все тот же Java-код, а не что‑то нативное. Может быть, здесь когда‑то были какие‑то функциональные возможности, ну или предполагались? Подобное я уже видел в мессенджере Telegram: legacy-кода хоть отбавляй, от такого большого attack surface поначалу чешутся руки, но после анализа все встает на свои места. Поэтому, уважаемый читатель, принимай во внимание, что иногда могут встречаться не только dead code, но и dead libraries, на анализ которых можно впустую потратить кучу ценного времени.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»