Análise de Taint¶
A análise de taint é uma técnica de análise de fluxo de informação que rastreia o fluxo de informações sensíveis dentro de um programa. Por exemplo, ela pode determinar se dados de geolocalização coletados em um aplicativo Android estão sendo transmitidos para domínios de terceiros.
Na análise de taint, a informação flui de uma "fonte" (source) para um "coletor" (sink). Uma fonte é onde a informação sensível se origina, e um coletor é onde esta informação é finalmente utilizada. Por exemplo, podemos determinar se o ID do dispositivo recuperado por uma função getDeviceId() é transmitido como uma mensagem de texto via outra função sendTextMessage(). Neste cenário, getDeviceId() é a fonte, e sendTextMessage() é o coletor. Se existir um caminho direto entre eles, isso é chamado de vazamento.
Em aplicações grandes, a análise manual de fluxo de informação pode ser muito demorada e imprecisa. A análise de taint automatiza este processo, com dois métodos principais: estático e dinâmico. O primeiro examina o código sem executá-lo, oferecendo ampla cobertura mas potencialmente gerando falsos positivos. Em contraste, a análise dinâmica observa a execução da aplicação em tempo real, fornecendo contexto real mas possivelmente deixando passar problemas não acionados. Uma comparação completa dessas técnicas está além do escopo desta seção.
Existem múltiplas ferramentas que realizam análise de taint em código nativo, incluindo Triton e bincat. Entretanto, nesta seção, focaremos principalmente no código Java para Android e utilizaremos FlowDroid para a análise de taint. Outra ferramenta notável que suporta análise de taint para aplicativos Android é a GDA.
Para nossa demonstração, usaremos a ferramenta de linha de comando FlowDroid para realizar análise de taint no aplicativo InsecureShop v1.0.
O aplicativo InsecureShop aceita um nome de usuário e senha como entrada e os armazena nas preferências compartilhadas do aplicativo. Em nossa análise de taint, estamos interessados em como esse nome de usuário e senha armazenados são utilizados. Neste contexto, o nome de usuário e senha são as informações sensíveis, e a leitura das preferências compartilhadas é a fonte. O coletor nesta análise poderia ser várias operações, como enviar informações pela rede, transmitir informações via uma Intent, ou armazenar informações em um arquivo externo.
Para usar o FlowDroid, primeiro precisamos fornecer uma lista de entrada de fontes e coletores potenciais para avaliação. No nosso caso, ler das preferências compartilhadas será a fonte, enquanto adicionar parâmetros a uma Intent será o coletor. O arquivo de configuração será parecido com o seguinte (vamos nomeá-lo como "source_sink.txt"):
<android.content.SharedPreferences: java.lang.String getString(java.lang.String, java.lang.String)> -> _SOURCE_
<android.content.Intent: android.content.Intent putExtra(java.lang.String,java.lang.CharSequence)> -> _SINK_
<android.content.Intent: android.content.Intent putExtra(java.lang.String,char)> -> _SINK_
<android.content.Intent: android.content.Intent putExtra(java.lang.String,java.lang.String)> -> _SINK_
Para invocar o FlowDroid via linha de comando, use o seguinte comando:
java -jar soot-infoflow-cmd/target/soot-infoflow-cmd-jar-with-dependencies.jar \
-a InsecureShop.apk \
-p Android/Sdk/platforms \
-s source_sink.txt
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - O coletor virtualinvoke r2.<android.content.Intent: android.content.Intent putExtra(java.lang.String,java.lang.String)>("password", $r5) no método <com.insecureshop.AboutUsActivity: void onSendData(android.view.View)> foi chamado com valores das seguintes fontes:
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - - $r1 = interfaceinvoke $r2.<android.content.SharedPreferences: java.lang.String getString(java.lang.String,java.lang.String)>("password", "") no método <com.insecureshop.util.Prefs: java.lang.String getPassword()>
...
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - O coletor virtualinvoke r2.<android.content.Intent: android.content.Intent putExtra(java.lang.String,java.lang.String)>("username", $r4) no método <com.insecureshop.AboutUsActivity: void onSendData(android.view.View)> foi chamado com valores das seguintes fontes:
[main] INFO soot.jimple.infoflow.android.SetupApplication$InPlaceInfoflow - - $r1 = interfaceinvoke $r2.<android.content.SharedPreferences: java.lang.String getString(java.lang.String,java.lang.String)>("username", "") no método <com.insecureshop.util.Prefs: java.lang.String getUsername()>
...
[main] INFO soot.jimple.infoflow.android.SetupApplication - Encontrados 2 vazamentos
A saída também usa a representação intermediária jimple e revela dois vazamentos na aplicação, cada um correspondendo ao nome de usuário e senha. Considerando que o aplicativo InsecureShop é de código aberto, podemos consultar seu código-fonte para validar as descobertas, como mostrado abaixo:
// arquivo: AboutActivity.kt
fun onSendData(view: View) {
val userName = Prefs.username!!
val password = Prefs.password!!
val intent = Intent("com.insecureshop.action.BROADCAST")
intent.putExtra("username", userName)
intent.putExtra("password", password)
sendBroadcast(intent)
textView.text = "InsecureShop é um aplicativo android vulnerável projetado intencionalmente e construído em Kotlin."
}
A análise de taint é especialmente benéfica para automatizar a análise de fluxo de informação em aplicações complexas. Entretanto, dada a complexidade de alguns aplicativos, a precisão dessas ferramentas pode variar. Assim, é essencial que os revisores encontrem um equilíbrio entre a precisão das ferramentas e o tempo gasto na análise manual.