martes, 17 de diciembre de 2019

BLECTF, Capture The Flag en formato hardware basado en Bluetooth Low Energy BLE + Write-Up

Hoy retomo la temática wireless para hablaros de BLE_CTF, un proyecto que nos ofrece la personalización de un módulo ESP32 para convertirlo en una plataforma "Capture the Flag" autónoma enfocada al uso de Bluetooth, lo cual nos permitirá empezar a familiarizarnos con los fundamentos básicos de las interacciones entre cliente y servidor Bluetooth LE.


BLECTF

BLECTF es una creación de Ryan Holeman @hackgnar y es una plataforma de 20 retos basados en Bluetooth Low Energy y en sus propias palabras "El propósito de BLE CTF es enseñar los conceptos básicos de las interacciones entre cliente y servidor de bajo consumo de energía Bluetooth. Su finalidad es la de enseñar y reforzar los conceptos básicos que son necesarios para sumergirse en el mundo del hacking Bluetooth". No suena nada mal, ¿no?.

El concepto de este CTF surge entorno a un proyecto DIY, donde deberemos contar con un hardware básico y barato, en el que "flashearemos" la plataforma después de compilarla y que en ultima instancia nos ofrecerá de manera totalmente autónoma 20 retos Bluetooth que deberemos resolver.

Vamos a empezar con ello!!



Comenzando por el comienzo! Fundamentos teóricos de BLE

Bluetooth Low Energy surge en la versión Bluetooth 4.0 y es un protocolo de comunicación inalámbrico que, entre otras cosas, permite una comunicación interactiva entre varios dispositivos con un consumo mínimo de recursos sirviéndose para ello de la de frecuencia 2.4GHz (al igual que las WiFi).

El protocolo utiliza un total de 40 canales de radiofrecuencia (con un ancho de banda de 2MHz cada uno) de los cuales 3 de ellos se utilizan para "anunciarse" (advertising)  y los otros 37 son para el trasporte e intercambio de datos.
Las peticiones de conexión entre los dispositivos y el comienzo de las mismas siempre se realiza mediante estos tres canales de Advertisement(Canales 37, 38 y 39).

Bluetooth Low Energy Channels and 2.4GHz Band with Wi-Fi Channels


Como el uso compartido del espectro de 2.4GHz es usando por dispositivos WiFi, Bluetooth y BLE, el protocolo establece un medio de funcionamiento para evitar las posibles colisiones en esa confluencia. Para ello el protocolo determina que el uso de los 37 canales de datos por parte de los dispositivos una vez que las conexiones se han establecido, se realiza mediante saltos entre los diferentes canales disponibles para datos, cuya ruta es conocida por los dispositivos interconectado en ese momento dado.
Del mismo modos, el protocolo define dos conceptos clave en este modo de utilización como con el Hop Interval (que determina los milisegundos que van a transcurrir antes del salto a otro canal) y el Hop Increment (que determina el número de canales que "saltaremos"y por tanto determina el canal de destino en el salto). La conjunción de estos dos elementos da lugar a otro nuevo "jugador" en esta partida que es el mapa de canales o Channel Map.

GAP y GATT

La pila del protocolo Bluetooth se divide en dos categorías: el controlador y el host. Cada categoría tiene a su vez subcategorías que desempeñan funciones específicas. Las dos subcategorías que vamos a examinar son el Generic Access Profile (GAP) y el Generic Attribute Profile (GATT).
En rasgos generales GAP define la topología general de la pila BLE mientras que GATT describe cómo se transfieren los atributos (datos) una vez la conexión es establecida.

El GATT se centra específicamente en cómo se formatean, empaquetan y envían los datos de acuerdo con las reglas descritas. En la pila de redes BLE, el Protocolo de Atributos (ATT) está estrechamente alineado con el GATT, donde el GATT se encuentra directamente encima del ATT. 



El GAP define varios roles para los dispositivos, pero por ahora lo único que debemos tener claro es que vamos a tener dispositivos centrales y los periféricos.
Los dispositivos periféricos son dispositivos pequeños, "sencillos", de bajos recursos y consumos (glucómetro, un medidor de pulsaciones, estaciones de temperatura y humedad, etc…) y que pueden conectarse a dispositivos centrales mucho más potentes.
Los dispositivos centrales se corresponde normalmente con dispositivos mas potentes y con mas recursos de memoria y procesamiento como teléfonos móviles, tablets, ordenadores, etc...

