your daily cup of tea™

powered by

Auditoria de seguridad: BiciMAD

Tras varios años en CityBikes, cada vez que aparece un sistema nuevo de bicicletas realizo un análisis general sobre el funcionamiento de sus sistemas. Qué sorpresa éste lunes cuando llegué a casa y me enteré de que BiciMAD ya está en marcha.

Ubicación de las estaciones

Normalmente, lo primero que hago es consultar si el sistema pone a la disposición un mapa en su página web: http://www.bicimad.com/mapa.html.

Si miramos el código fuente, podemos ver en la línea #158 como no aparecen las estaciones que deberían pintarse en el mapa. Mi única suposición es que varios servicios ya han estado scrapeando la web y desde el ayuntamiento o bonopark se ha decidido atajar el asunto.

var estaciones = [
					
	                  ]; 

Pero qué hay de las aplicaciones móviles? De alguna forma accederán a esos datos… El primer paso será descargarse la aplicación de android de bicimad y usarla. Curiosamente, no se puede usar sin estar dado de alta, pero eso tiene fácil solución. Tendremos que decompilar la aplicación y rastrear strings en búsqueda de algún servidor escondido ;) La navaja suiza en éste caso se compone de: Dex2jar, apktool y jd. No es que tengamos especial interés en el código fuente de la aplicación, sólo en las cadenas de texto que representen urls:

grep -r "http://" .
...
./json/JSONParser.java: public static final String URL_SERVIDOR = "http://xxx.xxx.xxx.xx/bicimadMovil/";
./json/JSONParser.java: public static final String url_all_estaciones = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_all_estaciones.php";
./json/JSONParser.java: public static final String url_change_password = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/change_password.php";
./json/JSONParser.java: public static final String url_enviar_push_bienvenida = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/send_push_welcome.php";
./json/JSONParser.java: public static final String url_estaciones_cercanas = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_estaciones_cercanas.php";
./json/JSONParser.java: public static final String url_generate_password = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/generate_new_password.php";
./json/JSONParser.java: public static final String url_get_datos_usuario = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_datos_usuario.php";
./json/JSONParser.java: public static final String url_get_info_tarjeta_consorcio = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_info_tarjeta_consorcio.php";
./json/JSONParser.java: public static final String url_get_ranking_usuarios = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_ranking_usuarios.php";
./json/JSONParser.java: public static final String url_get_ruta_usuario = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_historial_rutas_usuario.php";
./json/JSONParser.java: public static final String url_get_user = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_usuario.php";
./json/JSONParser.java: public static final String url_get_user_by_dni = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_usuario_por_dni.php";
./json/JSONParser.java: public static final String url_get_weather = "http://api.openweathermap.org/data/2.5/weather?q=Madrid,Spain";
./json/JSONParser.java: public static final String url_registrar_incidencia = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/registrar_incidencia.php";
./json/JSONParser.java: public static final String url_reservar_base = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/set_base_reservada.php";
./json/JSONParser.java: public static final String url_save_new_user = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/set_new_usuario.php";
./json/JSONParser.java: public static final String url_save_ruta = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/set_ruta_usuario.php";
./json/JSONParser.java: public static final String url_submit_form_alta = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/submit_form_tpv.php";
./json/JSONParser.java: public static final String url_submit_form_alta_tutor = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/submit_form_tpv_tutor.php";
./json/JSONParser.java: public static final String url_submit_form_recarga = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/submit_form_recarga_tpv.php";
./json/JSONParser.java: public static final String url_update_user = "http://xxx.xxx.xxx.xx/bicimadMovil/functions/update_usuario.php";
...

Atiza! Bueno, para empezar tenemos ya un enlace a un feed de datos.

curl http://xxx.xxx.xxx.xx/bicimadMovil/functions/get_all_estaciones.php | json_pp
{
   "success" : 1,
   "estaciones" : [
      {
         "porcentaje" : 62.5,
         "latitud" : "40.4168961",
         "longitud" : "-3.7024255",
         "numero_bases" : "24",
         "libres" : "15",
         "luz" : "0",
         "idestacion" : "1a",
         "activo" : "1",
         "numero_estacion" : "1a",
         "nombre" : "Puerta del Sol",
         "direccion" : "Puerta del Sol No 1"
      },
      {
         "porcentaje" : 45.833333333333,
         "latitud" : "40.4170009",
         "longitud" : "-3.7024207",
         "numero_bases" : "24",
         "libres" : "11",
         "luz" : "0",
         "idestacion" : "1b",
         "activo" : "1",
         "numero_estacion" : "1b",
         "nombre" : "Puerta del Sol",
         "direccion" : "Puerta del Sol No 1"
      },
      {
         "porcentaje" : 54.166666666667,
         "latitud" : "40.4205886",
         "longitud" : "-3.7058415",
         "numero_bases" : "24",
         "libres" : "13",
         "luz" : "0",
         "idestacion" : "2",
         "activo" : "1",
         "numero_estacion" : "2",
         "nombre" : "Miguel Moya",
         "direccion" : "Miguel Moya No 1"
      },
      {
         "porcentaje" : 22.222222222222,
         "latitud" : "40.4302937",
         "longitud" : "-3.7069171",
         "numero_bases" : "18",
         "libres" : "4",
         "luz" : "0",
         "idestacion" : "3",
         "activo" : "1",
         "numero_estacion" : "3",
         "nombre" : "Conde Suchil",
         "direccion" : " Plaza Conde Suchil No 2-4"
      },
      ...
   ]
}

