Пользователь
21,6
рейтинг
25 ноября 2014 в 16:20

Разработка → Обходим защиту iOS клиента Dropbox



Что в первую очередь сделает iOS разработчик, если перед ним встанет задача спрятать пользовательские данные от любопытных глаз? Конечно же, встроит экран парольной защиты. Особо хитрый разработчик даже не будет хранить установленный пользователем пароль в NSUserDefaults, а аккуратно спрячет его в связку ключей — в этом случае приложение в глазах как его, так и заказчика, сразу же перемещается в разряд «сверхзащищенных».
Мы не будем углубляться в описание опасностей хранения ключа в открытом доступе — ведь есть гораздо более интересный способ обхода такой защиты.

Cycript — консольная утилита, позволяющая подключаться к Mac OS X и iOS приложениям во время их выполнения. Для работы с кодом исполняемого приложения используется гибрид Javascript и Objective-C++. Проект живет с 2007 года, изначально представляв собой мост между Javascript и Objective-C. Cycript интересен в первую очередь тем, что позволяет исследовать приложение “изнутри”, посылая сообщения любому из его объектов. Кстати, автор проекта — сам saurik.

Внимание: для работы с Cycript необходим Jailbreak (разработчики, тем не менее, могут подключить библиотеку к своему проекту, чтобы получить возможность тестировать приложение на “чистом" устройстве).

Установка Cycript предельно проста — качаем последнюю версию пакета и заливаем ее по sftp на наше устройство:
sftp>put cycript.deb

после чего подключаемся по ssh и устанавливаем пакет:
iPad:/tmp root# dpkg -i cycript.deb

В качестве объекта тестирования был выбран клиент Dropbox, позволяющий установить экран парольной защиты, который появляется каждый раз при открытии приложения. Запустим Dropbox, заранее включив опцию запроса пароля.


Узнаем идентификатор процесса запущенного приложения, после чего подключимся к нему:
iPad:~ root# ps aux | grep Dropbox
mobile     283   0.0 10.1   641388  52020   ??  Ss    5:41PM   0:19.15 /var/mobile/Containers/Bundle/Application/93E2B5E6-B7EC-4D18-9697-021D24429D29/Dropbox.app/Dropbox
root       328   0.0  0.1   536256    440 s000  S+    6:07PM   0:00.01 grep Dropbox
iPad:~ root# cycript -p 283

Посмотрим, что представляет собой rootViewController запущенного приложения:
cy# UIApp.keyWindow.rootViewController
#"<DBSplitViewController: 0x16bc02a0>"

Теперь проверим, какой контроллер сейчас отображен на экране:
cy# UIApp.keyWindow.rootViewController.presentedViewController
#"<PasscodeFullscreenController: 0x187c8b50>"

Название обнадеживает :) Обратимся к одной из сильных сторон использования Cycript и определим свою функцию, которая покажет нам все методы, определенные для выбранного класса:
function printMethods(className) {
  var count = new new Type("I");
  var methods = class_copyMethodList(objc_getClass(className), count);
  var methodsArray = [];
  for(var i = 0; i < *count; i++) {
    var method = methods[i];
    methodsArray.push({selector:method_getName(method), implementation:method_getImplementation(method)});
  }
  free(methods);
  free(count);
  return methodsArray;
}

Теперь мы можем использовать эту функцию и посмотреть, чем занимается PasscodeFullscreenController:
cy# printMethods(PasscodeFullscreenController)

[{selector:@selector(initWithPasscodeViewController:),implementation:0xdf551},{selector:@selector(dealloc),implementation:0xe0131},{selector:@selector(shouldAutorotate),implementation:0xe0045},{selector:@selector(supportedInterfaceOrientations),implementation:0xe010d},{selector:@selector(viewDidLoad),implementation:0xdf5b1},{selector:@selector(.cxx_destruct),implementation:0xe0181}]

А теперь объявим переменную, содержащую текущий экземпляр этого класса, и посмотрим список его переменных:
cy# var PasscodeFullscreenController = #0x187c8b50
#"<PasscodeFullscreenController: 0x187c8b50>"
cy# *PasscodeFullscreenController

