En la última entrada lo dejamos justo cuando la cosa se ponía interesante: tenemos un puerto UART disponible, y conocemos la distribución de los pines.
En esta entrada vamos a poner a prueba la información que conseguimos y ver hasta donde podemos llegar.
Configurando Bus Pirate
Como hasta ahora, todas las pruebas las he estado realizando desde un equipo con Linux (no concibo otra manera de dedicarte a hardware hacking, si eres usuario de Window$, mucha suerte en el viaje). Por lo tanto, lo que mostraré a continuación son métodos que poner en marcha desde dicho sistema operativo.
Vamos al tema: conectamos los cables a los pines que soldamos previamente:
Yo personalmente utilizo minicom, pero también se pueden utilizar PuTTY o incluso screen para lo mismo. En el programa que elijas, tendrás que configurar los parámetros de la conexión serial con BusPirate (no confundir con la conexión UART al dispositivo, todavía no hemos llegado ahí).
En Linux, para conocer el dispositivo virtual asignado a la conexión miraremos el log de kernel, dmesg:
Y, de la documentación provista por Dangerous Prototypes sabemos que se comunica a 115200 baudios. Por lo que el comando que lanzo para la conexión es:
1 |
sudo minicom –D /dev/ttyUSB0 –b 115200 |
Por defecto minicom activa el control de flujo por hardware, lo que hace que no se puedan enviar comandos correctamente al dispositivo. Para desactivarlo, presiona la combinación Ctrl-A y después O para entrar en el menú de opciones, accede a la configuración de la puerta serial, y pulsa F para cambiar el modo. Esto se puede guardar en la configuración de minicom para no tener que realizarlo cada vez que se establece la conexión.
Una vez tengamos todo correctamente configurado, veremos la interfaz de BusPirate:
Para acceder a la ayuda, enviaremos el comando “?”.
El primer paso es cambiar el modo de aplicación. El que inicia por defecto es el modo de alta impedancia (HiZ), para mantener la seguridad de todos los pines. Nosotros seleccionaremos el modo UART y, seguidamente, configuraremos los parámetros de dicha conexión. Ya que no los conocemos, vamos a dejar los predeterminados, y utilizar un pequeño truco bajo la manga:
Por ahora tenemos una conexión UART 8N1 a 300 baudios.
Cada modo de operación incluye una serie de macros pre-grabadas, para no tener que programar o realizar a mano las lecturas y escrituras del dispositivo. Estas macros se listan con el comando “(0)” (con los paréntesis incluidos). Los que incluye el modo UART son:
Para comprobar si la configuración por defecto funciona correctamente, vamos a seleccionar la macro “Live monitor”, y encendemos el router:
Algo me dice que no está recibiendo correctamente la información. En este punto hay muchas cosas que pueden estar fallando: los cables hacen mal contacto, los pines están mal soldados, o la configuración de la conexión es incorrecta. Siguiendo la Ley de Makinavaja de Occam, ya que hemos dejado todos los parámetros por defecto, probablemente sea el último caso.
Atendiendo a la página de Wikipedia sobre UART, 8N1 es el esquema de data packaging más común, por lo que vamos a probar cambiando la velocidad de transmisión de datos. Para esto utilizaremos el truco del que hablaba antes, y que se puede intuir en el menú de macros de UART: “Auto Baud Detection”.
Confiando en que todo lo demás funciona, vamos a arrancar de nuevo el router, dejarlo unos 10 segundos (muchos bootloaders tienen un tiempo de margen para entrar en modos de recuperación), y seleccionamos la macro 4:
Feel like a hacker, ya hay una tool que nos hace el trabajo sucio (comprar un osciloscopio, romperlo intentando aprender a utilizarlo, y medir la frecuencia de cambios de señal del pin). BusPirate se está ganando el sueldo.
Reconfiguramos el modo de operación (como atajo, puedes entrar directamente a los parámetros de UART con m3, te saltas un paso), y volvemos a la macro “Live Monitor”, reiniciando el router por el camino:
¡Lo tenemos! Para tener una terminal completamente interactiva, vamos a utilizar la macro “Transparent Bridge” (en algunos dispositivos me ha dado problemas. Si es el caso, probad a user “Bridge with Flow Control”):
Por un lado, genial, tenemos una interfaz con el dispositivo. Por otra, fuck, las credenciales básicas que vimos repasando el firmware no funcionan… es aquí donde empieza a tomar relevancia la parte de reversing.
Extracción del firmware desde el Bootloader
Para todos aquellos que se asustaron al ver tanto cable y pinza en el segundo post de esta serie, ahí va un método más «sencillo».
Este apartado depende en gran medida del dispositivo con el que estemos trabajando. Dependiendo de múltiples factores, seremos capaces o no de ver una shell en el puerto serial al que estamos conectados, interactuar con ella, y acceder al menú del bootloader. Si se dan todas estas condiciones, todavía queda un último escollo: necesitamos una opción para leer memoria del dispositivo.
Si milagrosamente (no tanto, pero hay que darle un poco de épica a esto) se dan todas las condiciones indicadas, estaremos muy cerca de poder realizar el volcado del firmware.
Para ello, necesitamos conocer la estructura de la memoria, es decir, la dirección de memoria donde se copia el firmware durante el arranque, y el tamaño de la misma. Esto también varía de un dispositivo a otro, pero es común que toda esta información esté presente en la salida de depuración del arranque. Para poder recogerla toda sin miedo a perder algún fragmento, vamos a usar el método “logging” y guardar la salida del programa a un archivo.
Para ello, entramos en el menú de minicom con Ctrl-A + Z, e iniciamos el log con “L”. Aceptamos, y reiniciamos el router, dejando que se complete el arranque.
Una vez termine, inspeccionamos el archivo de log en busca de información sobre la estructura de la memoria:
Vemos que la flash se mapea en la dirección 0xbd000000 de memoria y que ocupa 16MB. Esta memoria está distribuida en las siguientes particiones:
- Boot: 4KB en 0x0
- RootFS: 3MB en 0x1000
- Data_store: 20KB en 0x3f0000
- Data_store_bk: 20KB en 0x3f5000
He invertido un poco de tiempo en sacar brillo a mi bash-fu y he llegado a automatizar la conversión de los tamaños. Por ejemplo, para rootfs:
1 |
echo "$(( (16#003f0000-16#00010000)/1024**2 ))" |
La mayoría de bootloaders, si no están capados, nos permiten hacer un volcado de memoria de la RAM con una dirección y un tamaño. Es nuestro caso, con el comando “d”. Como no disponemos de ayuda, nos apoyamos en ensayo y error para sacar los tipos de parámetros requeridos:
Parece que hemos dado con la clave, el primer parámetro es la dirección de memoria en 32 bits y el segundo el tamaño en bytes. Si no se ajusta a un múltiplo de 16, lo redondea al alza.
Para confirmar todo lo que llevamos hasta ahora, vamos a buscar la cabecera del sistema de ficheros. Partimos de la base de la memoria (0xbd000000), y que el offset entre la base de la flash y el sistema de ficheros es 0x1000. Por tanto, los sumamos, y escupimos 16 bytes para que imprima una línea:
Ahí está, “sqsh” es un formato conocido de sistemas de archivos (Squashfs). Por lo tanto, si nuestros cálculos son correctos, con
1 |
d 0xbd010000 4063232 |
deberíamos extraer toda la partición del sistema de archivos. De nuevo, para no perder información, vamos a grabar la salida del programa a un archivo raw que después convertiremos en un stream de bytes.
Una vez terminado este proceso (de nuevo, son 3MB volcados por una interfaz serial, es extremadamente lento), necesitaremos limpiar la entrada. Otro poco de bash-fu, y lo dejamos limpito y listo para ser interpretado:
1 |
cat td5130.raw | sed -r 's/^.{12}//g' | sed -r 's/^(.{48}).*$/\1/g' | tr -d '\n' | tr -d " " > td5130_fs.dump |
Sin embargo, por ahora tan sólo tenemos un archivo de texto con un chorro de letras y números, no se trata de código. Con el programa xxd podemos interpretar dichas letras como si fuera código, y guardar la salida en un archivo – ahora si – binario:
Al ver que se trata de un sistema de archivos SquashFS el paso obvio sería ejecutar la herramienta unsquashfs para extraerlo. Sin embargo, su ejecución falla:
De nuevo, hay mucho que comprobar. Puede tratarse de que hemos realizado mal los cálculos, que ha fallado algún punto durante la extracción, o que el fabricante está aprendiendo a proteger sus equipos modificando los binarios (jaja lol nope).
Resulta que en el mundo de los routers esta última práctica es relativamente común. Esto no significa que sea imposible extraerlo, si no que alguien le va a dedicar más tiempo, y, como es de esperar, existen herramientas que solucionan este problema.
Ya tardábamos en nombrar binwalk en este apartado. Entre las muchas herramientas que integra se incluyen modificaciones comunes a sistemas de ficheros como el que nos ocupa, de manera que podemos extraerlo con un “simple” binwalk –e:
¡Por fin! Tanto rodeo, tanto meme metido con calzador, pero al final llegamos a hacerlo.
Se que hay miles de puntos donde las cosas no os pueden salir del mismo modo que lo expuesto hasta ahora, es normal. Para esos casos, hay dos principios básicos del mundo, el universo, y mayormente de hacking: búscate la vida, y RTFM.
Conclusión
De nuevo nos encontramos con parte del firmware en nuestras manos. Ahora no queda otro remedio que aprender a analizarlo y hacer reversing. Intentaré tenerlo lo antes posible, pero llegados a este punto tengo mucho que aprender antes de ponerme a escribir de nuevo.
Espero que haya resultado interesante. ¡Un saludo!
Eriatarka.