(Не)безопасность iOS-приложений (Часть 2)

(Не)безопасность iOS-приложений (Часть 2)

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

Dominic Chell

4. Проверка на использование небезопасного API

Введение

Обычно приложения iOS применяют стандартный набор API для взаимодействия с серверами, локальными ресурсами и другими приложениями. Хотя многие из приложений реализуют опции безопасности по умолчанию, MDSec обнаружила много приложений, в которых не используются опции по умолчанию или в которых API попросту считается доверенным в плане безопасности операций. При выполнении аудита исходного кода в ходе проверок iOS-приложений следует рассмотреть следующие ключевые моменты.

4.1. Оценка безопасности передачи информации

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

Приложения iOS обычно взаимодействуют с онлайновыми веб-приложениями или основанными на RPC-механизмах веб-технологиями. Эти взаимодействия часто осуществляются с помощью класса NSURLConnection. Данный класс принимает объект NSURLRequest и производит посредством данного объекта HTTP(S)-запрос. API использует стандартный набор SSL-шифров для создания защищенных соединений; к сожалению, данный API недостаточно гранулирован, чтобы позволить разработчику выбирать какие шифры из набора использовать. Между SSL-транспортами, которые доступны в различных версиях SDK, существует несколько различий, которые сведены в следующую таблицу:

Версия SDKПротокол"Слабые" наборы шифровВсего наборов шифров
4.3TLS 1.0529
5.0TLS 1.2037
5.1TLS 1.2037

Таблица подчеркивает улучшение набора шифров в более новых версиях SDK.

Рассмотрим следующий пример, в котором создается простое HTTPS-соединение с localhost:

@implementation insecuressl
int main(int argc, const char* argv[])
{
NSString *myURL=@"https://localhost/test";
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL
URLWithString:myURL]];
NSURLResponse *resp = nil;
NSError *err = nil;
NSData *response = [NSURLConnection sendSynchronousRequest:
theRequest returningResponse: &resp error: &err];
NSString * theString = [[NSString alloc] initWithData:response
encoding:NSUTF8StringEncoding];
[resp release];
[err release];
return 0;
}

Если скомпилировать и запустить приложение для обоих SDK версий 5.0 или 5.1 и 4.3, то процесс обмена информацией будет различаться.

В SDK версии 4.3 приложение согласовывает сессию по протоколу TLS 1.0 c использованием одного из 29 наборов шифров, как показано на рисунках 4 и 5:

Рисунок 4 – SSL-согласование для SDK версии 4.3

Рисунок 5 – Шифры SSL для SDK версии 4.3

При использовании SDK версий 5.0 или 5.1, приложение согласовывает сессию по протоколу TLS 1.2 с использованием одного из 37 наборов шифров, как показано на рисунках 6 и 7:

Рисунок 6 – iOS 5.0/5.1 SSL Client Hello

Рисунок 7 – Наборы шифров для SDK версии 5.0/5.1

В показанном выше согласовании параметров для SDK 4.3 следующие наборы шифров считаются слабыми:

  • TLS_RSA_WITH_DES_CBC_SHA
  • TLS_RSA_EXPORT_WITH_RC4_MD5
  • TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
  • TLS_DHE_RSA_WITH_DES_CBC_SHA
  • TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA

Существенный шаг для предотвращения атак типа "человек-посередине" в iOS-приложениях – запрет на использование самоподписанных сертификатов. Стандартным поведением для класса NSURLRequest является отброс самоподписанных сертификатов и инициацию исключения NSURLErrorDomain. Однако нередко можно видеть, что разработчики перегружают данное поведение и позволяют принимать любые сертификаты, часто, чтобы разрешить использование самоподписанных сертификатов в тестовых средах. Проверка сертификата может быть отключена для запрошенного домена путем использования метода allowsAnyHTTPSCertificateForHost подобно тому, как это делается в следующем примере:

