Baixar PDF: 2022_C00C00R00CON_jailbreak.pdf
Sem JailBreak
Com ptrace (lldb/frida) → o aplicativo precisa do direito get-task-allow
Ao injetar código frida → o aplicativo precisa ser reembalado
Em ambos os casos, você precisa renunciar ao pedido
mas tem muitos efeitos colaterais:
ID de equipe diferente
Os arquivos são modificados
Com um JailBreak
Mecanismos de detecção de jailbreak são adicionados à defesa de engenharia reversa para dificultar a execução do aplicativo em um dispositivo desbloqueado. Isso bloqueia algumas das ferramentas e técnicas que os engenheiros reversos gostam de usar. Como a maioria dos outros tipos de defesa, a detecção de jailbreak não é muito eficaz por si só, mas espalhar verificações por todo o código-fonte do aplicativo pode melhorar a eficácia do esquema geral anti-adulteração.
Os riscos de segurança empresarial representados pelo jailbreak se agravam diante de ferramentas que podem ajudar os usuários a escapar facilmente dos métodos comuns de detecção de jailbreak. Um usuário pode baixar qualquer ajuste de aplicativo como o Spotify++ diretamente da loja de aplicativos de terceiros Cydia. Muitos aplicativos fornecem detecção de jailbreak, assim como muitos aplicativos de mídia e serviços financeiros que desejam limitar a pirataria de conteúdo e o comprometimento de contas, respectivamente. Infelizmente, essas detecções de jailbreak dependem de uma combinação de testes relativamente simples e evitáveis, como:
A limitação fundamental desses e de testes de detecção comparáveis é que, como testes do lado do cliente, eles podem ser acessados, submetidos a engenharia reversa e evitados por invasores. Além disso, os aplicativos que realizam esses testes de detecção de jailbreak (por exemplo, o aplicativo MDM) devem passar pelo processo de revisão de aplicativos da Apple, limitando o escopo dos dados que podem coletar para analisar o status de jailbreak de um dispositivo. Vamos dar uma olhada rápida neles:
Verifique arquivos e diretórios normalmente associados a jailbreaks, 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 "
]
}
Na maioria das vezes, estes são verificados usando o
-(BOOL)fileExistsAtPath:(NSString*)
método de caminho em NSFileManager ou FileManager.default.fileExists(atPath: path)
. No entanto, também existem aplicativos que usam funções C de nível inferior, como fopen(), stat() ou access().
Outra maneira de verificar mecanismos de jailbreak é tentar gravar em um local fora da sandbox do aplicativo. Você pode fazer isso fazendo com que o aplicativo tente criar um arquivo, por exemplo, no diretório /private
. Se o arquivo for criado com sucesso, o dispositivo foi desbloqueado.
Este método consiste em verificar as permissões de arquivos e diretórios específicos do sistema. Mais diretórios têm acesso de gravação em um dispositivo desbloqueado do que em um dispositivo não comprometido. Um exemplo disso é a partição raiz, que originalmente possui apenas permissão de leitura. Se for encontrado que possui permissões de leitura e gravação, significa que o dispositivo está desbloqueado. Existem diferentes maneiras de realizar essas verificações, como usar NSFileManager e funções 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 ];
}
Existem diferentes maneiras de realizar essas verificações, como usar NSFileManager e funções C como statfs(), open(), utimes(), stat(), pathconf(), stat64(), fopen().
Você pode verificar os manipuladores de protocolo tentando abrir uma URL do Cydia. A loja de aplicativos Cydia, que praticamente todas as ferramentas de jailbreak instalam por padrão, instala o manipulador 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
}
Essas verificações simples podem ser acessadas, submetidas a engenharia reversa e evitadas por invasores.
ptrace ( PT_DENY_ATTACH );
void try_kill () {
const int pid = getpid ();
int ret = kill (pid, 0 );
}
De acordo com a página man do kill ( man 2 kill
), o sinal 0
é usado para verificar se o pid
fornecido no primeiro parâmetro realmente existe.
[…] Um valor 0, porém, fará com que a verificação de erros seja realizada (sem envio de sinal). Isso pode ser usado para verificar a validade do pid.
Esta operação kill é seguida pela verificação 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 verificar a integridade da assinatura do nosso binário. Esta verificação começa abrindo o binário principal do aplicativo no disco, procure até a sequência kSecCodeMagicEmbeddedSignature 0xfade0cc0
, leia os direitos e calcule a soma de verificação.
O segmento de assinatura de código contém uma estrutura de superblob, que contém outros blobs de tipos diferentes. A estrutura de ambos é definida em algum código Apple mais aberto:
/*
* 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;
O super blob contém apenas um pouco de mágica, o comprimento de toda a seção de assinatura de código, uma contagem dos blobs e uma matriz de índices para esses blobs. As diferentes magias de identificação de blob são definidas neste arquivo:
/*
* 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 exemplo, o binário iOS ps
tem quatro blobs de conteúdo após o superblob inicial:
São eles, respectivamente:
0xfade0c02
), que você pode identificar, pois contém o nome binário qualificado com.apple.ps
.0xfade0c01
).0xfade7171
).0xfade0b01
).Tente interromper o aplicativo após um grande tempo limite e não saia diretamente do local de detecção do jailbreak.
fork()
Bifurcação de processo. O Sandboxd não nega aos aplicativos a capacidade de usar fork(), popen() ou qualquer outra função C para criar processos filhos em dispositivos desbloqueados. No entanto, o sandboxd nega explicitamente a bifurcação do processo em dispositivos sem jailbreak. Portanto, ao verificar o pid retornado em fork(), um aplicativo pode saber se um dispositivo está comprometido. Se a bifurcação for bem-sucedida, o aplicativo poderá deduzir que está sendo executado em um dispositivo desbloqueado.
system()
Chamar a função system() com um argumento NULL em um dispositivo sem jailbreak retornará 0. Fazer o mesmo em um dispositivo com jailbreak retornará 1. Isso ocorre porque a função verificará se /bin/sh existe, e só existe em dispositivos desbloqueados.
Conexões de loopback SSH. Devido à grande parte de dispositivos desbloqueados que possuem OpenSSH instalado, alguns aplicativos tentam fazer uma conexão com 127.0.0.1 na porta 22. Se a conexão for bem-sucedida, significa que o OpenSSH está instalado e em execução, o que prova que o dispositivo está desbloqueado.
Verifique o loopback para 22 (OpenSSH) e 44 (checkra1n) portas abertas.
Este método de detecção começa chamando funções como _dyld_image_count()
e _dyld_get_image_name()
para ver quais dylibs estão carregados no momento. Este método é muito difícil de corrigir dinamicamente devido ao fato de que os próprios patches fazem parte de dylibs.
/usr/lib/substitute-inserter.dylib
por exemplo
Pode usar dlopen/varredura de memória/estruturas internas 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
}
Verifique a integridade do código
Tente detectar 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
}
Em geral, quanto mais complicada for a detecção do jailbreak, mais difícil será detectá-lo e contorná-lo. O erro mais comum ao implementar a detecção de jailbreak geralmente reside na própria implementação.
Muitas vezes nos deparamos com aplicativos que possuem ótima detecção de jailbreak, mas a implementação está em uma função que retorna verdadeiro ou falso dependendo se o dispositivo está desbloqueado. Nesses casos, contornamos a detecção de jailbreak usando Cycript ou uma ferramenta semelhante para inverter o valor de retorno da função de detecção.
Na prática, a melhor detecção de jailbreak combina múltiplas técnicas e integra-as a outras funções para que não possam ser facilmente contornadas.
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/