20.07.2015

Безопасность IOS-приложений (часть 38) – Безопасность приложений на базе Parse

image

Parse – прекрасная технология на основе модели BaaS (Backend as a service), позволяющей в кратчайшие сроки настроить инфраструктуру бэк-энда для мобильного приложения. С другой стороны, простота «расслабила» многих разработчиков, заставить позабыть о проблемах с безопасностью и уязвимостях.

Автор: Егор Толстой

Parse – прекрасная технология на основе модели BaaS (Backend as a service), позволяющей в кратчайшие сроки настроить инфраструктуру бэк-энда для мобильного приложения. С другой стороны, простота «расслабила» многих разработчиков, заставить позабыть о проблемах с безопасностью и уязвимостях.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/8822BC1A-FA0D-4BFF-8DC0-8CC103DFB92D.png

Рисунок 1: Интерфейс Parse

Для тех, кто ничего не знает о Parse, позволю себе пару вводных слов. Parse значительно расширяет возможности разработчика мобильных приложений. Например, можно без особого труда подключить облачное хранилище, пуш-уведомления, сбор статистики и крэш репортов, систему хранения кода, фоновые задания и многие другие фишки. В этой статье мы будем использовать только облачное хранилище Cloud Core.

Вся информация в Cloud Core хранится в так называемых специальных классах (custom classes), представляющих собой обычные таблицы базы данных.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/8879CA5B-B494-43CD-A136-1DB8059F5AC1-1024x412.png

Рисунок 2: Интерфейс для работы с таблицами

Для каждого класса можно установить различные права доступа: GET, FIND, UPDATE, CREATE, DELETE и ADD FIELDS. По умолчанию, все права доступа идут с атрибутом Public (то есть всем все разрешено), и естественно большинство разработчиков забывает о правах доступа после добавления и настройки таблиц.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/FDF17B0E-CD2B-47AB-BB46-F0D0BE9BDE7E.png

Рисунок 3: Настройка прав для конкретной таблицы

В одном из своих проектов я довольно плотно работал с Parse и в частности с настройкой прав доступа, после чего мне стало интересно, как у других разработчиков организовано взаимодействие с учетными записями, зарегистрированными в Parse. Подопытным кроликом выступил сервис для поиска офисов Cubefree.

Для соединения с аккаунтом из мобильного приложения используется пара ключей: Application ID и Client Key. Чтобы как-то работать с таблицами в Cloud Core, необходимо заполучить эти ключи. Вначале расшифруем приложение при помощи idb, незаменимого инструмента в арсенале пентестера. Пока приложение расшифровывается, заглянем NSUserDefaults, где часто неграмотные разработчики хранят ключи.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/03B3C111-1C51-4330-899E-7E5873D94B7F.png

Рисунок 4: Содержимое NSUserDefaults

Как видно из рисунка выше, в NSUserDefaults не хранится никаких конфиденциальных сведений. Вернемся к расшифрованному приложению и выполним дизассемблирование при помощи Hopper (известная утилита, хорошо подходящая для реверс-инжиниринга приложений, написанных на Objective-C). Наш квест по поиску ключей начнется с метода application:didFinishLaunchingWithOptions, принадлежащего App Delegate. В Hopper любая процедура может отображаться в виде псевдокода, что заметно облегчает изучение логики работы приложения.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/AB772C3F-377A-4B02-BA39-946D0B54831C.png

Рисунок 5: Псевдокод метода application:didFinishLaunchingWithOptions

Как и ожидалось, подключение к Parse начинается отсюда. Далее мы будем анализировать структуру хранения данных и права доступа.

Для начала выясним имена таблиц. На Рисунке 5 мы видим множество вызовов метода registerSubclass. Классы, к которым принадлежит метод registerSubclass, являются дочерними элементами класса PFObject. У каждого из этих классов есть метод parseClassName, который возвращает имя таблицы, хранящейся в Parse.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/FC415E09-109A-460B-9E04-763532556FDB.png

Рисунок 6: Псевдокод метода parseClassName

Проанализируем структуру таблиц:

https://gist.github.com/igrekde/cb8e2c12408715c9f739#file-parse-security-1

Знания структуры классов (или таблиц) не достаточно. Чтобы понять, как мы можем повлиять на логику работы приложения, необходимо также проанализировать права доступа ко всем классам. Эта задача решается довольно просто. Нам необходимо выполнить пару запросов и проанализировать результаты. Я написал небольшую утилиту Parse Revealer, которая берет на себя всю рутинную работа и выводит права доступа ко всем известным классам.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/E171D146-358B-41FD-98E2-FBC293EBAFEF.png

Рисунок 7: Интерфейс утилиты Parse Revealer

Сведем в единую таблицу все полученные сведения.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/%C3%90%C2%A1%C3%90%C2%BA%C3%91%E2%82%AC%C3%90%C2%B8%C3%90%C2%BD%C3%91%CB%86%C3%90%C2%BE%C3%91%E2%80%9A-2015-01-24-20.41.30.png

Рисунок 8: Структура таблиц и права доступа

Изучив перечень прав доступа, мы понимаем, что разработчик приложения пытался реализовать некую политику безопасности, однако этого недостаточно. Рассмотрим, чего можно достичь посредством манипуляций с классом ChatMessage.

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

https://gist.github.com/igrekde/cb8e2c12408715c9f739#file-parse-security-2

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/cubefree-screen-1024x907.png

Рисунок 9: Интерфейс чата

Мы также можем добавлять новые сообщения в любую комнату, доставив новый объект PFObject с корректным chatId. Однако мы благородные пентестеры и обратим внимание на тот факт, что сообщения нельзя удалять из-за повышенной мнительности разработчика :)

Более серьезная уязвимость заключается в некорректно реализованном алгоритме мапирования данных. Когда у объекта ChatMessage в поле отправитель (sender) - пустота, Cubefree аварийно завершается. Таким образом, становится возможным зациклить все комнаты, сформировав некорректный ChatMessage – и если кто-то откроет окно чата, приложение упадет. Подобный расклад чреват плохим рейтингом в App Store, оттоком пользователей и, в конечном счете, полным провалом проекта.

Существует еще несколько уязвимостей в других классах, но эта тема выходит за рамки данной статьи.

Чтобы ваши приложения были безопасными, следуйте простым правилам:

  • Всегда настраивайте права доступа у всех классов в Parse.
  • Используйте пользовательские права доступа (user-based ACL) – это отличная функция у Parse.
  • Если клиент должен изменять только одно свойство класса, подумайте об инкапсуляции этого поля в отдельный класс. Подобный трюк ограждает от злонамеренных изменений в ваших объектах.
  • Не полагайтесь на механизмы Parser, касающиеся качественной проверки возвращаемых данных. Всегда дополнительно проверяйте возвращаемые результаты.
  • Помните о том, что теоретически applicationID и clientKey может найти любой злоумышленник. Выстраивайте политику безопасности приложения исходя из этого утверждения.
  • Предыдущий совет не отменяет обфускацию строк в коде.
  • В более сложных ситуациях используйте Cloud Code.

Если после прочтения статьи вы нашли бреши в своих приложениях, не обвиняйте Parse. Это отличный сервис, минимизирующий затраты на разработку бэк-энда. Все найденные уязвимости полностью находятся в зоне ответственности разработчика.

Ссылки