Descargar PDF: 2022_C00C00R00CON_jailbreak.pdf
Sin jailbreak
Con ptrace (lldb / frida) → la aplicación necesita el derecho get-task-allow
Al inyectar código Frida → la aplicación necesita ser reempaquetada
En ambos casos, deberá renunciar a la solicitud.
pero tiene muchos efectos secundarios:
ID de equipo diferente
Los archivos se modifican
Con un jailbreak
Se agregan mecanismos de detección de jailbreak a la defensa de ingeniería inversa para dificultar la ejecución de la aplicación en un dispositivo con jailbreak. Esto bloquea algunas de las herramientas y técnicas que les gusta usar a los ingenieros inversos. Como la mayoría de los otros tipos de defensa, la detección de jailbreak no es muy efectiva por sí sola, pero distribuir comprobaciones por todo el código fuente de la aplicación puede mejorar la efectividad del esquema general antimanipulación.
Los riesgos de seguridad empresarial que plantea el jailbreak se combinan frente a herramientas que pueden ayudar a los usuarios a evadir fácilmente los métodos comunes de detección de jailbreak. Un usuario puede descargar cualquier ajuste de aplicación como Spotify++ directamente desde la tienda de aplicaciones de terceros Cydia. Muchas aplicaciones proporcionan detección de jailbreak, al igual que muchas aplicaciones de servicios financieros y de medios que desean limitar la piratería de contenido y el compromiso de cuentas, respectivamente. Desafortunadamente, estas detecciones de jailbreak se basan en una combinación de pruebas relativamente sencillas y evitables, como:
La limitación fundamental de estas y otras pruebas de detección comparables es que, como pruebas del lado del cliente, los atacantes pueden acceder a ellas, realizar ingeniería inversa y evadirlas. Además, las aplicaciones que realizan estas pruebas de detección de jailbreak (por ejemplo, la aplicación MDM) deben pasar por el proceso de revisión de aplicaciones de Apple, lo que limita el alcance de los datos que pueden recopilar para analizar el estado del jailbreak de un dispositivo. Echemos un vistazo rápido a ellos:
Busque archivos y directorios típicamente asociados con jailbreak, como:
//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 "
]
}
En la mayoría de los casos, estos se verifican utilizando el
-(BOOL)fileExistsAtPath:(NSString*)
método de ruta en NSFileManager o FileManager.default.fileExists(atPath: path)
. Sin embargo, también hay aplicaciones que utilizan funciones C de nivel inferior como fopen(), stat() o access().
Otra forma de comprobar los mecanismos de jailbreak es intentar escribir en una ubicación que esté fuera del entorno limitado de la aplicación. Puede hacer esto haciendo que la aplicación intente crear un archivo en, por ejemplo, el directorio /private
. Si el archivo se crea correctamente, el dispositivo ha sido liberado.
Este método consiste en comprobar los permisos de archivos y directorios específicos del sistema. Más directorios tienen acceso de escritura en un dispositivo con jailbreak que en uno no comprometido. Un ejemplo de esto es la partición raíz, que originalmente sólo tiene permiso de lectura. Si se descubre que tiene permisos de lectura y escritura, significa que el dispositivo tiene jailbreak. Hay diferentes formas de realizar estas comprobaciones, como utilizar NSFileManager y funciones C como statfs().
Rápido:
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
}
Objetivo-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 ];
}
Hay diferentes formas de realizar estas comprobaciones, como utilizar NSFileManager y funciones C como statfs(), open(), utimes(), stat(), pathconf(), stat64(), fopen().
Puede comprobar los controladores de protocolo intentando abrir una URL de Cydia. La tienda de aplicaciones Cydia, que prácticamente todas las herramientas de jailbreak instalan de forma predeterminada, instala el controlador de protocolo cydia://.
Rápido:
if let url = URL ( string : " cydia://package/com.example.package " ) , UIApplication . shared . canOpenURL ( url ) {
// Device is jailbroken
}
Objetivo-C:
if ([[UIApplication sharedApplication ] canOpenURL: [ NSURL URLWithString: @" cydia://package/com.example.package " ]]){
// Device is jailbroken
}
Los atacantes pueden acceder a estas comprobaciones simples, realizar ingeniería inversa y evadirlas.
ptrace ( PT_DENY_ATTACH );
void try_kill () {
const int pid = getpid ();
int ret = kill (pid, 0 );
}
Según la página de manual de kill ( man 2 kill
), la señal 0
se utiliza para comprobar que el pid
indicado en el primer parámetro realmente existe.
[…] Sin embargo, un valor de 0 provocará que se realice una verificación de errores (sin enviar ninguna señal). Esto se puede utilizar para comprobar la validez de pid.
Esta operación de eliminación va seguida de la comprobación 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)
Podemos comprobar la integridad de la firma de nuestro binario. Esta verificación comienza abriendo el binario de la aplicación principal desde el disco, busca hasta la secuencia kSecCodeMagicEmbeddedSignature 0xfade0cc0
, lee los derechos y calcula la suma de verificación.
El segmento de firma de código contiene una estructura superblob, que a su vez contiene otros blobs de diferentes tipos. La estructura de ambos está definida en un código de Apple más abierto:
/*
* 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;
El superblob simplemente contiene algo de magia, la longitud de toda la sección de firma de código, un recuento de los blobs y una serie de índices para esos blobs. Las diferentes magias de blobs identificativas se definen en este archivo:
/*
* 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 */
};
Por ejemplo, el binario ps
de iOS tiene cuatro bloques de contenido después del superblob inicial:
Estos son, respectivamente:
0xfade0c02
), que puede detectar porque tiene el nombre binario calificado com.apple.ps
.0xfade0c01
).0xfade7171
).0xfade0b01
).Intente detener la aplicación después de un tiempo de espera prolongado y no salga directamente del lugar de detección de jailbreak.
fork()
Bifurcación del proceso. Sandboxd no niega a las aplicaciones la capacidad de usar fork(), popen() o cualquier otra función de C para crear procesos secundarios en dispositivos con jailbreak. Sin embargo, sandboxd niega explícitamente la bifurcación de procesos en dispositivos sin jailbreak. Por lo tanto, al verificar el pid devuelto en fork(), una aplicación puede saber si un dispositivo está comprometido. Si la bifurcación tiene éxito, la aplicación puede deducir que se está ejecutando en un dispositivo con jailbreak.
system()
Llamar a la función system() con un argumento NULL en un dispositivo sin jailbreak devolverá 0. Hacer lo mismo en un dispositivo con jailbreak devolverá 1. Esto se debe a que la función verificará si /bin/sh existe, y solo existe en Dispositivos liberados.
Conexiones de bucle invertido SSH. Debido a la gran cantidad de dispositivos con jailbreak que tienen OpenSSH instalado, algunas aplicaciones intentan establecer una conexión a 127.0.0.1 en el puerto 22. Si la conexión se realiza correctamente, significa que OpenSSH está instalado y ejecutándose, lo que demuestra que el dispositivo tiene jailbreak.
Verifique el loopback para ver si hay 22 (OpenSSH) y 44 (checkra1n) puertos abiertos.
Este método de detección comienza llamando a funciones como _dyld_image_count()
y _dyld_get_image_name()
para ver qué dylibs están cargados actualmente. Este método es muy difícil de parchear dinámicamente debido al hecho de que los parches en sí son parte de dylibs.
/usr/lib/substitute-inserter.dylib
por ejemplo
Puede utilizar estructuras internas dlopen/escaneo de memoria/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
}
Verificar la integridad del código
Intenta detectar a 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 general, cuanto más complicada es la detección del jailbreak, más difícil es detectarlo y evitarlo. El error más común al implementar la detección de jailbreak suele radicar en la implementación misma.
A menudo nos encontramos con aplicaciones que tienen una excelente detección de jailbreak, pero la implementación se realiza en una función que devuelve verdadero o falso dependiendo de si el dispositivo tiene jailbreak. En estos casos, evitamos la detección de jailbreak usando Cycript o una herramienta similar para invertir el valor de retorno de la función de detección.
En la práctica, la mejor detección de jailbreak combina múltiples técnicas e integrándolas en otras funciones para que no se puedan eludir fácilmente.
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/