#import "loadURL.h"
@interface NSURLRequest (DummyInterface)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
+ (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host;
@end
@implementation loadURL
-(void) run
{
NSURL *myURL = [NSURL URLWithString:@"https://localhost/test"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:myURL
cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[myURL host]];
[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
}
@end

Метод allowsAnyHTTPSCertificateForHost является закрытым (private) методом, и его использование в коде может привести к тому, что приложение будет отвергнуто App Store. Альтернативный и нередко используемый способ обхода проверки SSL состоит в использовании селектора continueWithoutCredentialForAuthenticationChallenge, реализованного в рамках делегированного метода didReceiveAuthenticationChallenge, как показано ниже:

- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge.protectionSpace.authenticationMethod
isEqualToString:NSURLAuthenticationMethodServerTrust])
{
[challenge.sender useCredential:[NSURLCredential
credentialForTrust:challenge.protectionSpace.serverTrust]forAuthenticationChallen
ge:challenge];
[challenge.sender
continueWithoutCredentialForAuthenticationChallenge:challenge];
return;
}

Фреймворк CFNetwork предоставляет альтернативный API для реализации SSL. На самом деле этот фреймворк позволяет разработчику лучше контролировать и настраивать SSL-сессии. Как и в случае с NSURLRequest, нередко можно увидеть разработчиков, ослабляющих SSL-конфигурацию. CFNetwork, однако, предоставляет более детальный контроль, позволяя приложению принимать просроченные сертификаты или любые корневые сертификаты или даже пропускать проверку цепочки сертификатов.

