User Dictionary — что это за программа на Андроид и нужна ли она?
- Разбираемся
- Нужна ли программа?
- Внешний вид
Всем пламенный привет! Сегодня разговор про одну прогу, которую можете заметить на устройствах под управлением Android.
Короткий ответ: данное приложение позволяет работать с пользовательским словарем (редактор).
Разбираемся
Это редактор пользовательского словаря стандартной клавы — LatinIME.
Важный момент: оказывается приложение не поддерживает девайсы HTC.
Основные функции редактора:
- Сохранение словаря на внешнюю карту памяти.
- Загрузка словаря из текстового файла txt или файла базы данных SQLite. Русский вариант загрузить можно только если кодировка файла UTF-8. Официально можно загрузить файл с 7000 слов, однако по отзывам в интернете — загружали и файлы с 30 000 слов.
- Поиск и удаление дубликатов.
- Основные опции редактирования: удаление/поиск/чтение/запись.
- Удаление пользовательского словаря.
Нужна ли эта программа?
Она не имеет критически важного значения. Однако позволяет повысить удобство использования телефона, помогая легко и быстрее набирать текст:
- Быстро вводить часто используемые фразы.
- Заканчивать ввод слова без необходимости полного набора.
Поэтому мое мнение — прогу лучше не удалять, без нее некоторые привычные для вас опции могут перестать работать. PS: особенно это касается, если вы сами программу не устанавливали. Есть удобные подсказки при вводе текста? Вот за их работу и может отвечать данная программа!
Внешний вид
Основное меню приложения:
Достаточно удобно и просто все сделано, интерфейс не перегружен и это хорошо.
Настройки экспорта (сохранения):
Можно выбрать какой именно словарь будет экспортировать, а также указать папку сохранения, название txt файла.
Окошко добавления новых данных в словарь:
Надеюсь данная информация оказалась полезной. Удачи и добра, до новых встреч друзья!
Добавить комментарий Отменить ответ
Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.
Источник
Поиск и эксплуатация уязвимости пользовательского словаря в Android (CVE-2018-9375)
Всегда старайтесь выйти за рамки шаблонного мышления и помните, что время – один из наиболее ценных ресурсов.
Автор: Daniel Kachakil
Некоторое время я проводил аудит смартфон на базе Android и, в частности, все установленные приложения. Обычно, если позволяет время, я стараюсь вручную исследовать код. Именно подобным образом мне удалось найти уязвимость, позволяющую получить доступ к содержимому, которое, как предполагается, должно быть защищенным, а конкретно – к пользовательскому словарю, где хранятся сохраненные нестандартные слова.
Теоретически, доступ к пользовательскому словарю разрешен только привилегированным аккаунтам, авторизированным Редакторам Методов Ввода (IME) и корректорам орфографии. Однако существовал метод обхода этих ограничений, позволяя вредоносному приложению обновлять, удалять и даже извлекать содержимое словаря без необходимости в получении прав доступа или взаимодействия с пользователем.
Эта уязвимость среднего уровня опасности была отнесена к категории, связанной с расширением привилегий, и исправлена в июне 2018 года. Данная проблема была в следующих версиях Android: 6.0, 6.0.1, 7.0, 7.1.1, 7.1.2, 8.0 и 8.1.
В Android есть специальный словарь, который можно пополнять вручную или автоматически на базе тех слов, которые вводит пользователь. Доступ к этому словарю можно получить через меню «Настройки -> Язык и ввод -> Пользовательский словарь» (Settings -> Language & keyboard -> Personal dictionary). В некоторых случаях соответствующий пункт меню находится в другом месте. В словаре может содержаться в том числе конфиденциальная информация: имена, адреса, телефонные номера, электронная почта, пароли, коммерческие бренды, нестандартные слова (наименования заболеваний, лекарств, технический жаргон и т. д.) и даже номера кредитных карт.
Рисунок 1: Пример содержимого пользовательского словаря
Пользователь также может назначить короткую последовательность для ускоренного ввода длинной фразы или предложения как, например, полного домашнего адреса.
Рисунок 2: Пример привязки короткого слова к более длинной фразе
Содержимое персонального словаря хранится в базе данных SQLite в таблице «words» (независимо от «android_metadata»), которая состоит из шести колонок:
- _id (INTEGER, PRIMARY KEY)
- word (TEXT)
- frequency (INTEGER)
- locale (TEXT)
- appid (INTEGER)
- shortcut (TEXT)
Основное наше внимание будет приковано на колонке «word» поскольку, исходя из названия, там как раз хранятся слова. Хотя остальные колонки и другие таблицы в той же базе также будут доступны.
Технические детали уязвимости
В старых версиях Android доступ к пользовательскому словарю управляется следующими константами:
- android.permission.READ_USER_DICTIONARY (права на чтение).
- android.permission.WRITE_USER_DICTIONARY (права на запись).
В новых версиях эта тема уже не работает. Согласно официальной документации [1] (см. раздел Ссылки): «Начиная с API 23, пользовательский словарь доступен только через IME и корректор орфографии». Вышеупомянутые константы были заменены внутренними проверками, и теоретически доступ к пользовательскому словарю (content://user_dictionary/words) стал разрешен только привилегированным учетным записям (например, root и system), подключенным IME и корректорам орфографии.
Если посмотреть код репозитория AOSP (Android Open Source Project) на предмет изменений [2], то можно обнаружить новую функцию canCallerAccessUserDictionary, которая стала вызываться внутри стандартных методов query, insert, update и delete класса UserDictionary, предназначенного для работы со пользовательским словарем, и не допускает неавторизованный вызов этих методов.
В методах query и insert нововведение работает эффективно, однако в случае с функциями update и delete та же самая проверка выполняется слишком поздно. Тем самым у нас возникает уязвимость, позволяющая любому приложению успешно обойти проверку авторизации и запустить соответствующие функции через доступный провайдер контента.
Если обратить внимание на выделенные фрагменты в коде класса UserDictionaryProvider [3], то можно увидеть, что проверки авторизации выполняются после того, как запрос к базе данных уже выполнен:
public int delete(Uri uri, String where, String[] whereArgs) <
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
count = db.delete(USERDICT_TABLE_NAME, where, whereArgs);
String wordId = uri.getPathSegments().get(1);
count = db.delete(USERDICT_TABLE_NAME, Words._ID + «=» + wordId
+ (!TextUtils.isEmpty(where) ? » AND (» + where + ‘)’ : «»), whereArgs);
throw new IllegalArgumentException(«Unknown URI » + uri);
// Only the enabled IMEs and spell checkers can access this provider.
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) <
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
count = db.update(USERDICT_TABLE_NAME, values, where, whereArgs);
String wordId = uri.getPathSegments().get(1);
count = db.update(USERDICT_TABLE_NAME, values, Words._ID + «=» + wordId
+ (!TextUtils.isEmpty(where) ? » AND (» + where + ‘)’ : «»), whereArgs);
throw new IllegalArgumentException(«Unknown URI » + uri);
// Only the enabled IMEs and spell checkers can access this provider.
Также заметьте, что в файле AndroidManifest.xml не предусмотрено никаких дополнительных мер защиты (например, intent-фильтры или права доступа) для экспортируемого явным образом провайдера контента.
Злоумышленнику не составит особого труда обновить содержимое пользовательского словаря через запуск следующего кода из любого приложения и без специальных прав доступа:
ContentValues values = new ContentValues();
Схожим образом, можно удалить отдельные строки или весь пользовательский словарь:
getContentResolver().delete(UserDictionary.Words.CONTENT_URI, null, null);
Оба метода (update и delete) возвращают количество строк, участвующих в операции, однако в случае с нелегитимными вызовами, эти методы всегда возвращают 0, что немного затрудняет работу с информацией, которая выдается провайдером контента.
На первый взгляд, кажется, что вышеуказанные операции – единственное, что доступно злоумышленнику. Конечно, удаление или обновление произвольных записей может навредить пользователю, однако намного интереснее получить доступ к персональным данным.
Даже если упомянутая уязвимость отсутствует в функции query, полная выгрузка содержимого словаря все еще возможна через реализацию атак, сочетающих эксплуатацию временных характеристик и сторонних каналов. Поскольку аргументы условия where полностью контролируется злоумышленником и тот факт, что запрос на обновление выполняется дольше, если строки изменяются, чем тот же самый запрос, который никак не влияет на содержимое таблицы, мы можем реализовать вполне эффективную атаку.
Рассмотрим следующий фрагмент кода, запускаемого локально из вредоносного приложения:
ContentValues values = new ContentValues();
long t0 = System.nanoTime();
Помните о том, что мы не можем извлечь цифровые и буквенные значения напрямую, и нам нужно преобразовать выражения в булевые запросы, которые затем будут вычисляться как истинные или ложные на основе времени выполнения. Так работает алгоритм бинарного поиска. Вместо перебора всех символов напрямую формируется запрос: «больше ли код символа значения X». Этот запрос выполняется повторно, а значение X меняется в каждой итерации до тех пор, пока мы не найдем корректное значение, выполнив log(n) запросов. Например, если код текущего значения 97, итерации алгоритма будут следующими:
Процедура, описанная выше, была реализована в виде утилиты. Исходный код и скомпилированный APK доступен в репозитории https://github.com/IOActive/AOSP-ExploitUserDictionary.
Минималистический пользовательский интерфейс показан на рисунке ниже:
Рисунок 4: Интерфейс утилиты для эксплуатации уязвимости
Вначале приложение пытается получить доступ к контент-провайдеру пользовательского словаря, запрашивая количество записей. В обычных условиях (когда мы работаем от имени обычного пользователя и т. д.) доступ к словарю не разрешен. Если по какой-то причине у нас есть прямой доступ, никаких эксплоитов нам не потребуется. Но даже в этом случае можно потратить некоторое количество ресурсов процесса на тестирование утилиты вместо майнинга криптовалют :).
Как говорилось выше, нам нужно два параметра:
· Изначальное количество итераций: сколько раз нужно выполнять один и тот же запрос, чтобы получить существенную разницу во времени.
· Минимальный порог (в миллисекундах): какой временной уровень подойдет в качестве наименьшего допустимого значения.
Хотя текущая версия утилиты подбирает эти параметры автоматически, на самой начальной стадии оба параметра выставлялись вручную, и эти два поля перекочевали с тех времен.
В теории, чем больше эти два числа, тем большую точность мы получим, но в то же время замедлится процесс извлечения данных. Если значения уменьшить, понизится точность, но повысится время извлечения. Поэтому были прописаны два минимальных значения: 10 итераций и 200 миллисекунд.
Если нажать кнопку «Start», приложение начнет подбирать параметры автоматически. Вначале будут запущены несколько запросов и отброшены первоначальные результаты как нерепрезентативные. Затем выполняется первоначально заданное количество итераций и вычисляется временной порог. Если полученный порог выше изначально заданного минимума, тестируется точность посредством запуска 20 запросов, часть которых возвращают «истину», а часть – «ложь». Если точность неудовлетворительная (допускается только одна ошибка), количество итераций увеличивается и весь процесс повторяется заданное количество раз до тех пор, пока параметры не будут правильно настроены, или процесс завершится, если условия не будут удовлетворены.
После начала процесса некоторые элементы интерфейса станут недоступны, и мы увидим подробную стенограмму в окне ниже (или при помощи команды logcat), где, помимо других сообщений, показывается текущий идентификатор строки, все SQL-подзапросы, общее время и прогнозируемую истинность. Символы по мере извлечения будут отображаться в верхней строке.
Рисунок 5: Процесс извлечения данных
Кнопки «UPD» и «DEL» с правой стороны не имеют отношения к извлечению символов, а предназначены для прямых вызовов к контент-провайдеру для выполнения запросов UPDATE и DELETE соответственно. Эти запросы ограничиваются только словами, начинающимися с числа 123, чтобы избежать случайного удаления всего содержимого словаря. Таким образом, чтобы протестировать функционал этих кнопок нужно добавить в словарь новый элемент, если значения, удовлетворяющего нужным условиям, изначально не было.
Демонстрация работы утилиты
Вероятно, самый простейший способ подытожить сказанное – наглядно увидеть весь процесс в действии. На видео ниже показан демо пример, записанный во время работы с реальным устройством.
Между теорией и практикой всегда есть расхождения, и я бы хотел рассказать о некоторых проблемах, которые возникли во время разработки утилиты. Во-первых, не забывайте, что этот инструмент реализован на скорую руку. Моей главной задачей было показать, что концепция рабочая. У этого эксплоита есть несколько ограничений, и код во многом не соответствует наилучшим практикам программирования. Я не задавался целью сделать продукт, который эффективен, удобен для сопровождения, имеет хороший пользовательский интерфейс и так далее.
На первоначальных стадиях я не заботился об интерактивности и просто все выгрузил в логи Android. Когда было решено, что нужно добавить графическую оболочку, я вывел запуск кода в отдельный поток, чтобы не блокировать поток пользовательского интерфейса (в противном случае приложение может перестать подавать признаки жизни и завершится средствами операционной системы). После разделения поток точность метода заметно снизилась, поскольку у потока выполнения кода был не особо высокий приоритет. Я выставил максимально возможный приоритет «-20», и все снова заработало, как полагается.
Обновление интерфейса из другого потока может привести к краху приложения, который детектируется и предотвращается через динамические исключения (runtime exception). Таким образом, чтобы вывести логи, я пользовался этими исключениями через вызов runOnUiThread. В реальном эксплоите в пользовательском интерфейсе нет необходимости.
Если персональный словарь пуст, мы не можем использовать строки для обновления, и, соответственно, все запросы будут выполняться примерно одинаковое время. В этом случае извлекать будет нечего, утилита не сможет подстроить параметры и в конечном итоге остановится. Иногда может произойти случайная калибровка даже с пустой базой, и программа будет пытаться извлечь мусор или псевдослучайные данные.
В обычном смартфоне через некоторое время ОС будет переходить в спящий режим, и производительность сильно снизится. Соответственно, время выполнения увеличится и все вызовы будут считаться истинными. Эту проблему можно было бы решить разными способами, но я выбрал наипростейший, который связан с классом android.os.PowerManager.WakeLock, чтобы предотвратить подвешивание приложения операционной системой. Я не заморачивался, чтобы вернуть все обратно, поэтому потребуется принудительно завершить приложение.
Поворот экрана также вызывает проблемы, и я принудительно переключился в ландшафтный режим, чтобы избежать автоповорота и заодно воспользоваться большей шириной для отображения сообщений в одну строку.
После нажатия кнопки «START» некоторые элементы интерфейса станут недоступны. Если нужно перенастроить параметры или запустить много раз, потребуется закрытие и повторное открытие приложения.
Некоторые внешние события и параллельные процессы (например, синхронизация почты или получение push-сообщений) могут конфликтовать с приложением и потенциально стать причиной неточных результатов. В этом случае попробуйте отключить доступ к сети или закрыть некоторые программы.
Пользовательский интерфейс не поддерживает локализацию и не предназначен для извлечения слов в кодировке Unicode (хотя реализовать этот функционал не составляет особого труда, я не ставил себе такую задачу).
В утилите намеренно реализовано ограничение на извлечение первых 5 слов, отсортированных по внутреннему идентификатору.
В исходном коде устранить уязвимость не составляет особого труда. Нужно лишь переместить проверочный вызов на предмет наличия у вызывающего нужных прав в начало соответствующих функций, чего вполне достаточно для исправления бреши.
Помимо описания уязвимости мы отправили заплатку разработчикам Google, при помощи которой уязвимость была устранена: https://android.googlesource.com/platform/packages/…
Поскольку проблема была исправлена в официальном репозитории, нам, пользователям, нужно убедиться, что установленные обновления содержат патч для уязвимости CVE-2018-9375. Для Google Pixel/Nexus заплатка была выпущена в июне 2018 года: https://source.android.com/security/bulletin/pixel/2018-06-01
Если по каким-то причинам вы не можете установить это обновление, проведите ревизию персонального словаря на предмет присутствия конфиденциальной информации.
Разработка программного обеспечения не так проста, как кажется на первый взгляд. Как вы могли убедиться, неправильно расположенный вызов функции может стать причиной серьезных последствий. Изменения, целью которых было улучшение безопасности и защита пользовательского словаря, привели к противоположному результату, когда словарь стал доступен любому желающему. Эта уязвимость оставалась незамеченной почти три года.
Поиск подобных брешей не сложнее, чем чтение и понимание исходного кода. Просто нужно отследить поток выполнения. Автоматические тесты могли бы помочь в детектировании подобного рода проблем на ранних стадиях и предотвратили бы повторное появления в будущих изменения. Однако эти тесты не всегда так легко реализовать и поддерживать.
Мы также изучили методы, как выжать максимум из того, что у нас есть. Изначально у нас была возможность только удалить или испортить содержимое словаря, но в итоге мы научились извлекать информацию посредством реализации атаки с использованием стороннего канала и временных характеристик.
Всегда старайтесь выйти за рамки шаблонного мышления и помните, что время – один из наиболее ценных ресурсов. Каждая наносекунда на счету!
Источник