Analizando la app de La Liga para Android

Buenas

Después de la última entrada un poco fuera de lo común, vuelvo con algo más en concordancia con las anteriores entradas.

Me ha llamado la atención una noticia que salió a la luz ayer sobre que La Liga espia a los usuarios a traves del microfono de la aplicacion. *Para los que no sean de España, La Liga es una organización que se encarga de gestionar las competiciones de futbol a nivel nacional.

En este y otros artículos se pueden leer frases como

La Liga parece querer convertir a los particulares en ‘micrófonos andantes’ al servicio de sus intereses, en este caso, detectar posibles fraudes

La noticia se viralizó y La Liga publicó un comunicado exponiendo sus argumentos al respecto, y es en este punto donde las cosas no me cuadraban.

*Antes de ir al lío me gustaria aclarar que no tengo nada en contra de La Liga ni de las empresas que están relacionadas con este asunto. Particularmente no soy un gran fan del fútbol y por tanto no tengo la aplicación, pero después de saber según que cosas de las que hace la app no creo que la instalase.

Primero vamos a ver que dice el comunicado sobre las acusaciones de espiar a los usuarios desde un punto de vista puramente lógico y luego vamos a ver cuál es la realidad basándonos en el código fuente de la aplicación en su versión 6.4.7 publicada el dia 8 de Junio de 2018 que al fin y al cabo es lo que ejecutan los usuarios en sus dispositivos.

Parte lógica/subjetiva sobre el comunicado de La Liga

Obviando la primera parte donde intentan justificarse hablando de pérdidas económicas y demás historias, en el tercer parrafo ya empiezan a decir cosas que no concuerdan con la realidad.

Esta nueva funcionalidad para la detección del fraude está habilitada en la app desde el pasado viernes 8 de junio de 2018, solo para usuarios del sistema Android y a nivel nacional*.

Dicen que la funcionalidad de recopilar información del microfono y ubicación se ha habilitado el 8 de junio, pues bien, la version 6.4.0 publicada el 21 de Febrero de 2018 con SHA1 efd50120f73c0d674492126ce9e9198da57c8287 tiene la capacidad de recopilar información tanto del micrófono como de la ubicación exactamente de la misma forma que la ultima versión disponible. Puede que se implementase en una versión anterior, es cuestión de mirarlo, pero con este ejemplo es suficiente para desmontar esa parte del comunicado. A no ser que la ‘funcionalidad’ a la que se refieran sea a la de pedir permiso y no a la de ‘espiar usuarios’.

*La primera versión de la aplicación de LaLiga que incluye el SDK de la empresa Fluzo para grabar el micrófono es la 6.3.0 publicada el 21 de Noviembre de 2017, si bien, la primera versión que envía la ubicación y el audio a sus servidores sin informar al usuario es la 6.4.5 publicada el 7 de Junio de 2018. Más info aquí.

(…) el micrófono captará el código binario de fragmentos de audio, con el único objeto de poder conocer si está viendo partidos de fútbol de competiciones disputadas por equipos de LaLiga, pero nunca se accederá al contenido de la grabación.

Poco que comentar aqui, salta a la vista la burrada de decir que el micrófono no graba fragmentos de audio. Ademas es contradictorio decir que se analiza la grabación (de la manera que sea, ya se vera luego)  y en la linea siguiente que nunca se accederá al contenido. Lo que se entiende de ahi es que graban e inmediatamente despues eliminan, por que en el momento que hagan cualquier otra cosa sobre el archivo generado que no sea eliminarlo ya están accediendo al contenido.

Ahora nos comentan como protegen la privacidad del usuario…

LaLiga sólo activará el micrófono y geoposicionamiento del dispositivo móvil durante las franjas horarias de partidos en los que compitan equipos de LaLiga.

Eso de la franja horaria es muy relativo, si un equipo Español juega en china cuando aquí sean las 5 de la madrugada, pueden activar los 10 millones de terminales y que graben.

LaLiga no accede a los fragmentos de audio captados por el micrófono del dispositivo, ya que estos se convierten de forma automática en un código binario en el propio dispositivo. LaLiga sólo accede a este código binario, que es irreversible y no permite obtener de nuevo la grabación de audio.

