AppSec: Cómo detectar Rooted en tu app

Por diferentes motivos muchas apps necesitan detectar si el teléfono ha sido “rooteado”, por lo que en este artículo veremos diferentes técnicas para averiguarlo. He pensado que un post sobre el tema sería de interés para muchos lectores, ya que es frecuente ver este tipo de preguntas en foros de desarrollo.

En este post de StackOverflow podemos encontrar técnicas comúnmente utilizadas en apps para detectar Rooted. El siguiente código hace uso de 3 métodos para detectar Rooted: el primero busca la cadena “test-keys”, que es una llave genérica para firmar paquetes; el segundo método comprueba si existe el Superuser.apk en el disco, esta app gestiona el acceso al comando su (privilegios administrador) para las demás apps; y por último el tercer método llama directamente a su y ejecuta un comando con privilegios de root.


 /**
 * @author Kevin Kowalewski
 * 
 */
public class Root {

    private static String LOG_TAG = Root.class.getName();

    public boolean isDeviceRooted() {
        if (checkRootMethod1()){return true;}
        if (checkRootMethod2()){return true;}
        if (checkRootMethod3()){return true;}
        return false;
    }

    public boolean checkRootMethod1(){
        String buildTags = android.os.Build.TAGS;

        if (buildTags != null && buildTags.contains("test-keys")) {
            return true;
        }
        return false;
    }

    public boolean checkRootMethod2(){
        try {
            File file = new File("/system/app/Superuser.apk");
            if (file.exists()) {
                return true;
            }
        } catch (Exception e) { }

        return false;
    }

    public boolean checkRootMethod3() {
        if (new ExecShell().executeCommand(SHELL_CMD.check_su_binary) != null){
            return true;
        }else{
            return false;
        }
    }
}

/**
 * @author Kevin Kowalewski
 *
 */
public class ExecShell {

    private static String LOG_TAG = ExecShell.class.getName();

    public static enum SHELL_CMD {
        check_su_binary(new String[] {"/system/xbin/which","su"}),
        ;

        String[] command;

        SHELL_CMD(String[] command){
            this.command = command;
        }
    }

    public ArrayList executeCommand(SHELL_CMD shellCmd){
        String line = null;
        ArrayList fullResponse = new ArrayList();
        Process localProcess = null;

        try {
            localProcess = Runtime.getRuntime().exec(shellCmd.command);
        } catch (Exception e) {
            return null;
            //e.printStackTrace();
        }

        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
        BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));

        try {
            while ((line = in.readLine()) != null) {
                Log.d(LOG_TAG, "--> Line received: " + line);
                fullResponse.add(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        Log.d(LOG_TAG, "--> Full response was: " + fullResponse);

        return fullResponse;
    }

}

Las apps en un teléfono que no este rooteado no podrían ejecutar cualquiera de estos métodos, ya que todas las apps en Android están dentro de una sandbox (un sistema de aislamiento de procesos) y con privilegios limitados.

Los tres métodos descritos posiblemente sean los más comunes y si hacemos ingeniería inversa de forma frecuente los podemos encontrar en conocidas apps.

Otras técnicas incluyen el uso de la fantástica librería RootTools que facilita el desarrollo de apps que necesitan root ofreciendo diversas herramientas. Muchas apps utilizan esta librería.

Las funcionalidades de esta librería incluyen comprobar si existe o instalar BusyBox (programa que combina muchas utilidades estándares de Unix en un solo ejecutable pequeño), comprobar si existe o instalar SuperUser, comprobar que se tiene acceso root, herramientas nativas o suficiente espacio en la SD Card.

Como ejercicio para comprobar las técnicas de detección de Rooted he escrito el VULNEX ROOT TESTER que combina diferentes técnicas, desde las básicas aquí presentadas a algunas más sofisticadas de las que hablaremos en otro post. A continuación algunas capturas de la herramienta.

vulnex_root_tester1

vulnex_root_tester2

vulnex_root_tester3

vulnex_root_tester4

Sin duda la capacidad de detectar Rooted puede ser necesaria para determinadas apps que requieren un elevado nivel de seguridad, pero igualmente para muchas apps legítimas, como diversas apps de seguridad, requieren de root para funcionar correctamente o aprovechar al máximo la plataforma Android.

Tenemos que tener en cuenta que a la hora de escribir una app segura no es suficiente con detectar Rooted, sino que también debemos pensar en realizar un modelo de amenazas de los posibles riesgos para nuestra app, desarrollar de forma segura (por ejemplo OWASP Mobile) y aplicar técnicas de ofuscación de código (entre otras muchas medidas) para mitigar vulnerabilidades y dificultar la ingeniería inversa. Mi recomendación es que si no estamos familiarizados con estos conceptos hablemos con algún profesional en desarrollo seguro.

¿Y vuestra app que técnicas utiliza para detectar Rooted?

— Simon Roses Femerling

Esta entrada fue publicada en Hacking, Hacking Etico, Modelo de Amenazas, OWASP, Privacidad, SDL, Seguridad y etiquetada , , , , , , , , , . Guarda el enlace permanente.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.