El GATT entra en juego cuando ya hemos establecido una conexión entre dos dispositivos (y por tanto, después de pasar previamente por el GAP) y lo más significativo del GATT es que las conexiones son exclusivas y esto es, en esencia, que cuando un dispositivo periférico se conecte a un dispositivo central de los expuestos en el párrafo anterior, este dejará de anunciarse y otros dispositivos ya no podrán verlo o conectarse a él hasta que se finalice la conexión existente.
Por todo ello, establecer una conexión también es la única forma de permitir la comunicación bidireccional en la que el dispositivo central puede enviar datos al periférico y viceversa.

GATT define dos roles en cada conexión: cliente y servidor. Existen algunos servicios y características de forma estándar predefinida que son representadas en formato de 16 bits, pero la ventaja del BLE es que permite a cada fabricante definir sus propios servicios utilizando un UUID de 128 bits para adaptar esta tecnología a aplicaciones propias.


Atributos en GATT

Los servidores GATT almacenan datos de información de diferentes tipos organizándolos en forma de atributos. Un atributo es un fragmento de datos etiquetados y accesibles. Su estructura es la siguiente:


  • El atributo Handle es un identificador único de 16-bits que nunca cambia y que le da al atributo la cualidad de accesible.
  • El atributo Type (UUID) determina que tipo de datos son almacenados en el atributo "Value" y utiliza un identificador único universal de 2 o 16 bytes. 
  • El atributo Value contiene el valor propiamente dicho de los datos a los que puede acceder un dispositivo cliente.
  • El atributo Permissions son metadatos que determinan las operaciones de acceso ATT permitidas para el atributo Value (lectura, escritura o ninguna) y los requisitos de seguridad (cifrado (y el tipo requerido del mismo) y requerir autorización).

GATT establece jerarquía basada en objetos anidados de alto nivel denominados: Perfiles, Servicios y Características, que se agrupan de la siguiente manera:



¿Y a que ha venido toda esta chapa infumable de teoría aburrida y que tiene que ver con el CTF? Pues porque, como veremos más adelante, los 20 retos de los que se componen las flag del CTF están identificados cada una por un handle, y será el value lo que deberemos leer, y en ocasiones modificar y remitir con las hints recibidas para obtener las flags. No sufráis, os prometo que a partir de ahora la cosa es menos pesada y entramos en el cacharreo que es lo que nos gusta a todos!.


El hardware

BLECTF corre sobre chipset ESP32, un SoC (System on Chip) diseñado por la compañía china Espressif y fabricado originariamente por TSMC que integra en un único chip un procesador Tensilica Xtensa de doble núcleo de 32bits a 160Mhz (con posibilidad de hasta 240Mhz), conectividad WiFi y Bluetooth.
De alguna manera entre la comunidad maker siempre se ha considerado como el "hermano mayor" o la evolución del clásico ESP8266 y de hecho  añade muchas funciones y mejoras respecto a el ESP8266 como son mayor potencia, Bluetooth 4.0, cifrado por hardware, sensor de temperatura, sensor hall, sensor táctil, reloj de tiempo real (RTC) y más puertos.



Podéis encontrarlo por un precio entorno a los 10€ en Amazon si sois de los más impacientes o entorno a los 4€ si no tenéis prisa por recibirlo y decidís pedirlo proveedores orientales.

Además del hardware para instalar y correr el CTF también deberemos de tener en cuenta el que vamos a utilizar desde nuestro ordenador para llevar a cabo los retos. Si bien algunos adaptadores bluetooth integrados en portátiles pueden ser suficientes para los retos, dependiendo del chipset que estos utilicen es posible que tengamos problemas en conectarnos al CTF o en determinados retos.

A mi personalmente me gusta contar en mi "arsenal Wireless" con un dispositivo totalmente dedicado para todos mis cacharreos con el mundo del Bluetooth y después de documentarme durante una temporada acerté de pleno haciéndome con un incombustible Parani UD-100 de SENA.




Flasheando el software

En primer lugar vamos a comenzar por instalar en nuestro equipo los pre-requisitos necesarios:

#apt-get install git wget flex bison gperf python python-pip python-setuptools python-serial python-click python-cryptography python-future python-pyparsing python-pyelftools cmake ninja-build ccache


Después debemos de descargarnos las librerías de software proporcionadas por Espressif en el repositorio ESP-IDF:

#cd ~/esp 
#git clone --recursive https://github.com/espressif/esp-idf.git