Рассмотрим следующий делегированный метод onSocket, взятый из реального приложения:

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host
port:(UInt16)port {
NSMutableDictionary *settings = [[NSMutableDictionary alloc]
initWithCapacity: 3];
[settings setObject:[NSNumber numberWithBool:YES]
forKey:(NSString *)kCFStreamSSLAllowsExpiredCertificates];
[settings setObject:[NSNumber numberWithBool:YES]
forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];
[settings setObject:[NSNumber numberWithBool:NO]
forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];
[sock startTLS:settings];

К сожалению, при использовании фреймворка CFNetwork не существует четкого способа изменения набора шифров – вновь используется стандартный набор шифров SDK.

В заключение отметим, что для мобильных приложений нужно обязательно реализовывать транспортные методы безопасным образом, в режиме по умолчанию и используя последнюю версию SDK. Похоже, что при разработке приложения iOS эти требования обычно выполняются. Однако API позволяет ослаблять защиту передаваемых данных, и нередко можно видеть, как разработчики используют такую возможность. Разработчики, желающие ослабить безопасность передачи данных в целях разработки или тестирования должны убедиться, что данный код не останется в финальной версии. Для этого проще всего использовать макросы препроцессора, позволяющие включать определенный код только в тестовые сборки.

4.2. Злоупотребление обработчиками протоколов

Из-за ограничений, налагаемых песочницей iOS, межпроцессное взаимодействие (IPC) в системе обычно запрещено. Тем не менее, API поддерживает простую форму IPC, если приложение регистрирует специальный обработчик протокола.

Существует много причин, по которым разработчику может понадобиться поддержка IPC. Некоторые примеры, с которыми мы сталкивались на практике, включают определение наличия других приложений, позволяющих приложению запускаться из браузера Safari или передавать данные между приложениями.

Существует два метода API, которые обычно используются для реализации обработчиков протоколов в iOS: "application:openURL" и "application:handleOpenURL" (последний сейчас считается устаревшим). Преимущество использования метода "openURL" в том, что он поддерживает проверку приложения-источника, которое инициировало URL-запрос.

Специальная схема URL может быть зарегистрирована в приложении iOS путем добавления типа URL к файлу со списком свойств приложения (plist). Ниже показан проект VulnerableiPhoneApp, где регистрируется обработчик протокола "vuln":

Рисунок 8 – Регистрация IPC в XCode

Код, обработчика протокола можно реализовать с помощью делегированных методов приложения handleOpenURL или openURL подобно тому, как это сделано в следующем примере, который просто показывает окно сообщения с запрошенным URL.

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
UIAlertView *alertView;
NSString *text = [[url host]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
alertView = [[UIAlertView alloc] initWithTitle:@"Text" message:text
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
return YES;
}

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

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
if (!url) { return NO; }
NSString *method = [[url host]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if([method isEqualToString:@"setHomeURL"])
{
Settings *s = [[Settings alloc] init];
NSString *querystr = [[url query]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSArray *param = [querystr componentsSeparatedByString:@"="];
NSString *value = [param objectAtIndex:1];
[s setHomeURL:value];
}
return YES;

В данном примере специальный обработчик URL используется для обновления URL по умолчанию, который приложение открывает при старте. Данный метод принимает объект NSURL, который оно разбирает. Если передаваемый в объекте NSURL хост равен "setHomeURL", то будет вызван метод "setHomeURL" объекта Settings с аргументом, равным значению первого параметра URL.

Метод setHomeURL объекта Settings устанавливает настройки и реализован следующим образом:

@implementation Settings
- (void) setHomeURL:(NSString*)url
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:url forKey:@"homeURL"];
[prefs synchronize];
}

Атакующий может перенастроить стартовую страницу приложения с помощью вредоносного iframe, например, так:

<iframe src="vuln://setHomeURL?url=http://mdattacker.net">
</iframe>

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

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSString* myBid = [[NSBundle mainBundle] bundleIdentifier];
if ([sourceApplication isEqualToString:myBid])
{
return NO;
}
else if (!url) { return NO; }
NSString *method = [[url host]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if([method isEqualToString:@"setHomeURL"])
{
Settings *s = [[Settings alloc] init];
NSString *querystr = [[url query]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSArray *param = [querystr componentsSeparatedByString:@"="];
NSString *value = [param objectAtIndex:1];
[s setHomeURL:value];
}
return YES;

Как вариант, если разработчик желает убедиться, что URL может быть вызван только из другого приложения, например, Safari, возможна следующая реализация:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSString *SafariPath = @"/Applications/MobileSafari.app";
NSBundle *bundle = [NSBundle bundleWithPath:SafariPath];
if ([sourceApplication isEqualToString:[bundle bundleIdentifier]])
{
return No;
}

Пример уязвимости "из жизни" можно было найти в приложении Skype для iOS, которое регистрировало обработчик протокола "skype", используемый для создания чатов и звонков. Атака, позволяющая выполнить звонок без авторизации с помощью вредоносного iframe, была задокументирована Найтешем Даньяни [15]. Полезную нагрузку данной атаки можно вызвать из MobileSafari, чтобы запустить приложение Skype, которое выполняло бы звонок как показано ниже:

<iframe src="skype://123456789?call"></iframe>

Skype при этом показывал UIView, которое позволяло пользователю принять или отклонить звонок.

Простой метод определения корректности URL в приложениях AppStore – взять расшифрованное приложение и проверить строки, содержащие тип протокола. Вот пример для приложения Facebook (показана выборка из 558 URL):

bash-3.2# strings Facebook.app/Facebook | grep "://" | grep -v
"http"
fb://upload/actions/newalbum
fb://root
fb://birthdays
fb://messaging
fb://notifications
fb://requests
fb://publish
fb://publish/profile/(gatePublishWithUID:)
fb://oldpublish
fb://oldpublish/profile/(initWithUID:)
fb://publish/post/(initWithPostId:)
fb://publish/photo/(initWithUID:)/(aid:)/(pid:)
fb://publish/mailbox/(initWithFolder:)/(tid:)
fb://publish/privacy
fb://place/create
fb://compose
fb://compose/profile/(initWithUID:)

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

4.3. Нахождение небезопасного хранилища данных

Защита данных, хранимых на мобильном устройстве, - возможно одна из наиболее важных проблем, с которыми приходится сталкиваться разработчику приложений. Необходимо, чтобы разработчики тщательно защищали конфиденциальные данные, хранимые на стороне клиента. Как ранее отмечалось, разработчикам, желающим зашифровать конфиденциальное содержимое на устройстве, следует использовать Data Protection API. К сожалению, на практике можно нередко столкнуться даже с приложениями от транснациональных корпораций, хранящими конфиденциальные данные в незашифрованном виде. Хороший тому пример имел место в 2010 году, когда уязвимости приложения Citigroup для интернет-банкинга привели к исключению этого приложения из AppStore. The Register сообщал по этому поводу:

"В одном из писем гигант американского банкинга заявил, что приложение Citi Mobile сохраняло пользовательскую информацию в скрытом файле, который атакующие могли использовать для получения неавторизованного доступа к интернет-аккаунтам. Персональная информация, хранимая в данном файле, может включать в себя номера аккаунтов, платежи по счетам и коды доступа…" [16].

В данном документе внимание будет сосредоточено только на хранении данных и том, как приложения могут использовать Data Protection API. Более тщательно вопрос шифрования на iPhone изложен Жаном-Батистом Бедруном и Жаном Сигвалом из ESEC [17].

Хранить данные на стороне клиента можно в различных формах, включая, но не ограничиваясь следующими:

  • Специально созданные файлы,
  • Базы данных,
  • Системные логи,
  • Хранилища куки,
  • Списки свойств (Plist),
  • Кэш данных.

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

Приложения хранятся в файловой системе от имени пользователя "mobile" в директории "/var/mobile/Applications". Для хранения данных приложения используется поддиректория, имя которой совпадает с уникальным идентификатором GUID. Так выглядит структура директории приложений:

ПапкаОписание
Application.appХранит статическое содержимое приложения и скомпилированное приложение. Это содержимое подписано и проверяется во время выполнения.
DocumentsПостоянное хранилище для данных приложения. Эти данные можно синхронизировать и резервировать на iTunes.
LibraryДанная папка содержит поддерживающие данные, используемые приложением, вроде настроек, предпочтений, кэша и куки.
tmpДанная папка используется для временных файлов.

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

Давайте взглянем на приложение из реального мира, взятое с AppStore. Kik Messenger – это приложение для социальных сетей с рейтингом 4+ звезды/6405 голосов с AppStore и используется более чем миллионом пользователей. Данное приложение позволяет пользователям отправлять бесплатные мгновенные сообщения через соединения устройства. Чтобы иметь такую возможность, пользователь должен бесплатно зарегистрировать Kik-аккаунт.

В папке приложения Kik есть список предпочтений – "Library/Preferences/com.kik.chat.plist", который используется приложением для хранения настроек, включая имя пользователя, пароль и адрес электронной почты, как показано ниже (обведено красным):

Рисунок 9 - Файл настроек Kik Messenger

Список свойств (plist = property list), показанный выше, не защищен Data Protection API и поэтому остается незашифрованным в файловой системе, пока устройство включено, независимо от состояния блокировки. Это классический пример некорректного использования хранилища данных: конфиденциальная информация вроде учетных данных должна храниться в цепочке ключей, а не в обычном списке свойств в файловой системе.

В дополнение к вышесказанному, Kik хранит на устройстве и другие данные, включая историю SMS-чатов и контактную информацию. Эти данные хранятся в базе данных sqlite, "Documents/kik.sqlite", которая опять же не зашифрована.

Рисунок 10 – SQLite база данных Kik

Одна из распространенных проблем, с которой сталкивается и приложение Kik, заключается в том, что приложение получает сообщения в реальном времени, находясь в фоне, независимо от состояния блокировки. Если приложение установило уровень защиты SQLite-хранилища в NSFileProtectionComplete, то не сможет обращаться к этому хранилищу, когда телефон заблокирован. С этой проблемой можно отчасти справиться, путем шифрования данных до первой разблокировки телефона, установив константу NSFileProtectionCompleteUntilFirstUserAuthentication. Последующая перезагрузка приведет к тому, что данные вновь окажутся зашифрованы. Однако, данная возможность доступна только начиная с iOS 5.

Приложение Kik также позволяет пользователям посылать вместе с мгновенными сообщениями вложения вроде фотографий. Эти вложения хранятся в незашифрованном виде в папке "Documents/fileAttachments". Например, так можно посмотреть фотографию, вложенную в сообщение:

mbp:Documents $ file fileAttachments/057a8fc9-0daf-4750-b356-5b28755f4ec4
fileAttachments/057a8fc9-0daf-4750-b356-5b28755f4ec4: JPEG image data, JFIF
standard 1.01 mbp:Documents $

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

Data Protection API предоставляет четыре уровня защиты файловой системы, которые настраиваются путем передачи расширенного атрибута классам NSData или NSFileManager. Вот возможные уровни защиты:

УровеньОписание
No Protection Файл не шифруется.
Complete Protection Файл шифруется и недоступен, когда устройство заблокировано.
Complete Unless Open Файл шифруется файловой системой и недоступен, пока закрыт. Когда устройство разблокируется, приложение может сохранять открытый хэндл на файл, даже если устройство позже будет заблокировано, однако в течение этого времени файл не будет зашифрован.
Complete Until First User Authentication Файл шифруется и недоступен, пока устройство не будет разблокировано первый раз. Это помогает защититься от атак, нуждающихся в перезагрузке устройства.

Чтобы применить один из перечисленных выше уровней защиты, соответствующему классу нужно передать один из следующих атрибутов:

NSDataNSFileManager
NSDataWritingFileProtectionNone NSFileProtectionNone
NSDataWritingFileProtectionComplete NSFileProtectionComplete
NSDataWritingFileProtectionCompleteUnlessOpen NSFileProtectionCompleteUnlessOpen
NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication NSFileProtectionCompleteUntilFirstUserAuthentication

Например, рассмотрим приложение, которому нужно сохранить в файловой системе некоторые данные, но которому не нужен доступ к файлу, когда устройство заблокировано. Это может быть приложение, которое позволяет вам загружать документы и просматривать их позднее. Поскольку приложению не нужен доступ к файлам, когда устройство заблокировано, оно может использовать преимущества полной защиты, установив атрибуты NSDataWritingFileProtectionComplete или NSFileProtectionComplete:

-(BOOL) getFile
{
NSString *fileURL = @"http://www.mdsec.co.uk/training/wahh-live.pdf";
NSURL *url = [NSURL URLWithString:fileURL];
NSData *urlData = [NSData dataWithContentsOfURL:url];
if ( urlData )
{
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:@"%@/%@",
documentsDirectory,@"wahh-live.pdf"];
NSError *error = nil;
[urlData writeToFile:filePath options:NSDataWritingFileProtectionComplete
error:&error];
return YES;
}
return NO;
}

В данном сценарии документ будет доступен только когда устройство разблокируется. Операционная система предоставляет десятисекундное окно между блокировкой устройства и моментом, когда файл будет заблокирован. Следующий листинг показывает попытку обращения к файлу, когда устройство блокировано:

mdsec-iPhone:/var/mobile/Applications/7F5ED565-781E-47FD-8787-
4C76CD7A4DD5 root#
ls -al Documents/ total 372
drwxr-xr-x 2 mobile mobile 102 Jan 20 15:24 ./
drwxr-xr-x 6 mobile mobile 204 Jan 20 15:23 ../
-rw-r--r-- 1 mobile mobile 379851 Jan 20 15:24 wahh-live.pdf
mdsec-iPhone:/var/mobile/Applications/7F5ED565-781E-47FD-8787-4C76CD7A4DD5 root#
strings Documents/wahh-live.pdf
strings: can't open file: Documents/wahh-live.pdf (Operation not permitted)
mdsec-iPhone:/var/mobile/Applications/7F5ED565-781E-47FD-8787-4C76CD7A4DD5 root#

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

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

4.4. Атака связки ключей iOS

Связка ключей iOS – это зашифрованный контейнер, используемый для хранения конфиденциальных данных (учетных данных, например). Приложения могут обращаться только к своим собственным элементам связки ключей, если не являются членами группы доступа к связке ключей. К элементам связки ключей может применяться уровень защиты Data Protection API, как и к файлам из файловой системы. Следующая таблица описывает доступные уровни защиты для элементов связки ключей:

АтрибутОписание
kSecAttrAccessibleAlways Элемент связки доступен всегда.
kSecAttrAccessibleWhenUnlocked Элемент связки доступен только когда устройство разблокировано.
kSecAttrAccessibleAfterFirstUnlock Элемент связки доступен только после первой разблокировки с момента загрузки. Это помогает защититься от атак, нуждающихся в перезагрузке устройства.
kSecAttrAccessibleAlwaysThisDeviceOnly Элемент связки всегда доступен, но не может быть перенесен на другие устройства.
kSecAttrAccessibleWhenUnlockedThisDeviceOnly Элемент связки доступен только когда устройство разблокировано и не может быть перенесен на другие устройства.
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly Элемент связки доступен только после первой разблокировки с момента загрузки и не может быть перенесен на другие устройства.

Элементы связки ключей могут добавляться с помощью метода SecItemAdd и обновляться с помощью метода SecItemUpdate, которые принимают один из вышеперечисленных атрибутов для опеределения применяемого уровня защиты. По умолчанию все элементы связки создаются с уровнем защиты kSecAttrAccessibleAlways, который позволяет получать к ним доступ в любое время и переносить их на другие устройства.

Доступ приложений к связке ключей ограничен их правами. Связка ключей использует идентификаторы приложения, хранимые в элементе "keychain-access-group" инициализирующего профиля приложения. Пример инициализирующего профиля, который разрешает доступ только к собственным элементам связке ключей приложения, показан ниже:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>my.company.VulnerableiPhoneApp</string>
<key>get-task-allow</key>
<true/>
<key>keychain-access-group</key>
<array>
<string>my.company.VulnerableiPhoneApp</string>
</array>
</dict>
</plist>

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

- (NSMutableDictionary *)getkeychainDict:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword, (id)kSecClass,
service,(id)kSecAttrService, service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleWhenUnlocked, (id)kSecAttrAccessible, nil];
}

- (BOOL) saveLicense:(NSString*)licenseKey {
static NSString *serviceName = @"my.company.VulnerableiPhoneApp";
NSMutableDictionary *myDict = [self getkeychainDict:serviceName];
SecItemDelete((CFDictionaryRef)myDict);
NSData *licenseData = [licenseKey dataUsingEncoding:NSUTF8StringEncoding];
[myDict setObject:[NSKeyedArchiver archivedDataWithRootObject:licenseData]
forKey:(id)kSecValueData];
OSStatus status = SecItemAdd((CFDictionaryRef)myDict, NULL);
if (status == errSecSuccess) return YES;
return NO;
}

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

На самом деле, связка ключей – просто база данных SQLite, к которой можно делать запросы так же, как и к любой другой базе данных. Например, чтобы получить список групп связки ключей, можно выполнить следующий запрос:

mdsec-iPhone:/var/Keychains root# sqlite3 keychain-2.db "select agrp from
genp"
apple
apple
apple
ichat
com.apple.apsd
apple
apple
T84QZS65DQ.platformFamily
T84QZS65DQ.platformFamily
apple
apple
my.company.VulnerableiPhoneApp
mdsec-iPhone:/var/Keychains root#

На телефоне, подвергшемся джейлбрейку, возможно выгрузить все элементы связки ключей для любого приложения с теми же условиями, которые были описаны при обсуждении Data Protection API. Это можно сделать, создав приложение, которое входит во все подходящие группы доступа к связке ключей, и выполнив запрос к службе связки ключей для получения защищенных элементов [18].

4.5. Проведение атак типа Межсайтовый скриптинг (XSS) через UIWebView

UIWebView – это движок iOS для отображения текста, который поддерживает большое количество разных файловых форматов, включая:

  • HTML
  • PDF
  • RTF
  • Офисные документы (doc, xls, ppt)
  • Документы iWork (Pages, Numbers и Keynote)

UIWebView построен на базе WebKit и использует те же базовые фреймворки, что Safari и MobileSafari. Поэтому UIWebView – это также веб-браузер, то есть, может быть использован для получения и отображения удаленного контента. Как и ожидается от веб-браузера, UIWebView также поддерживает JavaScript, позволяющий приложениям выполнять динамические скрипты на стороне клиента. Стоит, однако, отметить, что в API нет опции, позволяющей отключить данную возможность. Поэтому приложения iOS могут стать жертвами межсайтового скриптинга, как традиционные веб-приложения.

Межсайтовый скриптинг в приложениях iOS часто может иметь более тяжелые последствия, чем традиционные XSS-атаки вроде воровства сессии, поскольку разработчики обычно обнажают нативный функционал iOS, реализуя мост между JavaScript и Objective-C. MDSec сталкивалась на практике с примерами, когда через JavaScript делались фотографии, запрашивалась информация о географическом положении и посылались SMS/E-mail. Межсайтовый скриптинг может произойти в iOS везде, где введенные пользователем данные слепо передаются UIWebView без обработки. Это часто происходит, когда разработчику нужно использовать в UIWebView контролируемую пользователем переменную Objective-C. Приложение Skype для iOS имело подобную уязвимость при отображении полного имени пользователя при входящем вызове. Приложение Skype использовало локальный HTML-файл как шаблон для UIWebView, не обрабатывая полное имя пользователя при входящем звонке. В данном случае атакующий мог получить доступ к локальной файловой системе из-за того, что файл загружался в локльном контексте. Обнаружение данной уязвимости привело к разработке демонстрационного эксплоита для получения и скачивания адресной книги устройства [19].

Рассмотрим следующий простой пример, где имя пользователя из переменной Objective-C добавляется в объектную модель документа (DOM) UIWebView:

NSString *javascript = [[NSString alloc] initWithFormat:@"var myvar=\"%
@\";",
username];
[mywebView stringByEvaluatingJavaScriptFromString:javascript];
[mywebView loadRequest:[NSURLRequest requestWithURL:[NSURL
fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"index"
ofType:@"html"]isDirectory:NO]]];