Pero aún hay más, sin tan siquiera querer mirar urls tan jugosas como get_usuario_por_dni, el uso de http (no cifrado) para registrarse y hacer login desde la aplicación o analizar a fondo JSONParser nos encontramos con las siguientes joyas:

bicimad
key

Es eso que veo ahí una clave RSA privada para enviar notificaciones push a los dispositivos? Para los legos en materia, suponiendo que la clave esté en uso se podría, por ejemplo, enviar publicidad de parte de BiciMAD a todas las personas con la aplicación instalada. Es decir, suplantar la identidad de BiciMAD. Sin nombrar el resto de scripts de administración que tienen públicos en el listing, PRUEBA.php, etc. Llegados a éste punto decido dejar de hurgar en el bote de gusanos antes de ensuciarme demasiado. Ésto es sólo la punta del iceberg.

Un proyecto enmarcado en el Lote 5 de la contratación de gestión de los servicios públicos de Madrid de 884 millones de euros en los próximos 12 años, con licitación a Bonopark S.L. de 25 millones de euros en 10 años… no lo tengo muy claro.

Me gustaria añadir cómo lo ocurrido aquí defiende la tesis sostenida por CityBikes:

  1. Los datos deben ser abiertos. Más aún cuando el proyecto es público.
  2. Los datos deben ser propiedad del ayuntamiento y nunca de la empresa gestora. Ésta “cláusula” permite al ayuntamiento desarrollar libremente aplicaciones móviles sin depender de la empresa gestora o como me gusta llamarlo, sin que la empresa gestora secuestre los datos.
  3. Los datos son más importantes que las aplicaciones móviles: con datos se pueden fabricar aplicaciones, con las aplicaciones no se fabrica nada (excepto en algunos casos frustraciones).

Recomiendo encarecidamente al ayuntamiento de Madrid seguir los pasos de otros ayuntamientos en política de apertura de datos, como Londres o Barcelona. También al gobierno de España inspirarse en la política de datos abiertos de Francia y su licencia de datos abiertos Open Data Licence que recientemente obligó a JCDecaux a proporcionar una API pública.

En general, la misión de CityBikes pasaría por incorporar éstos datos en nuestras fuentes y ponerlos a la disposición del público de forma libre ya sea para consumirlo desde aplicaciones de terceros como para poder montar proyectos relacionados. Ésta vez, no voy a incluir los datos de BiciMAD en CityBikes ni pybikes hasta que los problemas en sus sistemas informáticos estén “resueltos” y se provea, desde BiciMAD o el ayuntamiento de Madrid, de un feed de estaciones correctamente licenciado, tal como se nos vende desde el gobierno central: http://datos.gob.es/.