Aqui ya el comunicado pierde toda la credibilidad que pudiera tener. Por un lado nos dicen que La Liga no accede a los audios, que los transforman en un código binario de forma automática en el dispositivo (evidentemente, en computación todo son datos binarios, lo cual no quiere decir que no estén grabando un audio que pueda ser reproducido posteriormente) pero si les damos el beneficio de la duda, ¿lo que nos intentan decir es que están generando un hash progresivo con su aplicación después de grabar el audio y en el propio terminal ? o dicho de otra manera, ¿se refieren a que su aplicación hace lo que shazam (valorada en 400 millones de euros) ? Pero en este caso mucho más complejo, por que shazam si puede construir una base de datos sobre canciones que son un conjunto finito y concreto; pero reconocer que el sonido ambiente corresponde a un partido de fútbol en un bar ya son palabras mayores.

Queda bastante claro a estas alturas que lo que hacen es eso, pero que evidentemente no lo hacen de forma local, si no que envian la grabación a otro servicio para identificarla y a lo mejor soy yo que he buscado mal, pero en ningun punto de las condiciones generales de uso y politicas de privacidad de la aplicacion he visto que se mencione que los datos recopilados se mandan a otra empresa para que los analice, realmente no se como iran estos temas a nivel legal, pero en el aviso legal sobre privacidad y cookies hace una mencion a

Sus datos personales no serán cedidos a otras personas o empresas para ser utilizados para sus propios fines. No obstante, algunas entidades subcontratadas por LaLiga podrían acceder a los Datos Personales e información como Encargados o Subencargados del tratamiento para prestar a LaLiga un servicio necesario. En particular, LaLiga recibe asistencia de:

(a) Proveedores de servicios. A veces, compartimos su información con nuestros terceros proveedores de servicios, que nos ayudan a proporcionar nuestros servicios. Ejemplos de proveedores de servicios: alojamiento, métricas y analíticas.

Eso vuelve a ser genérico y bajo mi punto de vista deja la puerta abierta a traficar con los datos sin límite ninguno, total, cualquier empresa se puede convertir en proveedor de servicios de la noche a la mañana, ¿no?.

A partir de aquí los siguientes puntos ya me parecen puro recochineo de quien lo haya escrito y los que lo hayan aprobado como un comunicado serio.

Si este código coincide con un código previo de control, LaLiga podrá saber que está viendo un partido determinado. Si no coincide, el código se elimina.

Esto no se como tomarmelo, no se si viajan al futuro para generar previamente un codigo de control de determinado partido o simplemente es mas humo. *Pueden generar un patron inaudible y emitirlo durante los partidos.

Los códigos no irán referidos a su nombre, sino a su dirección IP y al ID específico que asigna la APP cuando el usuario se registra.

Supongo que lo que espera aqui esta gente es que les demos las gracias por guardar solo el identificador de usuario y la direccion IP y no referir los codigos al nombre de la persona. Igualmente vuelven a mentir, la APP no necesita registro y te genera un ID que guarda en la primera hora y que por supuesto tambien envia a la misma empresa externa, para lo del fraude y tal.

Le recordaremos periódicamente que LaLiga puede activar tu micrófono y geoposicionamiento y le solicitaremos que confirme su consentimiento.

Una afirmación completamente ambigua de la que no hay evidencia en el codigo de la aplicacion.

Podrá revocar su consentimiento en cualquier momento en los ajustes del dispositivo móvil.

Faltaria mas LOL.

Parte técnica

Ahora vamos con lo interesante que es demostrar todo lo anterior pero con datos, y que mejor opción que destripar la aplicación a ver que es lo que hace y como lo hace. Aviso de que sera largo por que hay que ir función por función viendo que hace cada cosa para evitar que todo pueda parecer una conjetura o suposición mia.

Para esto se puede tirar de apktool para ver el smali o lo que sea, nos vale un dex to jar y tirando, total lo que interesa es ver el código de una manera estática sin necesidad de editar el comportamiento, ya que si no me equivoco modificar la aplicacion seria cagar fuera del tiesto en relación con la legalidad, aunque en este caso en ningun EULA se indica que esto no se pudiera hacer.

Hay principalemente dos paquetes que nos interesan dentro de la aplicación, uno el de las clases propias de la app, es.lfp.gi.main y el de la empresa que hace uso de las funcionalidades polémicas, en este caso la empresa se llama fluzo 

FLUZO is the Google Analytics for TV. We turn TV viewers and content into digital users and monetizable assets through a proprietary ACR technology.

y su codigo esta en com.fluzo.sdk .

Echando un primer vistazo al código del main de la app podemos intuir que quien lo haya hecho no es precisamente un genio.

Ahi es donde ya se encuentra la primera evidencia de que efectivamente va a comunicarse con un servicio externo (algo que para los que no hayan leido los acuerdos de usuario y el comunicado, no se menciona en ningún sitio, todo lo contrario), mas allá de hardcodear la API de un servicio privado, que bueno, si les gusta vivir al límite no es asunto mio. Ademas aquí hay un punto gracioso, que intuyo no les habrá hecho mucha gracia a los devs de fluzo, pero que ya trataremos mas adelante.