Además del ESP-IDF, también es necesario instalar las herramientas utilizadas por él como es el compilador, el depurador, los paquetes Python, etc. Y para ello podemos usar el script install.sh incluido en el paquete que acabamos de descargar dentro de la carpeta esp-idf



Debemos acompañarlo de un cambio en las rutas del entorno para que no tengamos problemas a la hora de compilar el proyecto:



Ahora estamos en disposición de compilar y flashear nuestro CTF y para ello lo primero es descargarlo desde el GitHub del creador y descomprimirlo en nuestro sistema. Acto seguido ejecutaremos "make menuconfig" lo que nos abrirá un menú de opciones y donde deberemos llevar a cabo una acción muy sencilla pero que a mi personalmente me volvió loco durante horas pues sin ella es imposible compilar y es, por lógico que parezca, habilitar Bluetooth en las opciones del framework!!
Esta configuración la encontraremos dentro de "Component config" y a su vez dentro de "Bluetooth".






Salimos del aplicativo (guardando los cambios antes!) y acto seguido podemos lanzar el comando "make" para llevar a cabo la complicacion del archivo bin que después grabaremos en el hardware.




Ya por último solo debemos conectar nuestro ESP32 a nuestro ordenador, asegurarnos que el dispositivo es reconocido para después ejecutar "make flash" y observar como comienza el  flasheo de la plataforma de retos en el ESP32. En solo unos minutos el dispositivo se reiniciará y ya estará anunciándose en el espectro inalámbrico como dispositivo BLE.





Primeros pasos con el CTF 

Antes de comenzar a pelearnos directamente con las flags, hay algunas cosas que debemos conocer y "poner en su sitio" de una forma mínimamente ordenada las cuales nos facilitarán el terreno para poder concentrarnos en resolver los retos propuestos, es una especie de fase de reconocimiento en el que unos de los primeros pasos será identificar nuestro dispositivo bluetooth local así como localizar en nuestras inmediaciones el ESP32 corriendo BLECTF.

Para ambas cosas nos ayudaremos de la suite de herramientas hcitool y vamos a comenzar por conocer como nuestro sistema ha nombrado nuestro dispositivo, usando el comando hcitool dev la herramienta nos muestra todos los dispositivos bluetooth que el sistema operativo haya reconocido, en nuestro caso al ser solo uno, nos lo ha nombrado como hci0.



El siguiente paso va a ser tratar de localizar el ESP32 que esta corriendo el BLECTF y para ello volvemos a usar hcitool esta vez con la opcion lescan que rastreará el espectro en busca de dispositivos BLE dentro del alcance, enseguida observaremos como aparece un dispositivo claramente diferenciado con el nombre públicado BLECTF, deberemos apuntarnos su dirección mac 30:AE:A4:7B:7E:72 pues vamos a usarla mucho de ahora en adelante.




Tal y como el autor nos indica en su Github, otra herramienta que necesitamos conocer y con la que es mejor que nos familiaricemos lo antes posible es GATTTOOL, una herramienta enfocada al BLE y que nos permitirá interactuar con los servicios GATT de nuestra unidad de hardware CTF.

Con gatttool podemos descubrir, leer y escribir características la estructura de datos de características y atributos que leímos en el aburrido apartado de teoría.



Comenzamos solicitándole al dispositivo los servicios ofrecidos con el comando gatttool -b 30:ae:a4:7b:7e:72 --primary donde obtendremos tres resultados cada uno con su UUID individual definido, y de los cuales solo uno no esta definido por la norma (00001800) y por tanto esta personalizado, por lo que es él quien alberga el CTF.



Si queremos observar donde están los 20 retos del CTF con sus respectivos handles usamos el comando gatttool -b 30:ae:a4:7b:7e:72 --characteristics 




Según el propio autor no sindica en la Github del proyecto, hay un comando que vamos a usar bastante a partir de ahora y que nos va a servir para saber nuestra puntuación y comprobar, después de meter las flags, que estas son correctas y suben al marcador. Esa puntuación se almacena en el handle 0x002a y el comando que nos sugierem el creador es: gatttool -b 30:ae:a4:7b:7e:72 --char-read -a 0x002a|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'
Como vamos a usarlo en numerosas ocasiones y no es un comando precisamente corto, es buena idea meterlo en un archivo bash para que sea mas fácil su ejecución.





