Безопасность IOS-приложений (часть 24) – Методы обнаружение джейлбрейка на устройстве и способы обхода этих методов

Безопасность IOS-приложений (часть 24) – Методы обнаружение джейлбрейка на устройстве и способы обхода этих методов

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

Автор: Пратик Джианчандани (Prateek Gianchandani)

В этой статье мы рассмотрим способы обнаружения на уровне приложения, присутствует ли джейлбрейк на устройстве. Подобная проверка во многих случаях может быть весьма полезной. Как мы уже знаем, злоумышленник может воспользоваться утилитами наподобие Cycript, GDB или Snoop-it для выполнения динамического анализа и кражи конфиденциальной информации, используемой приложением. Существуют методы повышения безопасности приложения, когда вы не разрешаете его запуск на джейлбрейковом устройстве. Однако следует отметить, что устройствами с джейлбрейком пользуются миллионы пользователей и, следовательно, подобного рода защиты значительно сократят размер вашей целевой аудитории. Вместо полного запрета на запуск приложения, можно отключить только некоторые функции. В этой статье мы также рассмотрим, как обойти подобную проверку при помощи Cycript.

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

1 NSString *filePath = @"/Applications/Cydia.app";
2 if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
3 {
4 //Device is jailbroken
5 }

Однако не на всех джейлбрейковых устройства установлена Cydia. Кроме того, взломщик может изменить месторасположение этого пакета. В этом случае более эффективна проверка на присутствие множества других приложений и файлов. Например, можно проверить присутствие фреймворка Mobile Substrate, который также установлен на множестве устройств, где есть джейлбрейк. Кроме того, можно проверить присутствие демона SSH или интерпретатора shell. Все вышеупомянутые проверки выполняет следующий код:

1 +(BOOL)isJailbroken{
2
3 if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]){
4 return YES;
5 }else if([[NSFileManager defaultManager] fileExistsAtPath:@"/Library/MobileSubstrate/MobileSubstrate.dylib"]){
6 return YES;
7 }else if([[NSFileManager defaultManager] fileExistsAtPath:@"/bin/bash"]){
8 return YES;
9 }else if([[NSFileManager defaultManager] fileExistsAtPath:@"/usr/sbin/sshd"]){
10 return YES;
11 }else if([[NSFileManager defaultManager] fileExistsAtPath:@"/etc/apt"]){
12 return YES;
13 }
14 return NO;
15}

Из предыдущих статей мы также знаем, что приложения, запускаемые под пользователем «mobile», работают внутри песочницы и находятся в директории /var/mobile/Applications, а приложения, которые запускаются от имени суперпользователя (root) находятся в директории /Applications. Таким образом, пользователь на джейлбрейковом устройстве может установить приложение в папку /Applications и тем самым наделить его привилегиями суперпользователя. Исходя из этого, мы можем проверить, работает ли приложение внутри песочницы или у него есть более широкие права, путем тестовой модификации файла вне директории, где установлено приложение.

1 NSError *error;
2 NSString *stringToBeWritten = @"This is a test.";
3 [stringToBeWritten writeToFile:@"/private/jailbreak.txt" atomically:YES
4 encoding:NSUTF8StringEncoding error:&error];
5 if(error==nil){
6 //Device is jailbroken
7 return YES;
8 } else {
9 //Device is not jailbroken
10 [[NSFileManager defaultManager] removeItemAtPath:@"/private/jailbreak.txt" error:nil];
11 }

Как было сказано выше, опытный злоумышленник может изменить местонахождение приложения, однако даже в этом случае мы можем узнать, установлена ли Cydia на устройстве, поскольку злоумышленник, скорее всего, не изменит схему URL, которая используется в Cydia. Если вызов URL-схемы (cydia://) из вашего приложения завершился успешно, можно быть уверенным в том, что устройство джейлбрейковое.

1 if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:
@"cydia://package/com.example.package"]]){
2 //Device is jailbroken
3 }

Также добавим условие, при котором код не будет выполняться в том случае, если приложение тестируется в симуляторе. Объединяя все вышеперечисленные техники, получаем следующий метод:

+(BOOL)isJailbroken{

#if !(TARGET_IPHONE_SIMULATOR)

if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@"/Library/MobileSubstrate/MobileSubstrate.dylib"]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@"/bin/bash"]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@"/usr/sbin/sshd"]){
return YES;
}else if([[NSFileManager defaultManager] fileExistsAtPath:@"/etc/apt"]){
return YES;
}

NSError *error;
NSString *stringToBeWritten = @"This is a test.";
[stringToBeWritten writeToFile:@"/private/jailbreak.txt" atomically:YES
encoding:NSUTF8StringEncoding error:&error];
if(error==nil){
//Device is jailbroken
return YES;
} else {
[[NSFileManager defaultManager] removeItemAtPath:@"/private/jailbreak.txt" error:nil];
}

if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]]){
//Device is jailbroken
return YES;
}
#endif

//All checks have failed. Most probably, the device is not jailbroken
return NO;
}

Честно сказать, на данный момент не существует метода, который определял бы джейлбрейк на устройстве в 100% случаев. Опытный взломщик всегда сможет найти способ обхода всех этих проверок. Кроме того, он попросту может найти соответствующие инструкции в бинарном файле и заменить их NOP’ами. Он также может изменить логику работы вашего метода на свою собственную при помощи Cycript.

Вначале злоумышленник может найти соответствующий класс, используя утилиту Class-dump-z. Далее он найдет метод +(BOOL)isJailbroken, который относится к классу JailbreakDetector. Обратите внимание, это так называемый классовый метод, поскольку вначале него стоит префикс «+». Само собой из названия метода очевидно, что он отвечает за проверку устройства на джейлбрейк, и возвращает YES, если устройство джейлбрейковое. Если вы не знакомы с этими концепциями, обращайтесь к предыдущим статьям из этой серии.

Рисунок 1: После получения информации о классах, находим метод, который проверяет присутствие джейлбрейка на устройстве

Затем злоумышленник может подцепиться к приложению при помощи Cycript

Рисунок 2: Подключение к приложению при помощи Cycript

После этого взломщик получает перечень методов класса JailbreakDetector. Обратите внимание, что мы получаем этот перечень, используя конструкцию JailbreakDetector->isa.messages, поскольку isJailbroken – классовый метод. Для нахождения методов экземпляра класса следует использовать конструкцию JailbreakDetector.messages.

Рисунок 3: Перечень методов класса JailbreakDetector

После того, как список методов получен, злоумышленник может изменить реализацию метода (используя технику method swizzling), где вместо YES будет возвращаться NO. Если вы не знакомы с этой техникой, рекомендую изучить восьмую статью из этой серии.

Рисунок 4: Изменение логики работы метода

Мы, как разработчики, можем усложнить жизнь взломщику, если будем использовать такие имена методов, которые не привлекают внимание. Например, класс JailbreakDetector, можно переименовать в ColorAdditions, а метод +(BOOL)isJailbroken в +(BOOL)didChangeColor. На подобные имена злоумышленник не обратит внимания. Конечно, он всегда может посмотреть вызовы внутри этого метода, используя Snoop-it, GDB и другие утилиты, но это маленькое изменение вполне может затруднить ему жизнь. 

Ваша приватность умирает красиво, но мы можем спасти её.

Присоединяйтесь к нам!