Сначала имя пользователя добавляется к объекту NSString, который содержит JavaScript. Затем данный объект добавляется в DOM веб-представления с помощью метода "stringByEvaluatingJavaScriptFromString". В данном случае имеет место не только межсайтовый скриптинг из-за непосредственного выполнения JavaScript из UIWebView: переменная также заносится в локальный HTML-файл, хранимый в папке приложения:

<html>
<p>
Cross-Site Scripting in UIWebView:
</p>
<p>
This is an example of XSS:
<script>document.write(myvar);</script>
</p>
</html>

Как и в случае с традиционными XSS-атаками, чтобы предотвратить XSS в iOS-приложениях, нужно тщательно обрабатывать данные, передающиеся в приложение, и обеспечивать правильную кодировку данных при их представлении в UIWebView.

4.6. Атака XML-процессоров

XML широко используется в мобильных приложениях для представления данных, и iPhone SDK предоставляет два инструмента для разбора XML: NSXMLParser и libxml2. Также существует множество популярных парсеров от третьих фирм.

Распространенная атака, часто связываемая с XML-парсерами, называется "billion laughs" [20]. В ходе этой атаки парсеру передается множество вложенных сущностей, которые при раскрытии могут вызвать отказ в обслуживании. Парсеры, включенные в iOS SDK по умолчанию, не уязвимы к данной атаке: когда обнаруживаются вложенные сущности, NSXMLParser бросает исключение NSXMLParserEntityRefLoopError, а libxml2 сообщает об ошибке "Detected an entity reference loop".