Si buscamos referencias a esa url fluzo.com ya empezamos a ver que donde hay geolocalización o grabación del microfono, acaban apareciendo de una u otra manera endpoints en esta URL, por ejemplo:

Ahí ya vemos que aflora el nombre del endpoint asignado a esta aplicación https://laliga.fluzo.com que aparentemente no tiene nada y en /settings responde con un json que es indiferente por que no es el objetivo del post analizar nada que no sea la aplicación de manera estática.

Empezamos mirando las funciones de las clases del paquete de La Liga

Dentro de la clase LFP en el onCreate de las primeras cosas que hace es comprobar (en cada inicio de la app) si tiene permiso para el micrófono y si el idioma del dispositivo es el español, no que la IP sea de españa o el usuario español como afirman ellos pero bueno, vamos a pensar que es un bug; y si no pide permiso para usar el micrófono, aunque no vincula el funcionamiento de la app a  tener ese permiso lógicamente.

6b311051ec79ce96e314dc6e1a534e6d

En la funcion requestPermissions que como su nombre indica es para pedirte que le des acceso al micrófono si quieres, ya de entrada te crea un objeto con tu id de Fluzo sin venir a cuento y lanza el servicio, -aunque según ellos eso de grabar y coger geolocalización era en franjas horarias en las que haya partido, y no hay ningún control aqui de si hay partido o no, simplemente lo hacen cada vez que el usuario cierra y abre la app, pero bueno. (alguien podria pensar quecrean el objeto pero que no lo usen hasta que haya partido. Podría ser, pero no, ̶e̶n̶ ̶l̶a̶ ̶r̶e̶a̶l̶i̶d̶a̶d̶ ̶l̶o̶ ̶u̶s̶a̶n̶ ̶e̶x̶a̶c̶t̶a̶m̶e̶n̶t̶e̶ ̶c̶a̶d̶a̶ ̶3̶0̶ ̶m̶i̶n̶u̶t̶o̶s̶,̶ ̶y̶a̶ ̶v̶e̶r̶e̶m̶o̶s̶ ̶l̶u̶e̶g̶o̶ ̶e̶n̶ ̶q̶u̶e̶ ̶p̶u̶n̶t̶o̶ ̶l̶o̶ ̶h̶a̶c̶e̶n̶ ̶y̶ ̶p̶a̶r̶a̶ ̶q̶u̶e̶).-

Primero se muestra el metodo startFluzo que arranca el servicio y despues el metodo requestPermissions

En este clase no hay mucho más que nos interese aparte de funciones relativas a permiso de geolocalización

Es el momento de moverse a LocationService, que como su propio nombre indica contendra los metodos que gestionan el tema de la geolocalización y donde hay varias cosas interesantes.

Nos encontramos con la clase SendLocationAsyncTask que recibe el identificador fluzo del usuario, la longitud y la latitud de su posicion GPS para enviarla a X sitio.

Mirando la funcion mas en profundidad, envía la información si la función DataManager.INSTANCE.getMatchLive() devuelve un True, osea deducciendo un poco, por el nombre, si hay partido activo, devuelve verdadero, sino, devuelve falso.

̶R̶e̶a̶l̶i̶d̶a̶d̶ ̶-̶>̶ ̶d̶e̶v̶u̶e̶l̶v̶e̶ ̶s̶i̶e̶m̶p̶r̶e̶ ̶v̶e̶r̶d̶a̶d̶e̶r̶o̶.̶

 ̶C̶o̶n̶ ̶e̶s̶t̶e̶ ̶f̶u̶n̶c̶i̶o̶n̶a̶m̶i̶e̶n̶t̶o̶,̶ ̶n̶o̶ ̶s̶e̶ ̶s̶i̶ ̶s̶e̶r̶á̶ ̶u̶n̶ ̶b̶u̶g̶ ̶o̶ ̶u̶n̶a̶ ̶f̶e̶a̶t̶u̶r̶e̶ ̶i̶n̶t̶e̶n̶c̶i̶o̶n̶a̶d̶a̶,̶ ̶p̶e̶r̶o̶ ̶p̶a̶r̶a̶ ̶l̶a̶ ̶a̶p̶l̶i̶c̶a̶c̶i̶ó̶n̶ ̶s̶i̶e̶m̶p̶r̶e̶ ̶h̶a̶y̶ ̶p̶a̶r̶t̶i̶d̶o̶,̶ ̶o̶s̶e̶a̶ ̶q̶u̶e̶ ̶s̶i̶e̶m̶p̶r̶e̶ ̶s̶e̶ ̶g̶u̶a̶r̶d̶a̶n̶ ̶l̶a̶ ̶u̶b̶i̶c̶a̶c̶i̶o̶n̶ ̶c̶a̶d̶a̶ ̶X̶ ̶t̶i̶e̶m̶p̶o̶.̶

