Pull to refresh

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

Reading time 5 min
Views 32K


Что в первую очередь сделает 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 — одним словом, это очень мощный инструмент для пентестинга как своих, так и сторонних приложений.

Ну и напоследок небольшой список ссылок для дальнейшего изучения:
Tags:
Hubs:
+35
Comments 21
Comments Comments 21

Articles