Другой распространенный сценарий атаки на XML-парсеры – разбор внешних XML-сущностей. Разбор внешних XML-сущностей не включен по умолчанию в NSXMLParser, но включен по умолчанию в LibXML2. Чтобы включить разбор внешних сущностей в NSXMLParser, разработчик должен установить опцию setShouldResolveExternalEntities, которая приводит к запуску делегированного метода foundExternalEntityDeclarationWithName при обнаружении таких сущностей.

Простая уязвимая реализация NSXMLParser выглядит примерно так:

#import "XMLParser.h"
@implementation XMLParser
- (void)parseXMLStr:(NSString *)xmlStr {
BOOL success;
NSData *xmlData = [xmlStr dataUsingEncoding:NSUTF8StringEncoding];
NSXMLParser *addressParser = [[NSXMLParser alloc] initWithData:xmlData];
[addressParser setDelegate:self];
[addressParser setShouldResolveExternalEntities:YES];
success = [addressParser parse];
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {}
- (void)parser:foundExternalEntityDeclarationWithName:publicID:systemID {}

- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
NSLog(@"Error %i, Description: %@", [parseError code],
[[parser parserError] localizedDescription]); }
@end

Разработчик включил разбор внешних сущностей путем установки опции парсера "setShouldResolveExternalEntities". Когда данная реализация вызывается, парсер попытается разрешить сущность:

