- PVSM.RU - https://www.pvsm.ru -
Вы хорошо поработали, и вот ваше приложение в App Store!
Повод задуматься о безопасности кода и данных! Мы будем искать уязвимости в тестовом приложении. В этой статье поговорим о безопасности данных, а в следующей — перейдём к коду.
Цель данного урока — не сделать вас хакером, а показать, как злоумышленники могут обвести вас вокруг пальца. В статье пропущена кое-какая инфа, необходимая для взлома реального приложения на девайсе. Будем мучить симулятор (кстати, это вроде бы даже законно[citation needed]).
Disclaimer от переводчика: из оригинального текста убрано много «воды» и отсылок к Голливуду (и так длинно). Добавлен ряд ключевых пояснений.
Ни одно приложение не является безопасным! Если кто-то действительно захочет найти у вас уязвимости, то он их найдёт. Нет гарантированного способа предотвратить атаку. Разве что, не выпускать приложение. Но есть отличные способы помешать взломщикам. (По мнению автора, им надоест, и они пойдут спать искать более лёгкую добычу, ага. — Прим. пер.)
Перед тем, как читать дальше, вы должны примерно понимать, что такое терминал, а также Objective C и Cocoa (впрочем, особо много кода не будет).
Нам понадобится:
1. Утилита class-dump-z [1];
2. Прокси для отладки по сети, например, Charles [2] (триал-версия отличается надоедливыми сообщениями и работает максимум 30 минут за 1 сеанс). В комментах к исходнику [3] статьи советуют альтернативу Чарльзу — Burpsuite [4].
Чтобы вы творчески подошли к процессу, предлагаю вам сценарий. Представьте: вышло новое приложение для айпада — «Собиратель мемов» (Meme Collector). Всем нравится. Но вам шепнули на ухо, что встроенные покупки вытянут из вас значительную сумму денег.
В общем, вы задумали получить платный контент (мемы) бесплатно. Есть несколько направлений, куда можно двигаться, мы поговорим о них — и о соответствующих методах защиты.
Взглянем на приложение с высоты птичьего полёта! Что оно делает, с точки зрения пользователя? Какова его основная структура?
Откройте проект Meme Collector [5] в своей любимой IDE или в Xcode (не реклама).
Мы будем запускать приложение в конфигурации Release.
Xcode: Product > Scheme > Edit scheme… (⌘<) — слева выбрать Run…, справа вкладка Info > Build Configuration: Release.
AppCode: Run > Edit configurations… > Configuration: Release.
Запустите приложение на симуляторе iPad (⌘R). А теперь забудьте, что у вас есть исходники, вы — юзер. Откроется одно из двух:
{"success":true,"result":[список мемов]}
. Во время перевода статьи у них были проблемы с сервером. Если есть желание избавить проект от зависимости от данного API, велкам на гитхаб.
Этот странный интерфейс позволяет «купить» мем по тапу на него, а также показывает число покупок данного мема и остаток денег на вашем счёте. От переводчика: нажав кнопку «Purchase Currency», я реально задумался: а не вводил ли я Apple ID на симуляторе?
…но вспомнил, что у меня к US-аккаунту не привязана кредитка. :) Русский бы сказал цену в рублях.
В общем, всё просто. Есть «игровая валюта», и вы (типа взломщик) не хотите отдавать за неё реальные деньги. Наша цель — получить больше мемов, не нажимая кнопку «Purchase Currency».
Мы примерно поняли, что делает приложение, давайте заглянем глубже. Что ещё мы можем узнать?
Упомянутая утилита class-dump-z отобразит нам все объявления (declarations), которые она сможет достать из исполняемого файла. Скачайте [1] последнюю версию утилиты (например, 0.2a), распакуйте архив. В терминале перейдите в папку class-dump-z_0.2a/mac_x86/
. Там есть исполняемый файл class-dump-z, установите его, например, скопировав в /usr/bin/
:
sudo cp class-dump-z /usr/bin/
Перейдите в папку симулятора iOS: (вместо 7.0.3 — ваша версия)
cd ~/Library/Application Support/iPhone Simulator/7.0.3/Applications/
Здесь лежат все приложения, которые вы запускали в симуляторе. Их там может быть много. Как найти Meme Collector?
Вариант 1. Если вы только что запускали Meme Collector, то он лежит в папке с самой новой датой модификации. Просто перейдите в неё:
cd `ls -tr | tail -1`
Вариант 2. Взять и найти:
find . -name "Meme Collector"
В моём случае папка называется 9A72F266-8851-4A25-84E4-9CF8EFF95CD4
— далее будем называть её просто «папкой приложения». В ней лежит:
Meme Collector.app
— главный бандл (main application bundle)
Бандл — это папка (именно папка, да, с расширением .app
), в которой есть один или несколько исполняемых файлов плюс ресурсы к ним (картинки, звуки, цифровая подпись, в общем, что угодно, даже другие бандлы… read more [7]). Перейдём в папку основного бандла:
cd Meme Collector.app
Тут есть исполняемый файл, который так и называется: Meme Collector
(без расширения). Посмотрим, на какие фреймворки (frameworks) и общие библиотеки (shared libraries) имеются в нём ссылки. Нам поможет стандартная утилита otool
:
otool -L "Meme Collector"
И вот что видим:
Meme Collector:
/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration (compatibility version 1.0.0, current version 615.0.0)
/System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices (compatibility version 1.0.0, current version 51.0.0)
/System/Library/Frameworks/Security.framework/Security (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/StoreKit.framework/StoreKit (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/QuartzCore.framework/QuartzCore (compatibility version 1.2.0, current version 1.8.0)
/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 2903.23.0)
/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1047.22.0)
/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics (compatibility version 64.0.0, current version 600.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
/usr/lib/libSystem.dylib (compatibility version 1.0.0, current version 111.0.0)
Мм, интересно, приложение использует фреймворк Store Kit [8] — встроенные покупки? Ну-ка, идём дальше. Запускаем утилиту class-dump-z:
class-dump-z "Meme Collector" > ClassDump.txt
И откроем полученный файл в стандартном гуёвом редакторе:
open ClassDump.txt
Ого! Хотя нет, по сравнению с примером из оригинальной статьи [9] (Xcode 4, iOS 6 SDK) появились неприятные моменты типа XXUnknownSuperclass
, но всё же! Мы видим не только интерфейсную часть, но и объявления закрытых (private) методов, свойств, протоколов.
Изучение дампа классов серьёзного проекта — чаще всего утомительное занятие. Но оно может дать потрясающую картину внутреннего устройства приложения!
Итак, мой юный следопыт! Найдите-ка мне все синглтоны в приложении.
Если встретите интересный синглтон, изучите все его методы.
(Видимо, «интересный» = «похоже, имеющий отношение к логике приложения». — Прим. пер.)
@property(retain, nonatomic) AFHTTPClient* client;
@property(readonly, assign, nonatomic) NSMutableArray* memes;
+(id)sharedManager;
-(void)memePurchased:(int)purchased;
-(void)getMemeInformation; // кстати, это private-метод
@property(readonly, assign, nonatomic) NSNumber* money;
+(id)sharedManager;
-(BOOL)saveState; // кстати, это private-метод
-(void)loadState; // кстати, это private-метод
-(BOOL)purchaseCurrency;
-(BOOL)buyObject:(id)object;
Взломщики часто делают то, что вы сейчас сделали — ищут определённые слова в дампе класса. Например, если они хотят узнать, есть ли в приложении логика, завязанная на джейлбрейк девайса, то поиск по словам 'jailbroken', 'security' и т.д. может дать быстрый ответ.
Есть две новости. Начну с хорошей.
Apple делает это за вас автоматически. Когда вы отправляете приложение в App Store, они шифруют ваши бинарники с использованием DRM-технологии под названием FairPlay [11]. Если вы сдампите class-dump-z'ом зашифрованный бинарник, то получите… тарабарщину.
Плохая новость: обойти эту защиту довольно легко! Дело на 10 минут (вручную), а ещё есть средства автоматизировать сей процесс. В общем, будьте уверены, ваше приложение расшифруют и увидят названия всех ваших классов, методов, протоколов и т.д.
Мы кое-что узнали о приложении. Теперь поищем всё, что плохо лежит. Разработчикам свойственно ошибаться. Злоумышленникам свойственно использовать эти ошибки в своих целях.
Ошибки… Начнём с самых глупых! Например, как вам идея: использовать файл .plist для хранения критической информации? Взломщики всегда смотрят plist'ы: их легко просмотреть, а их изучение может дать ключ к внутренней логике приложения. (Plist = property list, текстовый файл с сериализованными объектами).
Посмотрим, какие plist'ы у нас есть?
(напомню, мы всё ещё находимся в папке бандла «Meme Collector.app»)
ls *.plist
А вот какие — две штуки:
Info.plist MoneyDataStore.plist
Посмотрим первый — Info.plist:
plutil -p Info.plist
Результат:
{
"DTSDKName" => "iphonesimulator7.0"
"CFBundleName" => "Meme Collector"
"CFBundleDevelopmentRegion" => "en"
"CFBundleVersion" => "1.0"
"DTPlatformName" => "iphonesimulator"
…и ещё куча всего…
Ничего интересного, какая-то общая информация. Ладно. Ну-ка, а второй:
plutil -p MoneyDataStore.plist
И видим:
{
"userCurrency" => 108
}
Прикол! Этот .plist содержит ключ userCurrency
с тем же значением, которое вы только что видели на экране симулятора. Значит ли это, что приложение хранит деньги в .plist'е? Есть только один способ узнать!
open MoneyDataStore.plist
Измените значение userCurrency
, например, на 1234. Сохраните и закройте файл .plist.
Вернёмся к симулятору. Уберём наше приложение из оперативной памяти (как говорят, «из многозадачности»).
Кажется, он не очень доволен, что его смахивают:
В дальнейшем, когда я буду говорить «перезапустите приложение», я буду иметь в виду именно эту операцию.
Снова запустите приложение. 1234 виртуальных тугриков в вашем распоряжении!
От переводчика: я ждал большей интриги. Не бывает так просто! Хоть свой тестовый проект пиши.
Ну вы поняли? Хранить важную информацию в .plist — не самая лучшая идея. А теперь угадайте: что представляют из себя пользовательские настройки (NSUserDefaults
)? Ага, тоже plist! Физически он лежит по адресу {App Directory}/Library/Preferences/{Bundle Identifier}.plist
.
Это как раз то место, где разработчики часто ошибочно чувствуют себя в безопасности. Не только фрилансеры: крупные корпорации время от времени попадают в эту ловушку. Бесчисленное множество приложений хранят важные данные в NSUserDefaults
. Посмотрим, что нам здесь приготовил Meme Collector?
Из терминала (мы по-прежнему находимся в Meme Collector.app, так?) откройте файл:
open ../Library/Preferences/com.selander.Meme-Collector.plist
Несложное упражнение для вас: используя методы, о которых говорилось выше, модифицируйте NSUserDefaults
так, чтобы получить кучу мемов из серии «Y U No …» бесплатно. Даже если вы уже обо всём догадались, советую сделать это для закрепления.
Но ведь злоумышленники могут получить доступ к plist-файлам, даже когда iOS-девайс заблокирован — так где же безопасно хранить данные? Одно из решений — хранить данные в NSUserDefaults
в зашифрованном виде. В этом случае (и не только. — Прим. К.О.) нужно проверять на валидность данные, читаемые оттуда.
Другое возможное решение — перенос важных данных из .plist'ов в Связку ключей iOS (Keychain). Как это сделать — написано, например, здесь [12].
Связка ключей «повышает ставки» для хакера. Злоумышленники не смогут ничего стянуть, если устройство заблокировано.
Тем не менее, не стоит полностью полагаться на одну только Связку ключей! И вот почему. Связку ключей поддерживает Apple. (Ну вы уже всё поняли, да?) Информация в ней зашифрована паролем пользователя, который обычно является простым 4-значным цифровым кодом. А это значит, что атака брутфорсом [13] займёт минут двадцать. Узнав пароль, легко сдампить всю связку ключей.
Что же делать? Некоторые общие рекомендации:
Ещё хакеры любят наблюдать, как приложение взаимодействует с сетью. Самый тупой способ увидеть, происходит ли какая-то работа с сетью на устройстве, это поискать URL'ы в бинарнике.
Находясь в папке бандла (Meme Collector.app), наберите в терминале:
strings "Meme Collector"
Стой, куда столько! Команда strings идёт по разделам бинарника и выводит все элементы данных, похожие на строки. Отфильтруем шум:
strings "Meme Collector" | grep http
А, ну вот, одна строка:
http://version1.api.memegenerator.net/Generator_Select_ByUrlNameOrGeneratorID
Похоже, в какой-то момент приложение обращается к генератору мемов по этому урлу. Будучи хакером, вы бы хотели исследовать это дело дальше, изучив сетевой трафик приложения. Для этого нам понадобится сетевой монитор, перехватывающий все входящие и исходящие запросы.
Charles, упомянутый в начале статьи — хороший вариант для такого исследования. Скачайте [2] его, если ещё не сделали это. Установите и запустите.
Проверьте, что Charles ловит сетевое взаимодействие с симулятора iOS (путём запуска «Карт», или в Safari набрать урл). Вы увидите, как в Charles побегут сетевые запросы. Если этого не произошло, убедитесь, что в меню Proxy > Mac OS X Proxy галочка установлена.
strings
. Но этот шаг потребуется для других приложений, которые могут использовать HTTPS. В меню Proxy > Proxy Settings… > SSL нужно включить SSL-прокси и добавить домены, по которым требуется перехватывать (и расшифровывать) HTTPS-трафик. Пока вы это не сделаете, SSL будет выглядеть примерно так:
При работающем Charles перезапустите Meme Collector. После запуска вы должны увидеть три запроса к http://version1.api.memegenerator.net
, нажав на треугольник слева от урла (см. ниже). Раскроются три запроса, отличающиеся GET-параметрами. Например, у первого один GET-параметр: urlName=Foul-Bachelor-Frog
— это видно на вкладке Request.
Выберите вкладку Response и затем внизу JSON. Ответ сервера, расшифрованный из JSON, представлен в виде красивой таблички:
Мы видим здесь множество строк вида «ключ-значение»: заголовок (displayName
), описание (description
), URL картинки (imageUrl
) — в общем, некая информация по данному типу мемов «Four Bachelor Frog» из GET-параметра.
Что происходит дальше? Приложение трижды делает одно и то же: HTTP-запрос по урлу картинки, пытается скачать её с cdn.memegenerator.co
, получает 301-й редирект на cdn.memegenerator.net
и качает оттуда.
Похоже на то, что мы видели в приложении, правда? Двум другим картинкам в этот раз повезло меньше, они так и не дождались ответа от сервера (Charles сообщает об этом на вкладке Overview) и поэтому не отобразились в приложении.
rm -R Library/Caches/
Итак, с определённой долей вероятности делаем вывод: приложение берёт мемы с данного API и представляет их в виде платного контента. А что если попытаться изменить URL с целью приобрести какой-то новый контент, кроме этих трёх мемов? Непохоже, что тут есть проверка, действительно ли приложение получает с сервера то, что ожидал разработчик!
Вам уже надоели эти три мема? Ну-ка попробуем, можно ли отобразить и «купить» что-то новое, скажем, «Success Kid».
Выберите в меню Charles: Tools > Rewrite. Эта функция позволяет перехватывать входящие/исходящие запросы и модифицировать их по тем правилам, которые вы установите. Включите галочку Enable Rewrite. Правила группируются в «наборы» (Sets). Под списком Sets нажмите Add для добавления нового набора правил. По желанию, переименуйте (Name). Мы создали набор правил, но он пока пустой. Давайте добавим правило — в разделе Rules есть кнопка Add, нажмите её.
Открылось окно Rewrite Rule. Измените Type на «Modify Query Param» («Изменить параметр запроса») и заполните два поля:
urlName
success-kid
Нажмите OK, OK. Перезапустите приложение… Success! Мы можем покупать контент, который ранее был недоступен.
Интересно: для этого нового мема указана конкретная цена. Откуда? Приложение должно было как-то определить стоимость, исходя из JSON-ответа.
Откройте вкладку Response и посмотрите на JSON, который возвращает сервер. Что может определить стоимость цены?
Попробуйте найти JSON-ключи, которые могут определить стоимость мема в приложении. Может быть, это generatorID, totalVotesScore, instancesCount, templatesCount или ranking. В качестве упражнения для вас: найдите тот ключ, который влияет на стоимость мема.
Чтобы сделать это, перейдите к Proxy > Breakpoints. Нажмите Enable Breakpoints и нажмите Add, чтобы добавить новую точку останова. Появится окно Edit breakpoint, введите в нём следующие данные:
http
version1.api.memegenerator.net
Теперь перезапустите приложение. Как только нам придёт ответ с сервера, сработает точка останова (breakpoint). Когда это произойдёт, щелкните на вкладке Edit Response, внизу выберите JSON:
Здесь вы можете вручную модифицировать JSON-ответ, который пойдёт в приложение. Поиграйтесь с этими параметрами и попробуйте определить, какие ключи влияют на цену, отображаемую в приложении. Изменив JSON-ответ, нажмите Execute (выполнить) для отправки ответа. Приложение делает три запроса к API, поэтому вам понадобится нажать Execute трижды.
ranking
. Чем больше ranking
, тем меньше цена.
Важно: действуйте быстро! AFNetworking имеет таймаут 30 секунд. Если вы перехватили ответ, но не успели внести изменения, AFNetworking вернёт ошибку таймаута запроса и выполнит соответствующий обработчик в коде (который в данном случае не делает ничего). Если у вас вышло время, перезапустите приложение и попробуйте снова.
Вы открыли в себе хакерские способности и выполнили простейшие тесты на проникновение на примере файловой системы и сетевого взаимодействия конкретного приложения. Вы победили простые plist'ы и даже можете модифицировать ответы сервера.
Возможно, эти знания повысят безопасность вашего iOS-приложения… чуть-чуть. В следующей части мы углубимся гораздо дальше в недры приложения, будем изменять его функционал! А пока вы ждёте, когда я выложу перевод (до следующей пятницы), можно много чего попробовать на тему хранения данных в приложении:
Без джейлбрейка? Да ну?
Замечания по поводу перевода или неработающих примеров можно отправлять на почту dev@
x128.ru.
Автор: x256
Источник [17]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mobile-development/47820
Ссылки в тексте:
[1] class-dump-z: https://code.google.com/p/networkpx/wiki/class_dump_z
[2] Charles: http://www.charlesproxy.com/download/
[3] исходнику: http://www.raywenderlich.com/45645/ios-app-security-analysis-part-1#comments
[4] Burpsuite: http://highaltitudehacks.com/2013/08/20/ios-application-security-part-11-analyzing-network-traffic-over-http-slash-https/
[5] проект Meme Collector: https://github.com/x128/MemeCollector
[6] API memegenerator.net: http://version1.api.memegenerator.net/Generators_Select_ByNew
[7] read more: http://en.wikipedia.org/wiki/Application_bundle
[8] Store Kit: https://developer.apple.com/library/ios/documentation/StoreKit/Reference/StoreKit_Collection/_index.html
[9] оригинальной статьи: http://www.raywenderlich.com/45645/ios-app-security-analysis-part-1
[10] методов класса: http://en.wikipedia.org/wiki/Method_%28computer_programming%29#Class_methods
[11] FairPlay: http://en.wikipedia.org/wiki/FairPlay
[12] здесь: http://www.raywenderlich.com/6475/basic-security-in-ios-5-tutorial-part-1
[13] брутфорсом: http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%BB%D0%BD%D1%8B%D0%B9_%D0%BF%D0%B5%D1%80%D0%B5%D0%B1%D0%BE%D1%80
[14] пример: https://developer.apple.com/library/ios/samplecode/CryptoExercise/Introduction/Intro.html
[15] iExplorer: http://www.macroplant.com/iexplorer/
[16] этот туториал: http://www.raywenderlich.com/37181/ios-assembly-tutorial
[17] Источник: http://habrahabr.ru/post/199128/
Нажмите здесь для печати.