Télécharger le PDF : 2022_C00C00R00CON_jailbreak.pdf
Sans JailBreak
Avec ptrace (lldb / frida) → l'application a besoin du droit get-task-allow
En injectant du code Frida → l'application doit être reconditionnée
Dans les deux cas, vous devez renoncer à la candidature
mais cela a beaucoup d'effets secondaires :
ID d'équipe différent
Les fichiers sont modifiés
Avec une évasion de prison
Des mécanismes de détection de jailbreak sont ajoutés à la défense d’ingénierie inverse pour rendre plus difficile l’exécution de l’application sur un appareil jailbreaké. Cela bloque certains des outils et techniques que les rétro-ingénieurs aiment utiliser. Comme la plupart des autres types de défense, la détection du jailbreak n’est pas très efficace en soi, mais la dispersion des contrôles dans le code source de l’application peut améliorer l’efficacité du système global de lutte contre la falsification.
Les risques de sécurité d'entreprise posés par le jailbreak s'ajoutent aux outils qui peuvent aider les utilisateurs à échapper facilement aux méthodes courantes de détection de jailbreak. Un utilisateur peut télécharger n’importe quelle application comme Spotify++ directement depuis la boutique d’applications tierce Cydia. De nombreuses applications proposent une détection de jailbreak, tout comme de nombreuses applications de médias et de services financiers qui souhaitent respectivement limiter le piratage de contenu et la compromission de compte. Malheureusement, ces détections de jailbreak reposent sur une combinaison de tests relativement simples et évitables, tels que :
La limite fondamentale de ces tests de détection et d’autres tests comparables est qu’en tant que tests côté client, ils peuvent être consultés, rétro-conçus et évités par les attaquants. De plus, les applications effectuant ces tests de détection de jailbreak (par exemple l'application MDM) doivent passer par le processus d'examen des applications d'Apple, ce qui limite la portée des données qu'elles peuvent collecter pour analyser l'état de jailbreak d'un appareil. Jetons-y un coup d'œil rapide :
Recherchez les fichiers et répertoires généralement associés aux jailbreaks, tels que :
//suspicious system and app paths to check
private static var suspicousAppandSystemPaths : [ String ] {
return [
" /usr/sbin/frida-server " ,
" /etc/apt/sources.list.d/electra.list " ,
" /etc/apt/sources.list.d/sileo.sources " ,
" /.bootstrapped_electra " ,
" /usr/lib/libjailbreak.dylib " ,
" /jb/lzma " ,
" /.cydia_no_stash " ,
" /.installed_unc0ver " ,
" /jb/offsets.plist " ,
" /usr/share/jailbreak/injectme.plist " ,
" /etc/apt/undecimus/undecimus.list " ,
" /var/lib/dpkg/info/mobilesubstrate.md5sums " ,
" /Library/MobileSubstrate/MobileSubstrate.dylib " ,
" /jb/jailbreakd.plist " ,
" /jb/amfid_payload.dylib " ,
" /jb/libjailbreak.dylib " ,
" /usr/libexec/cydia/firmware.sh " ,
" /var/lib/cydia " ,
" /etc/apt " ,
" /private/var/lib/apt " ,
" /private/var/Users/ " ,
" /var/log/apt " ,
" /Applications/Cydia.app " ,
" /private/var/stash " ,
" /private/var/lib/apt/ " ,
" /private/var/lib/cydia " ,
" /private/var/cache/apt/ " ,
" /private/var/log/syslog " ,
" /private/var/tmp/cydia.log " ,
" /Applications/Icy.app " ,
" /Applications/MxTube.app " ,
" /Applications/RockApp.app " ,
" /Applications/blackra1n.app " ,
" /Applications/SBSettings.app " ,
" /Applications/FakeCarrier.app " ,
" /Applications/WinterBoard.app " ,
" /Applications/IntelliScreen.app " ,
" /private/var/mobile/Library/SBSettings/Themes " ,
" /Library/MobileSubstrate/CydiaSubstrate.dylib " ,
" /System/Library/LaunchDaemons/com.ikey.bbot.plist " ,
" /Library/MobileSubstrate/DynamicLibraries/Veency.plist " ,
" /Library/MobileSubstrate/DynamicLibraries/LiveClock.plist " ,
" /System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist " ,
" /Applications/Cydia.app " ,
" /Applications/blackra1n.app " ,
" /Applications/FakeCarrier.app " ,
" /Applications/Icy.app " ,
" /Applications/IntelliScreen.app " ,
" /Applications/MxTube.app " ,
" /Applications/RockApp.app " ,
" /Applications/SBSettings.app " ,
" /Applications/WinterBoard.app "
]
}
Le plus souvent, ceux-ci sont vérifiés à l'aide du
-(BOOL)fileExistsAtPath:(NSString*)
méthode de chemin dans NSFileManager ou FileManager.default.fileExists(atPath: path)
. Cependant, il existe également des applications qui utilisent des fonctions C de niveau inférieur comme fopen(), stat() ou access().
Une autre façon de vérifier les mécanismes de jailbreak consiste à essayer d'écrire dans un emplacement situé en dehors du bac à sable de l'application. Vous pouvez le faire en demandant à l'application de tenter de créer un fichier, par exemple, dans le répertoire /private
. Si le fichier est créé avec succès, l'appareil a été jailbreaké.
Cette méthode consiste à vérifier les autorisations de fichiers et répertoires spécifiques sur le système. Plus de répertoires ont un accès en écriture sur un appareil jailbreaké que sur un appareil non compromis. Un exemple de ceci est la partition racine, qui à l'origine ne dispose que d'une autorisation de lecture. S'il est trouvé, il dispose d'autorisations de lecture et d'écriture, cela signifie que l'appareil est jailbreaké. Il existe différentes manières d'effectuer ces vérifications, par exemple en utilisant NSFileManager et des fonctions C comme statfs().
Rapide:
do {
let pathToFileInRestrictedDirectory = " /private/jailbreak.txt "
try " This is a test. " . write ( toFile : pathToFileInRestrictedDirectory , atomically : true , encoding : String . Encoding . utf8 )
try FileManager . default . removeItem ( atPath : pathToFileInRestrictedDirectory )
// Device is jailbroken
} catch {
// Device is not jailbroken
}
Objectif-C :
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 {
// Device is not jailbroken
[[ NSFileManager defaultManager ] removeItemAtPath: @" /private/jailbreak.txt " error: nil ];
}
Il existe différentes manières d'effectuer ces vérifications, par exemple en utilisant NSFileManager et des fonctions C telles que statfs(), open(), utimes(), stat(), pathconf(), stat64(), fopen().
Vous pouvez vérifier les gestionnaires de protocole en essayant d'ouvrir une URL Cydia. La boutique d'applications Cydia, que pratiquement tous les outils de jailbreak installent par défaut, installe le gestionnaire de protocole cydia://.
Rapide:
if let url = URL ( string : " cydia://package/com.example.package " ) , UIApplication . shared . canOpenURL ( url ) {
// Device is jailbroken
}
Objectif-C :
if ([[UIApplication sharedApplication ] canOpenURL: [ NSURL URLWithString: @" cydia://package/com.example.package " ]]){
// Device is jailbroken
}
Ces vérifications simples peuvent être consultées, rétro-conçues et éludées par les attaquants.
ptrace ( PT_DENY_ATTACH );
void try_kill () {
const int pid = getpid ();
int ret = kill (pid, 0 );
}
D'après la page de manuel de kill ( man 2 kill
), le signal 0
est utilisé pour vérifier que le pid
donné dans le premier paramètre existe réellement.
[…] Une valeur de 0, cependant, entraînera une vérification des erreurs (sans qu'aucun signal ne soit envoyé). Cela peut être utilisé pour vérifier la validité du pid.
Cette opération de mise à mort est suivie d'une vérification PTRACE
:
inline bool ptrace_detect () {
int32_t opt[ 4 ] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID,
getpid (),
};
kinfo_proc info;
sysctl (opt, 4 , &info, sizeof (kinfo_proc), nullptr , 0 );
return info. kp_proc . p_flag & P_TRACED;
}
getppid() == 1
getfsstat64(), statvfs()
fcntl(F_ADDSIGS)
csops(CS_OPS_MARKKILL)
Nous pouvons vérifier l'intégrité de la signature de notre binaire. Cette vérification commence par ouvrir le binaire principal de l'application à partir du disque, rechercher jusqu'à la séquence kSecCodeMagicEmbeddedSignature 0xfade0cc0
, lire les droits et calculer la somme de contrôle.
Le segment de signature de code contient une structure superbe, qui elle-même contient d'autres blobs de types différents. La structure des deux est définie dans du code Apple plus open source :
/*
* Structure of an embedded-signature SuperBlob
*/
typedef struct __BlobIndex {
uint32_t type; /* type of entry */
uint32_t offset; /* offset of entry */
} CS_BlobIndex;
typedef struct __SuperBlob {
uint32_t magic; /* magic number */
uint32_t length; /* total length of SuperBlob */
uint32_t count; /* number of index entries following */
CS_BlobIndex index[]; /* (count) entries */
/* followed by Blobs in no particular order as indicated by offsets in index */
} CS_SuperBlob;
Le super blob contient juste un peu de magie, la longueur de toute la section de signature de code, un nombre de blobs et un tableau d'index pour ces blobs. Les différentes magies d'identification du blob sont définies dans ce fichier :
/*
* Magic numbers used by Code Signing
*/
enum {
kSecCodeMagicRequirement = 0xfade0c00 , /* single requirement */
kSecCodeMagicRequirementSet = 0xfade0c01 , /* requirement set */
kSecCodeMagicCodeDirectory = 0xfade0c02 , /* CodeDirectory */
kSecCodeMagicEmbeddedSignature = 0xfade0cc0 , /* single-architecture embedded signature */
kSecCodeMagicDetachedSignature = 0xfade0cc1 , /* detached multi-architecture signature */
kSecCodeMagicEntitlement = 0xfade7171 , /* entitlement blob */
};
Par exemple, le binaire iOS ps
comporte quatre blobs de contenu suivant le superlob initial :
Il s’agit respectivement :
0xfade0c02
), que vous pouvez repérer car il contient le nom binaire qualifié com.apple.ps
.0xfade0c01
).0xfade7171
).0xfade0b01
).Essayez d'arrêter l'application après un long délai d'attente et ne quittez pas directement le lieu de détection du jailbreak.
fork()
Processus de bifurcation. Sandboxd ne refuse pas aux applications la possibilité d'utiliser fork(), popen() ou toute autre fonction C pour créer des processus enfants sur des appareils jailbreakés. Cependant, sandboxd refuse explicitement la bifurcation du processus sur les appareils non jailbreakés. Par conséquent, en vérifiant le pid renvoyé sur fork(), une application peut savoir si un périphérique est compromis. Si le fork réussit, l’application peut en déduire qu’elle s’exécute sur un appareil jailbreaké.
system()
Appeler la fonction system() avec un argument NULL sur un appareil non jailbreaké renverra 0. Faire la même chose sur un appareil jailbreaké renverra 1. En effet, la fonction vérifiera si /bin/sh existe, et il n'existe que sur appareils jailbreakés.
Connexions de bouclage SSH. En raison de la très grande partie des appareils jailbreakés sur lesquels OpenSSH est installé, certaines applications tentent d'établir une connexion à 127.0.0.1 sur le port 22. Si la connexion réussit, cela signifie qu'OpenSSH est installé et en cours d'exécution, ce qui prouve que l'appareil est jailbreaké.
Vérifiez le bouclage pour 22 (OpenSSH) et 44 (checkra1n) ports ouverts.
Cette méthode de détection commence par appeler des fonctions telles que _dyld_image_count()
et _dyld_get_image_name()
pour voir quels dylibs sont actuellement chargés. Cette méthode est très difficile à patcher dynamiquement car les patchs eux-mêmes font partie des dylibs.
/usr/lib/substitute-inserter.dylib
par exemple
Peut utiliser dlopen / analyse de mémoire / structures internes dyld, etc.
private static func checkDYLD ( ) -> Bool {
let suspiciousLibraries = [
" FridaGadget " ,
" frida " ,
" cynject " ,
" libcycript "
]
for libraryIndex in 0 ..< _dyld_image_count ( ) {
guard let loadedLibrary = String ( validatingUTF8 : _dyld_get_image_name ( libraryIndex ) ) else { continue }
for suspiciousLibrary in suspiciousLibraries {
if loadedLibrary . lowercased ( ) . contains ( suspiciousLibrary . lowercased ( ) ) {
return true
}
}
}
return false
}
Vérifier l'intégrité du code
Essayez de détecter Frida
private static func isFridaRunning ( ) -> Bool {
func swapBytesIfNeeded ( port : in_port_t ) -> in_port_t {
let littleEndian = Int ( OSHostByteOrder ( ) ) == OSLittleEndian
return littleEndian ? _OSSwapInt16 ( port ) : port
}
var serverAddress = sockaddr_in ( )
serverAddress . sin_family = sa_family_t ( AF_INET )
serverAddress . sin_addr . s_addr = inet_addr ( " 127.0.0.1 " )
serverAddress . sin_port = swapBytesIfNeeded ( port : in_port_t ( 27042 ) )
let sock = socket ( AF_INET , SOCK_STREAM , 0 )
let result = withUnsafePointer ( to : & serverAddress ) {
$0 . withMemoryRebound ( to : sockaddr . self , capacity : 1 ) {
connect ( sock , $0 , socklen_t ( MemoryLayout < sockaddr_in > . stride ) )
}
}
if result != - 1 {
return true
}
return false
}
En général, plus la détection du jailbreak est compliquée, plus elle est difficile à détecter et à contourner. L’erreur la plus courante lors de la mise en œuvre de la détection du jailbreak réside souvent dans la mise en œuvre elle-même.
Nous rencontrons souvent des applications dotées d'une excellente détection de jailbreak, mais l'implémentation se fait dans une fonction qui renvoie vrai ou faux selon que l'appareil est jailbreaké. Dans ces cas, nous contournons la détection du jailbreak en utilisant Cycript ou un outil similaire pour inverser la valeur de retour de la fonction de détection.
En pratique, la meilleure détection de jailbreak combine plusieurs techniques et les intègre à d’autres fonctions afin qu’elles ne puissent pas être facilement contournées.
https://github.com/mehrankmlf/SecurityKit
https://www.synacktiv.com/sites/default/files/2021-10/2021_sthack_jailbreak.pdf
https://www.romainthomas.fr/post/21-07-pokemongo-anti-frida-jailbreak-bypass/
https://aeonlucid.com/Snapchat-detection-on-iOS/
https://github.com/AeonLucid/SnapHide
https://redmaple.tech/blogs/macho-files/