48 thoughts on “Auditoria de seguridad: BiciMAD”

  1. «Llegados a éste punto decido dejar de urgar en el bote de gusanos».

    Una pena, porque seguramente haya mucho que destripar todavía…

  2. Puedo confirmar que es solo la punta del iceberg… que se descubran unos fallos tan monumentales con “dos pinzeladas” de hacking es lamentable :(

  3. Lo malo es que alguno habrá que decida seguir arañando. Me gustaría conocer al experto en seguridad del proyecto aunque me puedo imaginar que no había porque como dice un director de Tuenti: “Los desarrolladores cobran demasiado”. Precisamente para evitar este tipo de cosas, añado

  4. Lo realmente lamentable es que seguro que el proyecto se lo han adjudicado a algún enchufe y no por concurso o si ha habido concurso público se han hecho las típicas triquiñuelas para que le tocara al hijo o al cuñado de turno de la alcaldesa, concejal, ministro o senador de turno. No nos cansaremos de ver como en España se paran obras o sistemas (la web está caída en estos momentos) por culpa única y exclusivamente de quienes nos (mal) gobiernan, lo cuál es una pena.

  5. Los desarroladores “no cobran demasiado”, como dicen aquí abajo, lo que ocurre es que cobran demasiado poco. Si cobrasen más, se les exigiría más, pero claro, no puedes exigir conocimientos de arquitecto a un albañil mileurista.

    1. No es mejor decir: “Los desarrolladores son malos”. Da igual el dinero que les pagues, esta chapuza la hubieran hecho igual.

  6. Una pregunta al autor del post.
    ¿Haces auditorías para empresas por encargo? ¿Dominas también iPhone?
    Saludos.

  7. Muy interesante, ahora solo te falta escribirlo bien, utilizando la puntuación correcta y evitando palabras como “scrapear” o “strings”, completamente innecesarias.

    1. Muy bien tío listo. Cuando queramos ver un texto perfectamente redactado iremos a ver tu website. “Scrapear”, “strings” y otros anglicismos son usados habitualmente en nuestro argot, por lo que están bien escritos, más que de sobra bien escritos. Si realmente lo importante de todo el documento, es que has visto unos cuantos anglicismos y unas puntuaciones colocadas de forma “diferente” a la norma, es que no entiendes el alcance de lo que se dice aquí.

      Un saludo :)

        1. Feed the troll, until it bursts.

          Decir que escribir string es completamente innecesario… Teniendo en cuenta que las URLs son cadenas de caracteres que se declaran de tipo “String”, no veo como puede estar mal llamarlas “strings”. Es como decir que utilizar “push” es totalmente innecesario… sin mas.

          Muy bueno el artículo portzierto.

  8. Pingback: Anonymous
  9. En su momento también lo descubrí y me quede flipando, claves privadas al acceso de cualquiera, mapas, datos,…

    No quise hacerlo público un tema personal con la empresa responsable, pero fue un verdadero despropósito…

  10. Anda que tio más tonto! has puesto un monstruito en la clave y no puedo copiarla. Puedes volver a poner las imágenes sin el mounstruito que las tape?? mira que eres garrulo….

  11. Los que vienen a insultar y desacreditar que son, los programadores que hicieron para Bonopark S.L. esta chapuza?

    Por otra parte lo de no aceptar comentarios dando más detalles divertidos es por…?

    1. Disculpa, por alguna razon el comentario se habia quedado pendiente. A priori tengo los comentarios abiertos. Es posible que akismet lo haya interpretado como posible spam.

  12. Hombre tampoco veo nada tan grave en lo que has publicado, a no ser que en los otros endpoints haya información privada todo lo que has puesto parece de dominio público. Los endpoints tipo get_datos_usuario están también abiertos o requieren de algún tipo de sesión?
    Por otra parte no está mal que te tires el rollo buscando strings en el binario pero habría sido mucho más fácil y rápido poner un proxy como Charles.
    Como auditoría no se sí vale pero como curiosidad está claro que llama la atención…

    1. El problema es que eso era solo la punta del iceberg, en cuanto mires un poquito más verás otro certificado (con un RSA private key), el código de un php que se dedica a cifrar y descifrar datos (incluída su clave), PHPs de administración sin ningún tipo de control de acceso, y ya para rematar, probando la API “pública”, se ve que con saber pocos datos de un usuario (como su DNI) puedes obtener más datos suyos (como el nombre completo o el teléfono). Parece que también sabiendo el correo se puede localizar al usuario a través del gps de la bici…
      Más cagadas por parte de estos pseudoinformáticos, difícil.
      Ah, y la base de datos la tienen expuesta a todo internet (una MySQL 5.5.37-0ubuntu0.12.04.1), que es otra burrada por innecesario/malísimo diseño.

      1. Juas! eso ya tiene otra pinta jajaja. Menudo desastre! cuantas cenas se habrán pagado con esos 800 millones??

  13. Alguien sabe qué empresa de desarrollo ha hecho esto?

    Lo he estado buscando y no he encontrado nada. Yo voto por Indra :) y no porque sus desarrolladores sean malos.

  14. En la última actualización han introducido la importantísima medida de seguridad de “encriptar” ¡en Base64! la IP a la que se conecta la aplicación. Pero claro, siguen estando exáctamente las mismas APIs, y sigue toda la comunicación haciéndose en claro.

  15. Ademas, el servidor tiene un FTP (INDURAIN) y un VNC escuchando, a parte de otros puertos varios abiertos (2010, 2020, 2030, 2040, 2060, 3020, 4099…). Vaya joyita! :)

  16. en alguna estación he visto la consola abierta y accesible como usuario … Todo abierto en pantalla

  17. Mala configuración del Apache, el MySQL corriendo en el puerto típico, sin SSL en ningún sitio.. No es por defender al picateclas de turno ni mucho menos (get_usuario_por_dni.php, really?), pero parece más un problema de un sysadmin muy poco competente.

  18. “Recomiendo encarecidamente al ayuntamiento de Madrid seguir los pasos de otros ayuntamientos en política de apertura de datos, como Londres o Barcelona” – es que no lo están haciendo ya? xD

  19. “la culpa no es de los desarrolladores”, ” les pagan mal”, ” si pagaran mas tendrian algo mejor”.
    Este producto es una autentica verguenza desde el ayuntamiento de Madrid , hasta el ultimo mono de la empresa que lo desarrolla.

    El diseno malo, la perfomance horrible, el desarrollo con agujeros de seguridad por todos lados, el testing inexistente, la direccion de proyecto de cubatas, el que ha aprobado la salida a produccion de cubatas con la direccion de proyectos.

    Alguno dira que el programador hizo lo que le mandaron, y ese el problema, NO HAY PROFESIONALIDAD, ponen a dirigir el projecto al amguete del director de la concesionaria , cuantos proyectos como estos ha hecho antes : CERO y de programador a uno de fisicas que a visto la programacion en dos asignaturas a lo largo de la carrera, que igual podia estar programando que recogiendo manzanas.

    Ahora, barato a tenido que ser de huevos.

Leave a Reply to Ángel Cancel Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.