*Fallo mio aquí, como algunas personas han apuntado por twitter y en los comentarios, enviar o no la localización si depende de la respuesta del servidor, la cual se sigue comprobando cada X minutos

*Lo que se corta es el timetamp en milisegundos

Volviendo a LocationService para seguir analizando que hace la app con la geolocalización nos queda saber donde ha mandado nuestra ubicación la aplicación.

Nos lo ponen facil con la función sendUserMetadataToFluzo que es donde se hace la llamada luego a SendLocationAsyncTask, nada mas que añadir.

Ya sabemos donde van los datos de ubicación, pero nos falta responder a la pregunta de cada cuanto tiempo envia los datos la aplicación, ya hemos visto que da lo mismo si hay o no partido.

Hay varias funciones que loggean la geolocalización, pero la unica que llama a la función que envia los datos a fluzo es onNewLocation que borra las localizaciones anteriores (del telefono, no del servidor) y llama a sendUserMetadataToFluzo pasándole la nueva localización.

Después de enviarla crea un delay en el servicio de 30 * 60 * 1000 = 1800000 ms = 30 minutos, osea que recopila la geolocalización cada 30 minutos y se la envía a fluzo *si se ha recibido una respuesta positiva del servidor.

En este punto y antes de entrar en tema micrófono donde se complica un poco la cosa ya que el codigo del SDK de Fluzo esta ofuscado, me gustaria hacer referencia a como los adminsitradores de La Liga y Fluzo son capaces luego de relacionar los datos con el usuario. La respuesta a esta incógnita esta en la funcion linkUserIds

donde como su propio nombre indica se vinculan la id del usuario de la aplicacion y la ID que se asigna para fluzo. Ni se vincula la IP ni es necesario estar registrado en la app como dice La Liga en el comunicado.

Aprovecho también para hacer un inciso. La Liga afirma que no se guardan los archivos y las relaciones, hay una línea de debug en el codigo del SDK de fluzo que me hace dudar y es la siguiente:

