25.07.2015

Безопасность IOS-приложений (часть 39) – Поиск конфиденциальной информации в оперативной памяти

image

В этой статье мы поговорим о том, как проанализировать содержимое участков памяти, где iOS-приложения могут хранить конфиденциальную информацию: пароли, идентификаторы сессий и т. д.

Автор: Prateek Gianchandani

В этой статье мы поговорим о том, как проанализировать содержимое участков памяти, где iOS-приложения могут хранить конфиденциальную информацию: пароли, идентификаторы сессий и т. д. Часто подобные экземпляры продолжают оставаться в памяти, когда, например, приложение интенсивно работает запросами, для отсылки которых требуется аутентификационный токен. Следовательно, должна существовать ссылка на токен, находящийся где-то в памяти. Даже если переменные хранятся в зашифрованном виде, во время работы приложение расшифровывает эти данные. Таким образом, анализ содержимого памяти – одно из важнейших направлений во время аудита iOS-приложения. Совет разработчику: если какая-либо важная информация не требуется для работы, эти сведения следует незамедлительно удалять из оперативной памяти сразу же после использования.

Большая часть содержимого оперативной памяти (например, UIView и UI element) не представляет для нас никакого интереса, и важно сосредоточить свое внимание только на релевантной информации. С точки зрения пентестера нам будут интересны контроллеры преставлений, свойства и экземпляры переменных.

Все эксперименты опять будут проводиться над приложением Damn Vulnerable iOS application.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/031715_1235_iOSApplicat1.png

Рисунок 1: Пользователь залогинился в приложении

Первая задача – найти свойства, в которых хранится имя пользователя и пароль. Для начала необходимо получить ссылку на текущий контроллер представления. При помощи утилиты class-dump выясняем, что текущий контроллер представлений - SensitiveInformationDetailsVC. Ссылку на этот класс будем искать через cycript при помощи функции choose(), которая принимает в качестве аргумента Objective-C класс и пытается просмотреть кучу на предмет присутствия участков, подходящих по форме и размеру под указанный класс (или подкласс). Как правило, функция возвращает ссылки на все объекты классов и подклассов, которые вы указали.

При помощи функции choose находим все экземпляры класса SensitiveInformationDetailsVC.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/031715_1235_iOSApplicat2.png

Рисунок 2: Получаем массив ссылок на экземпляры класса

Как видно из рисунка выше, в памяти присутствует только один экземпляр.

Чтобы найти перечень свойств найденного экземпляра, используем команду a[0]->isa.messages.

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/031715_1235_iOSApplicat3.png

Рисунок 3: Полный перечень свойств найденного экземпляра

Поскольку мы уже знаем имена нужных свойств («username» и «password»), то можем обратиться напрямую к этим свойствам при помощи команды ниже:

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/031715_1235_iOSApplicat4.png

Рисунок 4: Содержимое некоторых свойств экземпляра

Все просто как две копейки J.

Далее нам необходимо найти экземпляр переменной. Выведем на экран экземпляры всех переменных при помощи следующей функции:

function tryPrintIvars(a){ var x={}; for(i in *a){ try{ x[i] = (*a)[i]; } catch(e){} } return x; }

В качестве аргумента указываем ранее найденный экземпляр класса SensitiveInformationDetailsVC:

http://2we26u4fam7n16rz3a44uhbe1bq2.wpengine.netdna-cdn.com/wp-content/uploads/031715_1235_iOSApplicat5.png

Рисунок 5: Все экземпляры переменных, имеющие отношение к экземпляру класса SensitiveInformationDetailsVC

Однако на рисунке выше нет ни одной переменной с именем passwd. Дело в том, что экземпляры переменных могут быть инициализированы внутри определенного метода, и если функция не вызывается, то и переменная не инициализируется. Кроме того, в iOS есть функция автоматического подсчета ссылок (Automatic reference counting, ARC), которая автоматически удаляет переменные из памяти, как только приостанавливается вызов функции. В некоторых случаях вы не можете вызывать функцию посредством навигации внутри приложения. И тут на вновь на помощь приходит cycript. Возвращаясь к перечню методов, находим метод initializeLogin. И поскольку это единственный оставшийся метод среди стандартных iOS-методов наподобие viewDidLoad и didReceiveMemoryWarning, нетрудно предположить, что экземпляр переменной создается в функции initializeLogin.

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

-(void)initializeLogin {
//DO random stuff
NSString *passwd = @"MYw0r1d1821";
//Finish doing random stuff
}

Рекомендую вам ознакомиться с отличной статьей Марка Бёрда, посвященной выгрузке кучи приложения. К статье прилагается утилита.