NSString *xmlStr = @"<?xml version=\"1.0\" encoding=
\"ISO-8859-1\"?>\
<!DOCTYPE foo [ \
<!ELEMENT foo ANY > \
<!ENTITY xxe SYSTEM \"http://192.168.0.7/hello\"> \
]> \
<foo>&xxe;</foo>";

XMLParser *xp = [[XMLParser alloc] init];
[xp parseXMLStr:xmlStr];

При попытке разрешить данную сущность, парсер сделает HTTP-запрос от устройства к веб-серверу:

bash-3.2# nc -lvp 80
listening on [any] 80 ...
192.168.0.2: inverse host lookup failed: Unknown host connect to [192.168.0.7]
from (UNKNOWN) [192.168.0.2] 49287 GET /hello HTTP/1.0
Host: 192.168.0.7
Accept-Encoding: gzip

Реализация данного вектора атаки в случае парсера libxml2 может выглядеть так:

#import <libxml/xmlmemory.h>
@implementation LibXml2
-(BOOL) parser:(NSString *)xml {
xmlDocPtr doc = xmlParseMemory([xml UTF8String], [xml
lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);

xmlNodePtr root = xmlDocGetRootElement(doc);
}
@end

В данном примере внешняя сущность разбирается при передаче методу "xmlParseMemory" XML-строки. В результате с устройства будет установлено исходящее HTTP-соединение. Разработчику следует знать, что при других обстоятельствах таким образом можно открывать и локальные файлы, используя обработчик протокола file:// (в рамках ограничений, установленных песочницей).

Домашний Wi-Fi – ваша крепость или картонный домик?

Узнайте, как построить неприступную стену