Con solo darle permisos de ejecución nos habremos hecho la vida mucho mas fácil y bastará ejecutarlo para consultar la puntuación en todo momento.




Del mismo modo que nos hemos ayudado de bash para facilitarnos la consulta de nuestra puntuación al ESP32, podemos repetir el mismo procedimiento con el resto de comandos que vamos a necesitar de forma repetitiva como es el de leer el contenido del handle.
Como este varia con cada flag, deberemos configurare nuestro ejecutable en bash para pasarle como argumento el valor del handle con '-a $1'




Y ahora ya si, por fin, lo tenemos todo en su sitio y preparado para empezar a enfrentarnos a los 20 retos de la plataforma. Manos a la obra!!


WRITE-UP

Flag 1

La primera flag la debemos hacer tan solo siguiendo las indicaciones que nos da el propio autor en su hint correspondiente y es un "regalo" para que nos familiaricemos con con el modo en el que vamos a subir las flags a la plataforma.



Dicho y hecho! Seguimos sus indicaciones al pie de la letra y  recibimos un mensaje indicando que se ha grabado correctamente el valor en esa característica, comprobamos la puntuación con nuestro ejecutable en bash y efectivamente tenemos un punto correspondiente a esa primera flag!! 



Lo que aprendemos con esta primera flag es que los valores vamos a tener que meterlos en el handle de lectura y escritura 0x002c, por lo que quizás no es tan mala idea volver a facilitarnos la vida y crear otro archivo ejecutable en bash que memorice por nosotros el comando completo y cuyo valor de la flag le pasemos como argumento para utilizarlo de ahora en adelante. Es importante respetar xxd para que la grabe en formato hexadecimal.





Flag 0x002e 

En esta flag lo que nos piden es que leamos el handle 0x002e y el valor resultante sea el que metamos como flag en el handle 0x002c. Manos a la obra: comenzamos por leer el handle para obtener el valor del mismo.


Ahora procedemos a usar nuestro archivo creado en el reto anterior para remitir la flag y acto seguido consultar del mismo modo la puntuación y observar si contamos con un punto más y por tanto la flag era correcta.




Flag 0x0030

Comenzamos por leer el handle 0x0030 y nos indica que la flag consiste en el MD5 del nombre del dispositivo.



Averiguaremos el valor necesario usando "echo -n BLECTF | md5sum" para acto seguido remitir esos 20 primeros caracteres con nuestro archivo ejecutable y al comprobar la puntuación observamos que ya tenemos un punto más en la saca!



IMPORTANTE: en todas las flags, el string que debemos pasarle como argumento debe estar limitado a los 20 primeros caracteres. Podéis implementar una función en el ejecutable para que trunque la cadenas a esos 20 primeros caracteres).



Flag 0x0016

La hint de esta flag nos invita a encontrar el valor de [Generic Access - Device Name]. Para ello vamos a comenzar una sesión interactiva con el ESP32 mediante gatttool.
Comenzamos abriendo una sesión interactiva y conectando al dispositivo con el comando "gatttool -b 30:AE:A4:7B:7E:72 -I" y despues usando el comando "connect":



Acorde a la norma del BLE, el UUID que debe almacenar el nombre del dispositivo es 00002a00, por lo que debemos is pidiéndole al ESP32 las características de los servicios ofrecidos hasta localizar ese UUID, lo vamos a encontrar dentro del handle 0x0016:



Aprovechamos la sesión interactiva conectada al dispositivo para leer el valor del handle y obtendremos el valor en hexadecimal.



Podemos remitir la flag en formato hexadecimal truncando la cadenas a los primeros 20 caracteres como ya sabemos y usando gatttool, o podemos convertir la cadena aunque sea mas perdida de tiempo si después lo que queremos es usar nuestro archivo ejecutable para remitir la flag.



Ya solo nos queda meter la flag y consultar el score para ver como otro tanto mas a subido al marcado a nuestro favor.




Flag 0x0032

Comenzamos leyendo el handle 0x0032 y observamos que nos pide sobrescribirlo con, literalmente,  cualquier cosa.



Vamos a escribir por ejemplo la string ""Hacker De Cabecera" con gatttool, una vez hecho leemos de nuevo el handle y observamos como efectivamente ya ha cambiado por lo que acabamos de escribir.



Remitimos la flag y comprobamos puntuación: another one bites the dust!!!




Flag 0x0034

La hint de esta flag nos invita a hacer lo mismo que en la anterior: rescribir un handle con una cadena ascii concreta, en este caso debe ser "yo".