C0850c.m819b(“FLUZO: you can check this user at https://platform.fluzo.com/audience/” + this.f662a.f730m);

f730m = ID

f662a = objeto fluzo

C0850c.m819b = metodo que escribe logs

De aquí entiendo que fluzo sí almacena los datos, y ya hemos visto que la app los manda, queda ahí la dudad de si realmente La Liga borra los datos (ubicación y grabaciones de micrófono) que no han dado positivo, o ya que los tiene se los guarda, es algo que sólo ellos saben.

Siguiendo ahora con la parte de las grabaciones de audio, aqui la cosa se complica a la vez que se hace mas divertido, el codigo del SDK de fluzo esta ofuscado, no mucho, es una ofuscacion muy basica pero lleva mucho tiempo hacerlo entendible, ademas no soy quién para modificar un código que no esta bajo un tipo de licencia que lo permita, asi que trabajaremos con el código ofuscado, que al haber hecho los devs un buen trabajo con las excepciones y el debug, podemos reconocer rapidamente que hace cada cosa.

Nos movemos ahora al paquete com.fluzo.sdk

Antes de entrar con la parte de las grabaciones, al principio de todo he dicho que en una captura habia algo gracioso, la gracia esta en que los devs de La Liga han hardcodeado el endpoint de la API de fluzo, mientras que en el propio SDK de fluzo se cifra el endpoint y se descifra al usarlo me imagino que por seguridad, aunque la funcion de descifrado tampoco es gran cosa.

Función de descifrado

Cifrado: Base64 <- rotN (n = -4) <- reverseString

Hay un par de funciones que llaman la atención en la clase Fluzo

El nombre es interesante pero poco más podemos saber a ojo.

Vamos a ver que busca al ver si f696C es un nulo, en la parte de declaraciones vemos private JSONArray f696C;

Buscando funciones que usen este objeto llegamos a m747c que llama a m777a pasándole true

Vamos a ver que hace la función isOnline

Comprueba si estamos conectados a internet, en caso contrario pues lo reintentará.

Volvemos a la función m747c 

Comprueba que ap no sea nulo private MatchListener ap; vamos a dar por hecho que no lo es para agilizar ya que no es relevante

La próxima comprobación no la entiendo muy bien, por que como comente antes esa primera funcion es para escribir logs y el argumento es el ID de usuario.

Luego tenemos f724f que es un booleano inicializado a falso asi que la pasa

Comprueba que f693g no sea nulo y si es nulo dice que el contexto es nulo. no deberia serlo asi que seguimos.

Ahora crea el objeto sharedPreferences a partir del xontexto f693g

Hace algunas reasignaciones y asigna al objeto un nuevo JSONArray pasando como argumento aa que es k098762563824 cifrado.

Por ultimo llama a MatchListener(aa) fluzoWillStartListening

Al final f705L es una flag para activar el modo debug a través del método setDebug

Por último en la linea this.f706M.m787a(); que hace unas comprobaciones sobre el estado del micrófono (no lo tengo muy claro esto)

Y por último llama a this.f706M.m788a(this.f732o, this.f696C); pasandole f732o que tiene el valor 16000 y f696C que es el objeto JSONArray con todas las preferencias

Asi que en conclusión lo que creo que hace esta función es configurar los parámetros para la grabación

Nos movemos ahora  a com.fluzo.sdk.p000a.C0840a

En esta nueva función los datos son

f748b = 16000

f753g = objeto con las preferencias de la función anterior

Llama a m818a pasándole el JSONArray

Busca “stops” en el índice 0 y si es iguual a 0 devuelve true.

Así que si esto pasa crea el log de startRecording warning: record pattern is empty, we will skip this request. y no graba, si ese índice no es 0 comprueba si C0840a.m784c devuelve true y si lo es empieza a grabar audio del micrófono.

No desarrollo android asi que tirare de instinto y docs  

La conclusión a la que llego es que comprueba que el micro este libre, luego empieza a grabar y si el estado es 0x3 (segun la API está grabando), así que para y devuelve true para grabar de verdad.

Volviendo a m788a

Inicializa el buffer en f752f

Crea el objeto audioRecorder en f749c :

AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

Fuente de audio = 1 = micrófono

HZRatio = i = 16000

Canal = 16

Formato = 2 (encoding_pcm_16bits) = WAV (?)

Tamaño de buffer = valor de f752f

Crea el hilo y empieza a grabar.

¿Por cuanto tiempo? No han definido tiempo, hasta que se llene el buffer.

Cuando esto ocurra si nada ha fallado se llamara a recordDidFinish (desde 761 a 1062) en com.fluzo.sdk.fluzo y es aquí donde se sube el archivo al servidor de fluzo. al endpoint /match .

Quedaria por responder la pregunta de cada cuanto tiempo se graba el microfono y se envian los datos para hacer el reconocimiento. La respuesta es no lo sé. Quizás cada 30 minutos también, pero no he encontrado la sección de código que lo confirme asi que no es más que una especulación.

Resumen y opinión

Después de analizar la aplicación y ver como La Liga por el motivo que sea, falta a la verdad sobre lo que hace su aplicación, me queda la duda de el por qué. Quiero decir, el hecho de grabar audio y geolocalizar para detectar emisiones fraudulentas me parece una idea bastante buena, pero ahorraría muchos mal entendidos explicar claramente a los usuarios qué se graba, para qué y a quién se le dan los datos para que los procese y con que finalidad así como facilitar el acceso a las politicas de privacidad que pueda hacer esa empresa de los datos igual que ya hacen por otro lado con el tema cookies facilitando la política de privacidad de Google Analytics.

Sobre la Startup Fluzo, evidentemente no puedo opinar con fundamento por que no tengo resultados objetivos, pero me imagino que si lo esta usando La Liga a la que le habra supuesto una inversion implementar el sistema es que la cosa funciona, si ese es el caso mis diez para los creadores de Fluzo por que como ya dije al principio me parece algo muy complejo lo que hace su servicio, mucho más que shazam o similares y que tanta repercusión tienen en la sociedad y a los que compañias como Apple echaron el ojo rápidamente.

*Algunas personas apuntan que es posible que la liga durante los partidos emita algún tipo de código inaudible para las personas pero que los micrófonos de los móviles si puedan captar para determinar si hay un partido de fútbol en el sonido ambiente. Esto lo veo mas fácil y factible que lo que había supuesto en un inicio.

Por último si hay algún fallo dejadlo por los comentarios o MD de twitter y así puedo corregirlo.

Hasta la próxima.

 

 

Anuncios

17 comentarios en “Analizando la app de La Liga para Android

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

w

Conectando a %s