{isa:#"PasscodeFullscreenController",_view:#"<UIView: 0x187c9870; frame = (0 0; 768 1024); autoresize = W+H; layer = <CALayer: 0x187c9800>>",_tabBarItem:null,_navigationItem:null,_toolbarItems:null,_title:null,_nibName:null,_nibBundle:#"NSBundle </private/var/mobile/Containers/Bundle/Application/93E2B5E6-B7EC-4D18-9697-021D24429D29/Dropbox.app> (loaded)",_parentViewController:null,_childModalViewController:null,_parentModalViewController:#"<DBSplitViewController: 0x183c3ca0>",_previousRootViewController:null,
...
_presentationSizeClassPair:{width:0,height:0},_navigationControllerContentInsetAdjustment:{top:0,left:0,bottom:0,right:0},_contentOverlayInsets:{top:20,left:20,bottom:0,right:20},__embeddedViewFrame:{origin:{x:0,y:0},size:{width:0,height:0}},_passcodeController:#"<PasscodeViewController: 0x187c8730>"}

Судя по всему, нам нужно прямиком в PasscodeViewController:
cy# printMethods(PasscodeViewController)

[{selector:@selector(dismissPasscodeAnimated:),implementation:0xa933d},{selector:@selector(initWithPasscodeEntryMode:),implementation:0xa7801},{selector:@selector(presentPasscodeModalAnimated:),implementation:0xa91c5},
...
{selector:@selector(presentationControllerForPresentedViewController:presentingViewController:sourceViewController:),implementation:0xaba01},{selector:@selector(canCancel),implementation:0xa95b9},{selector:@selector(setCanCancel:),implementation:0xa9575}]

Похоже, что первый метод делает именно то, что нас интересует. Проверим:
cy# var PasscodeViewController = #0x187c8730
#"<PasscodeViewController: 0x187c8730>"
cy# [PasscodeViewController dismissPasscodeAnimated:YES]

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


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


При выборе пункта “Passcode Lock” появляется уже знакомая форма ввода пароля. Воспользуемся уже знакомым нам способом, чтобы от нее избавиться:
cy# var PasscodeController2 = #0x1992aab0
#"<PasscodeViewController: 0x1992aab0>"
cy# [PasscodeController2 dismissPasscodeAnimated:YES]



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

Среди сотни различных селекторов интерес представляет следующий:
db_handleVerifyModeCorrectPasscodeEntered:0xb1bd5

И действительно, его вызов закрывает экран ввода пароля и открывает настройки. Повторим то же самое при подтверждении отключения пароля:


Теперь приложение можно спокойно закрывать — ввод пароля больше не требуется.

Мы рассмотрели отнюдь не весь функционал Cycript — помимо этого, он позволяет определять свои классы, категории, работать с блоками, импортировать дополнительные библиотеки, производить method swizzling — одним словом, это очень мощный инструмент для пентестинга как своих, так и сторонних приложений.

Ну и напоследок небольшой список ссылок для дальнейшего изучения:
Егор @YourDestiny
карма
35,7
рейтинг 21,6
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (21)

  • +19
    Ну в целом выходит если пользователь сам сделал Jailbreak, то сам виноват в уязвимости своего устройства?
    • +11
      Есть кейсы, в которых устройство пользователя попадает в руки злоумышленника, и он уже jailbreak-ет его.
      • +1
        Надо сразу блочить телефон и менять все пароли, что использовались на телефоне.
      • +2
        Если воспользоваться функцией Find My iPhone, то никакой джейл не поможет. Так что кейс очень специфический.
        • 0
          Чтобы вы могли ею воспользоваться и заблокировать айфон, айфон должен иметь соединение с интернетом. Злоумышленник может вынуть симку.
          • –2
            А кому и зачем нужен айфон без интернета?

            Ну если только злоумышленник сумеет продать телефон жертве не дав ей подключить его ни к WiFi ни вставить свою симку на которой интернет скорее всего был бы.
            • 0
              Ну если ваш телефон украли, чтобы перепродать, то да. Find My iPhone снизит продажную цену раз в десять.
              А если злоумышленнику нужен не телефон, а информация на нём, то тут возможны варианты.
      • 0
        Опять же: если включен Find My iPhone (у кого более-менее ценная информация на телефоне он включен) — джейл не поставить.
    • +1
      Статья не только о потенциальной пользе от такого взлома, а о возможностях Cycript для реверсинжиринга и изучения сторонних приложений.
  • +5


    И нафига вообще замазывать было?
    • +4
      Присоединюсь к вопросу.

      Единственный надежный простой способ что-то спрятать — непрозрачный прямоугольник. Размытие, вне зависимости от силы, прекраснейшим образом обращается. А такое размытие, как на картинке выше, и глазами читается без проблем.
  • +2
    аккуратно спрячет его в связку ключей

    После этих слов ожидал увидеть дампер связки ключей. Оно конечно все там в безопасности, но если речь о jailbreak, то все ее содержимое можно получить в открытом виде.

    А описанный метод больше похож на туториал по cycript. Для простого баловства с игрульками и реверсинга простейших private API — пожалуйста. И то, лично мне удобнее в IDA сразу залезть. Тем более что, попадется вам приложение, где все obj-c символы (селекторы и классы) подменены на что-то иное (рандом или просто A, AA, B, CDC), то ничем уже ни cycript, ни class-dump не помогут. Видел такие лично. Не говоря уже о С/С++. Боюсь, что с массовым переходом на Swift и эта лавочка прикроется. И ничего кроме IDA не останется.
  • +2
    Позор дропбоксу, конечно. Пасскодом нужно не вьюшку скрывать, а шифровать данные. Если после такого «отключения» оно ещё и продолжает синхронизироваться, то можно сам пароль от дропбокса оттуда вытащить.
  • 0
    Использую для этих целей твик iAppLock
  • 0
    Хотелось бы на что-то претендующее на серьезную защиту посмотреть изнутри. Например, ВКармане.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.