Repetimos el proceso de la flag anterior y escribimos la cadena indicada en el handle 0x0034 para posteriormente meter nuestra flag.




Flag 0x0036

Para esta flag de nuevo nos piden escribir sobre un handle, pero en esta ocasión nos piden que sea el valor hexadecimal 0x07. Nos volveremos a valer de gatttol y de la opción --char-write-req igual que las flags anteriores. 



De nuevo solo nos falta meter la flag y comprobar que es correcta.



Flag 0x0038

En esta ocasión el handle nos solicita escribir en un handle diferente a él mismo y nos pide que rescribamos el valor concreto 0xC9.



Procedemos del mismo modo que en las flags anteriores y reescribimos el handle 0x0058 y al volver a leer el 0x0038 observamos que ya ha cambiado.
Solo nos queda meter la flag y comprobar que el punto ha subido a nuestro marcador.




Flag 0x003c

Aquí la cosa se puse interesante: este handle nos reta a hacerle fuerza bruta desde el valor hexadecimal 00 hasta el valor hexadecimal ff.



Hablando "en cristiano" o mejor dicho en decimal, nos pide hacerle fuerza bruta entre el valor decimal 0 y el valor decimal 255. Para ello podemos organizar desde la propia consola un bucle en bash para que itere por cada valor comprendido entre las indicadas.



Una vez que haya finalizado, volvemos a leer el handle 0x003c y observamos que ya ha cambiado su valor, por lo que procedemos a meter la flag y sumar otro punto más.




Flag 0x003e

Otro reto interesante! En esta ocasión el handle nos pide que lo leamos... nada más y nada menos que 1000 veces!!



Como el usar el cursor superior y pulsar el intro durante 999 veces más no suena del todo interesante... será mejor que confeccionemos otro bucle en bash que lance durante 1000 veces nuestro archivo ejecutable para leer el handle.



Después de un rato corriendo nuestro bucle, observaremos como el valor a pasado de ser "Read me 1000 times" a "6ffcd214ffebdc0d069e".



Metemos la flag y comprobamos la puntuación para dar por superada esta flag.




Flag 0x0040

Al leer el handle lo que nos solicita en esta ocasión es que nos mantengamos a la escucha del mismo a la espera de alguna notificación.



Para ello vamos a grabar un valor de 0100 con gatttool que junto a la opción --listen para quedarnos a la escucha (los valores para mantener a la escucha corresponden a 0100 para notificaciones, 0200 para indicaciones, 0300 para ambos). 
Después de solo unos segundos obtendremos la notificación necesaria.



Ya solo nos falta remitir la flag y comprobar el marcador: ya hemos superado la mitad de los retos!!!




Flag 0x0042

En esta ocasión al leer el handle lo que nos indica es que debemos escuchar el handle 0x0044 por una "indicación".



Como hemos visto en el reto anterior con la opcion --listen podemos usar valores de 0100 para notificaciones, 0200 para indicaciones y 0300 para ambos, como nos indica explícitamente que escuchemos una indicación usaremos el valor 0200 en esta ocasión.
Después de solo unos segundos obtendremos la notificación necesaria.



Ya solo nos falta remitir la flag y comprobar el marcador




Flag 0x0046

Leemos el handle y observamos que nos pide exactamente lo mismo que en la flag anterior pero en esta ocasión habla de varias notificaciones.



Repetimos el mismo comando que en la flag anterior y recibimos una primera notificación diferente de la segunda y de las consecutivas.



Las convertimos y al hacerlo podemos observar claramente como la primera podemos desecharla directamente (U no want this msg) y la segunda es la que tiene mejor pinta para nuestro propósito.



Probamos suerte con esa segunda notificación y: voilá! Lo tenemos!




Flag 0x0048

Este handle es un poco extraño... porque básicamente no esta solicitando lo mismo que en el reto anterior, solo que en esa ocasión nos pide escuchar las múltiples notificaciones en un handle diferente a si mismo.



Repetimos por tanto todos los pasos que en el reto anterior pero esta vez sobre la flag 0x004a y terminaremos llegando a la flag necesaria y subiéndola al marcador del mismo modo.






Flag 0x004c

En este reto nos indican que para obtener la flag debemos de interacturar con este handle con una dirección MAC determinada en nuestro adaptador, concretamente la 11:22:33:44:55:66.



Para falsear la dirección MAC de nuestro adaptador (OJO! Puede que no todos los adaptadores Bluetooth LE permitan realizar esto) vamos a usar la herramienta spooftooph indicándole nuestra interface y la nueva MAC que queremos obtener.



Nos aseguramos que el cambio se efectivo y correcto con hcitool dev.



Con la dirección MAC que nos han indicado en nuestro adaptador volvemos a leer las características del handle y procedemos a meter la flag con el resultado que nos devuelve para así subir otro punto más a nuestro marcador.




Flag 0x004e

En esta handle  se nos invita a jugar con el valor MTU (Unidad máxima de transferencia, la cual expresa el tamaño en bytes de la unidad de datos más grande que puede enviarse usando un protocolo de comunicaciones).



Para ello vamos a conectarnos mediante gatttool con una sesión interactiva al dispositivo y una vez conectado vamos a usar el comando mtu para asignarle valor 44 como se nos ha solicitado y acto seguido leemos de nuevo el handle 0x004e y obtenemos la cadena hexadecimal.



Remitimos la flag y... ya estamos mucho mas cerca de la meta!!



Flag 0x0050

Este handle nos pide escribir "hello" pero parece indicarnos que también debemos esperar respuesta.



Para llevarlo a buen puerto en vez de usar el modificador --char-write, que tan solo escribe pero no espera ninguna respuesta, debemos usar --char-write-req que igualmente realiza una petición de escritura peor además espera una respuesta por parte del servidor.
Después de hacerlo leemos de nuevo el handle y observamos que ya ha cambiado.



Remitimos la flag y lo tenemos. Ya solo nos quedan tres retos pendientes!!!




Flag 0x0052

Leyendo este handle obtenemos un misterioso mensaje en el que afirma que no hay notificaciones en él, pero con un "really" que suena sospechoso...



Si hacemos caso omiso y repetimos el procedimiento de flags anteriores en el que vamos a permanecer a la escucha de notificaciones y... tenemos suerte! Recibimos una notificación!



Metemos la flag con ese valor recibido y hay suerte! una más!




Flag 0x0054

Este handle nos indica que hay demasiadas propiedades!



Básicamente lo que viene a indicarnos es que la flag completa esta separada en dos partes y tenemos que reconstruirla utilizando dos técnicas que ya hemos visto en los retos anteriores: para ello en primer lugar vamos a escribirla con un valor alto, la leemos y tendremos media flag. Para la segunda mitad lanzamos request quedando a la escucha y capturamos la segunda mitad de la flag en modo de notificación.




Flag 0x0056

El último reto no es difícil, pero es un buen colofón al CTF: al leer en handle vemos que os pide el MD5 del Twitter del creador.



Es repetir el mismo proceso que en uno de los primeros retos que si recordáis nos pedían el MD5 del nombre del dispositivo, pero esta vez con el Twitter del creador que es @hackgnar así que vamos a ello.



Y ya solo nos falta probar suerte y meter la flag, comprobar puntuación y observar si por fin hemos finalizado el CTF!!






Y esto es todo por ahora! Hemos terminado el CTF! Enhorabuena si lo habéis seguido y habéis llegado hasta la meta, pero sobre todo por el mérito que tiene que hayáis leído hasta este párrafo!
Siento que al final haya quedado un post tan extenso y siento haberos metido toda esa morralla de teoría al principio, pero si de algo me ha servido personalmente este proyecto más allá de "jugar el CTF" ha sido para obligarme a documentarme sobre Bluetooth y BLE y, aunque por supuesto aún me queda un mundo por aprender sobre ello, consideré necesario e interesante presentar algunos de los fundamentos con los que más adelante íbamos a interacturar en mayor o en menor medida a lo largo de los retos.



Espero que lo hayáis disfrutado y os haya sido de utilidad, si os animáis a hacer el proyecto y experimentar con ello y tenéis cualquier duda en la que pueda ayudaros no dudéis en contactarme!!


4 comentarios:

  1. Respuestas
    1. Jajajaja tu si que eres un crack! Mil gracias por el comentario y por pasarte por aqui!

      Eliminar
  2. Gran trabajo tron! Se ve que hay invertidas muchas horas tanto de aprendizaje como de querer enseñarlo con el mimo que tratas todos tus articulos!

    Enhorabuena por otro gran articulo!

    ResponderEliminar
    Respuestas
    1. Se ha hecho lo que humildemente se ha podido! Muchas gracias por el comentario!

      Eliminar