2024/09/18

Pager Bomb

(Artículo en borrador completo, puede ser que luego agregue información o diagramas, última modificación 2024-09-19 03:xx)

A raíz del reciente atentado de Israel a Hezbollah se ha abierto una discusión de cómo pudo haber sido implementado el ataque y no puedo dejar de opinar técnicamente del asunto. No me voy a calificar de "hablemos sin saber" pues vengo estudiando asuntos equivalentes desde hacer rato en mi serie de experimentos y charlas relacionadas con Malware en Hardware (citas a completar) pero considerá que mi visión es completamente académica y hobbysta.

Tampoco he leído todas las noticias y discusiones, sólo estoy al tanto de que Snowden dijo que no parecía haber sido solo la batería el explosivo pues no se aprecia en los videos la explosión normal de una batería.

Se ha dicho que el ataque afectó a varios países y que aparentemente llegó un mensaje previo.

A partir de esa información y mi mejor criterio, voy a desarrollar un escenario factible de ataque.

Como todo ataque, consiste de un payload y un evento de activación. En este caso el payload es el explosivo y el elemento de activación la detección de alguna condición y su implementación física, esto es, el encendido del explosivo a partir del evento.

Dado que la activación fue simultánea evidentemente el evento de activación ha sido un mensaje ya sea dirigido a cada dispositivo o un mensaje de broadcast, que no sé si existe en esa tecnología.

Consideremos elementos de escenarios alternativos partiendo de dos aspectos: cómo llegó la carga explosiva al artefacto y cómo se activó. Antes, una esbozo de la cadena de suministro apostando a que el pager tiene como un celular dos componentes principales: una cpu y una radio.

 

  • Fabricación de los componentes
    • Integrados
      • CPU/memoria/control de display/ input-output
      • radio
    • Display y botonera
    • Circuito impreso
      • Los integrados más circuitos adiciones más fuente
    • Batería
    • Carcaza 
  • Software
    • Firmware CPU
    • Firmware radio
    • Ambos
      • diseño
      • compilación
      • burning
  • Ensamblado de todo lo anterior
  • Distribución

Consideremos además que para cada componente y flujo pueden estar involucrados distintos proveedores.

 

Explosivo

Ya descartamos la batería por si misma.

Pudo haber sido un agregado en espacios libres dentro de la carcaza (investigar si los hay)

Argumentos en contra

Si la modificación no se hace en el ensamblado, hace falta afectar la cadena de suministro en la distribución y lleva mucho tiempo por ejemplar pues hay que desarmar y volver a armar. Quizás haya que lidiar con etiquetas de precintado. Si no tiene tornillos pueden quedar marcas en la carcaza.

Es muy fácil de detectar, incluso alcanza una sencilla inspección visual.

Activación

 

Software: se modifica el firmware de la CPU o de la radio para que al recibir un mensaje active el circuito de encendido.

Hardware: se agrega un circuito extra que hace sniffing sobre los mensajes y al detectar un cierto mensaje o patrón, activa el explosivo.

Argumento en contra

Hay que afectar la cadena de suministro en múltiples puntos.

 

Propuesta de explosivo y activación


La idea que me parece más sencilla es:

Que se incorpora el explosivo a la batería y se agrega un circuito en la misma de inciación del mismo. De este modo la inspección implicaría desarmar la batería o usar rayos X, la visual no sirve y menos si se mantiene el peso.

La activación está dada por una de las siguientes posibilidades.

SCA: (Side Channel Attack) un ataque contra componentes de hardware y la ejecución del software consiste en analizar el consumo y sus variaciones para determinar que está haciendo una CPU. Si sabemos que hay una operación o mensaje que produce un patrón de consumo particular, el circuito, que sería una CPU extra, lo detecta y activa. El SCA tengo entendido que, al menos contra sistemas criptográficos, requiere muchos mensajes.

Más sencillo, si una cierta operación o una falla de software produce un consumo excesivo durante un tiempo determinado, como puede ser entrar en un loop infinito por una falla lógica o un buffer overflow, el circuito, que bien podría ser analógico, lo detecta y se activa.

En resumen, tenemos una batería del mismo tamaño y aspecto a la original que incorpora el explosivo, el circuito de iniciación y la lógica de activación que consiste en observar la variación del consumo, en particular un consumo elevado durante un período determinado.

Este escenario es el más fácil de implementar en la cadena de suministro y es absolutamente natural: sólo hay que cambiar o directamente proveer a ese lote con las baterías adulteradas.

Seguramente a alguien se le ha ocurrido esta idea también, ya veremos que se analiza y divulga.





2024/01/29

Análisis de entrega de material ciberciruja

Un poco como actualización de wipe, donde explico cómo disponer de modo seguro y eficiente de medios magnéticos y señalo lo ineficiente que me resultó donar material de computación, cosa sobre la cual reflexiono y vuelvo a recalcar en cyberciruja 2023, a raiz de la experiencia del domingo 2024-01-28, reviso un poco esas ideas.


Por motivos personales tuve que hacer una purga y disponer de bastante material, calculo que un tercio de metro cúbico, detallado en material cyberciruja (Listado de la purga 2024/01/28)

 

Parte del lote
Parte del lote

 


El método consiste en preparar el lote (algún detalle extra más abajo en "Actividad común a cualquier metodología de donación"), coordinar con el grupo cyberciruja ubicación y fecha/hora, conseguir transporte, solicitar la ayuda de una o dos personas, llevar todo, explicar a quien manifieste interés por cada pieza qué es, para qué sirve y su estado y listo.


En comparación a experiencias anteriores, resultó muy eficiente. Para cada pieza  de wipe calculo que le habré dedicado dos horas promedio pese a que cada una podía ser más interesante que cualquiera del lote actual y llevó meses en tiempo bruto. En este caso mi esfuerzo invertido fué más o menos este:

 

Tiempo bruto

 

Desde que me decidí hasta que ocurrió dos semanas, pero pudo haber sido una.

 

Tiempos netos

 

Menos de 10 horas, incluyendo la disposición segura

 

Conseguir cajas

 

20 minutos repartido en varios días

 

Elegir que purgar

 

2 horas, la clave es elegir poco, si hay algo que es muy interesante, como por ejemplo una dataset de commodore o unas netbooks del gobierno de las primeras y hace falta hacer el borrado seguro, descartar y dejar para otra oportunidad.

 

Coordinar en el grupo

 

menos de 2 horas, es avisar "tengo todo esto, ayúdenme, cuándo podemos?"

 

Llevar todo al parque

 

1 hora, depende de la distancia y el volumen de cosas....

 

Explicar y repartir

 

2 horas, compartido por las personas presentes. Primero hay que atender al cyberciruja. Segundo hay que avisarle a quien pasa que el material está disponible y sus condiciones de gratuitidad. En ambos casos, explicar lo que sepas de la pieza en cuestión, si funciona, si es a probar o reparar.

 

Actividad común a cualquier metodología de donación


Y ahí es dónde entra al seguridad, es el tema de que los medios magnéticos no deben contener información. Esto implica borrado "seguro" o destrucción.

En este caso hubo un solo borrado de un ide de 80GB.

Tenía unos diez discos rígidos que no funcionaban asi que no se podían borrar, pero al ignorar que información tenían, no podía tampoco entregarlos. Puede parecer medio estúpido pero así funciona mi mente.

Lo que hice fué desarmarlos (de ahí salió "montón boards de discos rígidos") para quedarme con los platos, los motores y los imanes. Esto me llevó unas dos horas.

Los imanes suelen estar pegados a una chapita, basta con retorcer la chapita con dos pinzas para que el adhesivo falle y quedarse con el imán. Estas chapas junto a las carcazas se las entregué en mano a un cartonero con carrito en cualquier otro momento.

Respecto a cd/dvd, entregué todos los que tenían drivers o programas, un poco confiando en que los grabados por mí no tuvieran información. De todos modos los únicos que suscitaron interés fueron los "impresos", tipo encarta 2000.

De souvenir exclusivo para cybercirujas, repartí unos discos ópticos locos, confiando en que no tenían nada y que nadie tiene el lector.

Los diskettes elegí que no tuvieran etiquetas o estuvieran en blanco, confiando....


Anécdotas


Una persona estaba muy interesada en recuperación de componentes electrónicos, en particular motores, se llevó entre otras cosas:

  • componente lectora triple de cd
  • componente dual deck

 

Gran interés tuvieron:

  • trafo original atari 110v
  • gabinete de la alarma casera
  • tablet coby
  • dock thinkpad con fuente y llave
  • tv philips 40 tocado por un rayo

 

Este tipo de evento, aunque en esta ocasión tuvo un fuerte protagonismo de una persona, puede ser más "cada uno lleva un poco". De hecho algunas personas trajeron alguna cosita y el encuentro también sirvió para intercambiar cosas que estaban esperado a cambiar de manos.

 

Conclusión

 

Aunque quizás no séa óptima la reutilización por componente, capaz que alguien se llevó un componente útil para sacarle el cobre, el método presentado, entre los extremos posibles, es más eficiente para quien se está deshaciendo de material que la donación pieza por pieza con seguimiento por un lado y por el otro, probablemente genere menos residuos tirados en la calle que tirar todo en la basura.


El equipo desde un árbol
El equipo desde un árbol




2023/12/03

Para leer Emerging Topics in Hardware Security - General

Esta nota es el resultado de la lectura atenta del libro Emerging Topics in Hardware Security, registro mis opiniones, inquietudes y hallazgos, apuntando a que sea útil para mí como memoria y para tí como disparador.

Desde hace meses sino años, tengo una pila de libros para leer, pero todos tienen alguna parte que es mejor practicar, incompatible con vacaciones, transporte público. Al ser este libro muy teórico, me basta con la lectura atenta y tomar notas... estas son las notas.

 

Empecemos con que no es un libro, es un compendio de papers.

En general, los artículos son, tal como insinúa el título, temas en la cresta de la ola y además de un nivel técnico muy alto y de temas muy específicos. Eso hace que el lector, Charly, por momentos quede un poco afuera. No es que no haya ocurrido con otros libros, pero en los otros no tuve la siguiente sensación:

Parecen escritos por personas que no dominan el idioma inglés, no lo digo porque los nombres de todos los autores parezcan ser de la India, sino porque efectivamente hay expresiones, formas de armar la frase y algunas cosas que me parecen que son errores gramaticales. También hay errores de sintaxis.

No señalaría esta superficialidad, pero como no conozco algunos de los temas, me produce una cierta desconfianza. No mucha, conozco gente del grupo de embebidos que me consta que saben un montón pero no escribir.

No sé cual es la tasa de entradas bibliográficas normal para un paper, pero con respecto a algunos libros conocidos, es mayor y en algunos capítulos muy mayor:




páginasnotasn / p
1Blockchain-Enabled Electronics Supply Chain Assurance25481.9
2Digital Twin with a Perspective from Manufacturing Industry351454.1
3Trillion Sensors Security341514.4
4Security of AI Hardware Systems16372.3
5Machine Learning in Hardware Security36852.4
6Security Assessment of High-Level Synthesis24401.7
7CAD for Side-Channel Leakage Assessment28451.6
8Post-Quantum Hardware Security301013.4
9Post-Quantum Cryptographic Hardware and Embedded Systems28531.9
10Neuromorphic Security24612.5
11Homomorphic Encryption28642.3
12Software Security with Hardware in Mind26612.3
13Firmware Protection22211.0
14Security of Emerging Memory Chips34762.2
15Security of Analog, Mixed-Signal, and RF Devices28632.3
16Analog IP Protection and Evaluation52671.3
17Application of Optical Techniques to Hardware Assurance22602.7
18Computer Vision for Hardware Security34752.2
19Asynchronous Circuits and Their Applications in Hardware Security28682.4
20Microfluidic Device Security24682.8

Total57813892.4

 


páginasnotasn / p
Artificial Intelligence - Russel/Norvig10009000.9
Object Oriented Software Construction - Meyer12003600.3
Security Engineering - Anderson89013801.6
Computer Architecture - Hennessy/Patterson6306401.0
Hardware Security - Tehranipoor/Bhunia4507481.7

 

El último es especialmente interesante pues Tehranipoor es el editor del libro en cuestión.

Las cuentas no están del todo bien hechas pues no estoy considerando al cantidad de palabras por página, OOSC en particular tiene la letra grande. No estoy seguro que estos números sean indicadores de calidad o de su falta.

¿Por qué me molesta esto de la bibliografía? Se debe a que estoy más acostumbrado a leer textos donde la mayor parte es explicación, no "tal xxx hizo tal prueba" sin dar ningún otro detalle.

Dejando de lado este aspecto superficial, pasemos a lo importante.


El contexto general de la seguridad de hardware, que es el hilo conductor de algunos artículos de modo explícito y afecta de modo implícto a todos es la protección durante la cadena de suministro del hardware (esto sale del capítulo 1):

  • IP Owner y la fábrica
  • Ensamblado del PCB
  • Integración del sistema
  • Usuario final
  • Disposición final o reciclado

Existiendo estos ataques más comunes:

  • Sobreproducción, para venta no declarada
  • Ingeniería reversa, para falsificación, copia o ahorrarse parte del diseño
  • Reutilización en lugar de disposición final
  • Falsificación, es hacer algo copiado y ponerle una marca ajena
  • Reclasificación, tipo remarcar un componente común como industrial


Mi opinión


Dejando de lado lo superficial y la calidad despareja, es un libro muy rellenador de detalles de los temas que ya conocía de antes y más aún disparador de los que ni estaba al tanto de la existencia, como:


  • Neuromorphic y sus memristores
  • Microfluidic devices
  • El nicho de los circuitos asincrónicos.


Si prestaste atención al título, "General", da a entender que puede haber otra nota. No te prometo, me llevó cuatro meses leerlo, como estoy un poco traumatizado y con más ganas de pasar al siguiente libro que de revisar este para dejar registradas mis notas.



2023/10/08

Un poco de history

En esta ocasión, algunas ideas y tips relativas al comando history.

Provee la funcionalidad de registrar los comandos ingresados para consulta o reejecución.

Repecto a la concurrencia, tiene la particularidad de que a medida que se van cerrando las sesiones se van guardando, cada una con su estado y sobreescribiendo las otras. En otras palabras, gana la última que escriba y eso vale para las abiertas que no tenés a la vista como accesos vía ssh o terminales en otras workspaces.

El registro está en ~/.bash_history, se puede editar.

Con el comando 

history -d N

eliminar la línea N.

Si no querés que se salve

unset HISTFILE && exit

y este que es más lindo, pero no cierra la ventana si estás en un entorno gráfico

kill -9 $$ 

y la verdad que no sé que hace en un entorno no gráfico.

Un truquito que se me ocurrió hace muchos años, incluso me lo pagaron en la revista Linux Journal, lo llamé "disposable alias", me había matado con el texto y lo redujeron a casi nada:

 

tech tip
tech tip

 

Consiste en agregar un comentario al final de los comandos que querés ejecutar varias veces pero no vale la pena hacer un script o un alias y querés recuperar con control-R:

comando ; # tag

Por ejemplo:

find . -iname "*borrar* -exec ls -l {} \;" ; # listar_a_borrar

Si pensás que con control-R borrar podés lograr el mismo efecto, considerá que cerca va a estar lleno de cosas como:

view borrar.txt

rm borrar2.txt

mkdir TRASH

mv borrar3.txt TRASH

y así...

 

La joya


Todo esto que escribí es solo para entrar en tema, ¿qué pasa si ejecutaste algo como esto?

export HTTP_PROXY=http://user:password@server:port
 
Te ha quedado en history la clave del proxy, para que la vea cualquiera que pueda acceder a tu history.
 
Tenés que hacer history | less para encontrar el número y luego history -d N.
 
Según ví en man history, hay toda una serie interminable de opciones para hacer... ni entendí bien qué, tomar un comando, reemplazarle una parte y ejecutarlo, me parece un poco bloatware.

 
Hay una manera mucho más sencilla 
 

Escondida en la documentación, hay una joyita que Mar Fer comentó en una conversación cyberciruja. Si tocás una cierta variable de la configuración, podés evitar que queden registrados los comandos de las líneas que inicies con espacios.

 

 
HISTCONTROL

A colon-separated list of values controlling how commands are saved on the history list. If the list of values includes ‘ignorespace’, lines which begin with a space character are not saved in the history list.

 

Cuando Mar Fer contó, me fijé en man history y nada dice, casi que parecía un bug, pero no, it is a feature.


Perspectiva de seguridad

 

Debido a la destrucción producto de la concurrencia, la facilidad de eliminar, editar o incluso suprimir todo, history sirve de modo muy incompleto para análisis forense. Si realmente necesitás trazabilidad, andá por el lado de auditd.

Respecto a comentar que existe lo de HISTCONTROL, un atacante que lo conozca podría en el medio de una serie de comandos normales que no llamarían la atención, ejecutar sin que quede registrado alguno malicioso en particular.

Mantener oculto lo de HISTCONTROL sólo contribuiría a una falsa sensación de seguridad mientras que el hacerlo conocido ayuda a que hayan menos datos sensibles en history, gana comentarlo.


2023/09/29

Cómo pegar texto sin portapapeles

Supongamos estos escenarios:


Tenés una VM recién instalada sin entorno gráfico y hay que ejecutar algunas instrucciones muy largas. Como no tenés portapapeles, a tipear.

Estás haciendo login en una terminal o aplicación que no soporta copiar y pegar y terminás bloqueándote porque tenés una clave tipo "1lI0OG68B".

Estás llenando un campo en una página web que por algún perverso motivo tiene

 <input  type="text" onpaste="return false" /> 

Seguro te ha ocurrido cuando te piden un mail y en la verificación no te deja pegar.

La generalización del problema es:

 

Quiero pegar texto y el portapapeles no funciona.


Entonces pensás. Si hiciste ataque del "falso pendrive" en el cual pusiste un microcontrolador que se registra como un teclado y envía texto, ¿se podría hacer algo parecido con software? ¿Un programa que envíe el texto a la ventana como si fuera el teclado?

Pues existe y si usás X11, no Wayland, se llaman xdotool.

Para saber si usás X11, ejecutás:

$ echo "$XDG_SESSION_TYPE"

o

$ env | grep -E -i 'x11|xorg|wayland'

Para instalar xdotool en Debian y derivados

$ sudo apt install xdotools

Luego, tenés que ejecutar esta secuencia:

  • Encontrar la ventana
  • Seleccionar la ventana
  • Enviar el texto

Por ejemplo:

xdotool search "Virtual" windowactivate --sync type "ls"

¿Fácil? Nada lo es. Si mirás 

$ man xdotool

verás que es una herramienta superpower, pero quedémonos con lo más básico y veamos los detalles

 

Encontrar la ventana


El search viene a ser un /.*PATTERN.*/i

Otra, hay ventanas embebidas, por ejemplo para firefox, te trae todas las solapas:

$ xdotool search "Firefox" | wc -l
Defaulting to search window name, class, and classname
94

 

Seleccionar la ventana


Es hacerla activa y el --sync es para que espere a que ocurra

 

Enviar el texto


Esto no debería tener sorpresas hasta que lo probás y el teclado de un host no corresponde con el de destino.

Se debe a que en realidad no se están enviando letras sino keycodes, que es exactamente lo mismo que pasa con el microcontrolador.

O cambiás la disposición del teclado en el destino o tenés que hacer alguna conversión del texto que vas a enviar. Un tr nunca viene mal, hasta que intentás hacerlo y te recomiendo cambiar la disposición.


Si no podés hacerlo y no hay mucho que enviar, te recomiendo que lo mandes como está y luego edites y corrijas, perdí un montón de tiempo con tr

 

Retrospectivamente, lo mejor sería escribir un programita que haga el proceso inverso, obtener el keycode de las teclas que habría que mapear y que genere el texto apropiado pero ya es todo un proyectito.


Ponele que no podés cambiar la disposición del teclado, aún queda el Plan Base64.

Primero la falla y luego con base64
Primero la falla y luego con base64

Después de haber fallado:

  1. Pasás el texto a base64
  2. Preparás el comando y ponés el cursos entre las comillas ""
  3. Enviás el texto
  4. Ejecutás falla porque el "=" no se transmite...
  5. Lo arreglás a mano y volvés a ejecutar
  6. Mostrás el contenido correcto

 

Ideas adicionales


En una terminal no X de una VM, instalando gpm se puede copiar y pegar localmente, pero aún con VboxAdditions y el portapapeles activado no pude pegar entre entornos. Igual no me maté mucho probando.

Lo de los campos a los que no se les puede pegar, que el ejemplo más básico sería una página que abrís directamente del sistema de archivos con este contenido:

<form>
  <input type="text" /><br/>
  <input type="text" /><br/>
  <input type="text" onpaste="return false" />
</form>

lo podés burlar muy fácilmente quitando lo marcado en amarillo desde el navegador con Developer Tools, ahí fué la seguridad de Client Side Validation...

Estos métodos pueden parecer rehacking pero si mirás atentamente sólo estás introduciendo texto, no hay fuga de información, cosa que sí habría en caso de funcionar el portapapeles.

Una idea para facilitar la transcripción de credenciales cuando todo lo demás falla, es  que tengan la forma ???_???_???_???, no disminuye la fortaleza y facilita la copia humana.

Finalmente, si usás estos métodos con claves, no te olvides de borrar en el historial de comandos...

Bonus: gzalo me avisó que la mayoría de los password managers tiene esta función, en el caso particular de KeePassXC es botón derecho sobre la cuenta, "Perform Auto-Type", que le envía la clave a la última ventana a la que le hayas hecho foco. Probalo con una cuenta inútil y un editor de texto hasta que lo entiendas bien.

Bonus: lo de usar un microcontrolador para almacenar claves es la idea que todos tuvimos pero solo mar fer realizó.


Dejo pegados acá los comandos:

 

xdotool search "ventana" windowactivate --sync type "txt"

 

xdotool search "ventana" windowactivate --sync type $( echo "txt" | base64 ) 

cmd=$( echo "" | base64 -d )

 

xdotool search "ventana" windowactivate --sync type $( base64 script.sh) 

echo "" | base64 -d > script.sh




2023/08/03

Emulador chip-8

 

En el marco cyberciruja, gzalo dictó un excelente taller de emulación de CHIP-8 el sábado 2023-07-22. Comparto mi experiencia y resultado.


Me apoyé en el ejemplo provisto, tanto el base como los fragmentos de código de la presentación, le refactoricé las globales a un struct, quité el boolean y renombré .cpp a .c, es que había una trampita de estar usando g++ para compilar código C.

 

Screen dump al finalizar la ejecución
Screen dump al finalizar la ejecución

 

Mi objetivo, además de la diversión, es repasar un poco C para no olvidar demasiado, probar algunos recursos que no he usado mucho y otros que sí pero no en C. Entonces ejercité:

  • Makefile 
  • procesar argumentos con getopt
  • FSM
  • testing unitario sin framework
  • valgrind
  • gdb

Mi enfoque fue incrementar de modo testeable la menor funcionalidad posible en cada paso. En un comienzo, testeable era que ejecutara el programa hasta la próxima instrucción no implementada, luego agregué test unitario.

Antes de seguir, para no spoilearte, te dejo mi recomendación de cómo encararlo:

  • Elegí e inicializá las estructuras u objetos de tu arquitectura.
  • Hacé el loop de ejecución.
  • Implementá los dumps de los registros y estructuras
  • Cargá la ROM
  • Armá el switch para los opcodes y los switches internos para los subcodes, poné en cada rama una llamada a una función unimplemented(), unknown() en el default del switch principal, invalid() en cada default de los switches internos:


     void unimplemented(){
       printf("UNIMPLEMENTED INSTRUCTION\n");
       exit(1);
     }

  • Ejecutá el programa, tiene que tirar un unimplemented, implementá esa instrucción. Y así....

Ya sea a mano o con un framework de testing, respetá TDD.

  • Ejecutá hasta el unimplemented: eso te genera el requerimiento
  • Escribí el test ateniendote a la especificación, ojo que hay sutiles diferencias
    • https://chip8.gulrak.net/ (marcá solo CHIP-8) 
    • https://github.com/Timendus/chip8-test-suite
    • https://en.wikipedia.org/wiki/CHIP-8
    • https://tobiasvl.github.io/blog/write-a-chip-8-emulator/
  • Dejá que falle para comprobar que es efectivo en la falla
  • Hardcodeá para comprobar que es efectivo en el éxito
  • Implementá hasta que pase
  • Refactorizá
  • Again...

 

Spoiler Alert 


Ahora te cuento lo que hice yo, que más o menos se parece, está en cpantel/CHIP-8

Implementé la carga del programa en memoria y comprobé visualmente que soc_dump_memory() y hexdump -C ROM/1-chip8-logo.ch8 correspondieran. Recordá que en el dump debe estar la fuente, luego todos ceros y la imagen del ROM recién en 0x200, 512 decimal)

Comparación de memory dump y hexdump de 3-corax+.ch8
Comparación de memory dump y hexdump de 3-corax+.ch8

Implementé  avanzar un paso, que haga el fetch y la descomposición en opcode, nibbles, X, Y, keys, NN, NNN, el nombre que sea útil en cada contexto

Tomé el ROM 1-chip8-logo.ch8 que es el que menos instrucciones precisa. De cada instrucción que invoca, implementé el decode y execute. Obviamente para SPRITE me copié de lo que Gustavo proveyó.

Para la primera instrucción que es CLS, mi clear_screen toma un valor, así en init() pongo la pantalla en blanco y ante la primera llamada de CLS se pone en negro.

Implemente que varias medidas intrusivas se activen con left shift, tales como:

  • avance paso a paso
  • volcado de memoria, video, registros, teclas
  • des/activación de debug

 Esto es por que el teclado esta mapeado en:

  1 2 3 4

   q w r t

    a s d f

     z x c v

Y algunas de esas letras son buenas como atajos:

while (!quit) {
  while (SDL_PollEvent(&event)) {
    if (event.type == SDL_QUIT) {
      quit = 1;
    } else if(event.type == SDL_KEYDOWN){
      if (event.key.keysym.mod & KMOD_LSHIFT ) {
 
      // para que se vea bien acá
        // sym = event.key.keysym.sym;
        if(sym == SDLK_r) run = ! run;
        if(sym == SDLK_n) soc_step(&soc,1);
        if(sym == SDLK_d) {debug = ! debug;         run=0;}
        if(sym == SDLK_t) {show_time = ! show_time; run=0;}
        if(sym == SDLK_v) {soc_dump_registers(&soc);run=0;}
        if(sym == SDLK_m) {soc_dump_memory(&soc);   run=0;}
        if(sym == SDLK_s) {soc_dump_screen(&soc);   run=0;}
        if(sym == SDLK_k) {soc_dump_key(&soc);      run=0;}
        if(sym == SDLK_h) {show_help();             run=0;}
        if(sym == SDLK_q) quit = 1;
      } else {
        if(sym == SDLK_1) soc_press_key(&soc, 1);
        ....

      }
    } else if(event.type == SDL_KEYUP){
      if (event.key.keysym.mod == KMOD_NONE ) {
        if(sym == SDLK_1) soc_release_key(&soc, 1);
        ...

    if (run) soc_step(&soc, debug);
  }
  ...


Ahí entrás en un loop de ir implementando la siguiente instrucción que falle....


pc: 200 fetch: 00E0 opcode: 0 X: 0 Y: E opcode2: 0 NN: E0 NNN: 0E0
pc: 202 fetch: A22A 
opcode: A X: 2 Y: 2 opcode2: A NN: 2A NNN: 22A
pc: 204 fetch: 600C 
opcode: 6 X: 0 Y: 0 opcode2: C NN: 0C NNN: 00C
pc: 206 fetch: 6108 
opcode: 6 X: 1 Y: 0 opcode2: 8 NN: 08 NNN: 108
pc: 208 fetch: D01F 
opcode: D X: 0 Y: 1 opcode2: F NN: 1F NNN: 01F
UNIMPLEMENTED INSTRUCTION


Implementé una opciones de línea de comando para determinar:

  • Estado inicial variable
    • Corriendo o pausado
    • Imprimiendo debug o no
    • Hacer o no dump de pantalla al finalizar
  • Cantidad de instrucciones a ejecutar hasta detenerse
  • Cantidad de instrucciones por frame, 8 parece ok
  • Delay para completar el frame, medio en vano, todo ocurre en mucho menos de un milisegundo, el default de 16 parece ok
  • ROM a cargar

Fui tomando cada ROM de https://github.com/Timendus/chip8-test-suite y no hubo problemas hasta la quinta, quirks. De paso ahí noté que muchas de las operaciones tenían una equis en lugar del check ok en los anteriores, entonces tuve que...

 

Test

 

Llegó el momento de agregar test unitario. Hasta este momento tenía algo parecido a:

void step(struct typeSOC* soc) {
  struct typeInstruction ins;
  fetch(soc,&ins);
  soc->pc+=2;
  switch (ins.opcode) {
    case 0x3:
      if (soc->v[ins->X] == ins->NN) soc->pc+=2;
    break;
    ...
  ...

Lo cual es imposible de probar, la implementación de la instrucción debe estar en una función aislada para no estar probando a la vez el fetch y fundamentalmente ese soc->pc+=2;

Me llevé la implementación de cada operación a otro archivo

  switch (ins.opcode) {
    case 0x0: // CLS
      switch(ins.NNN) {
        case 0x0E0:
          api(soc, &ins, OPCODE_CLS);
          if (debug) printf("  CLS\n");
    break;

    case 0x0EE: // RET
          api(soc,&ins, OPCODE_RET);
    break;
 

 Este es el test de XOR

void test_XOR(struct typeSOC* soc, struct typeInstruction* ins) {
  soc->v[8] = 0xff;
  soc->v[2] = 0x2a;
  ins->fetch = 0x8823;
  predecode(ins);
  api(soc, ins, OPCODE_XOR);
  assert_equal(0xd5, soc->v[8], "XOR: bad result");
}

 

siendo assert_equal:

void assert_equal(uint32_t expected, uint32_t got, char * msg) {
  if (expected != got) {
    printf("\n%s, expected: %x got: %x\n", msg, expected, got);
  } else {
    printf(".");
  }
}

En lo versionado actual quedaron unos if's en lugar de assert_equal, algún dia...
 

Pude haber implementado una función para cada instrucción, tipo api_CLS(), api_RET() o como lo hice, que es pasarle a una sola función el código y ahí dentro otro switch(), lo cual puede ser mal visto por que parece haber repetición de código, pero, no estoy tan seguro. El primer switch tiene switches anidados y el segundo no, es plano. 

Usar funciones distintas es mejor manera de autodocumentar y un mejor lugar para poner los unimplemented(). Los invalid() y unknown() quedarían en el switch de soc.c.

Tal como hice los test, sin framework, tiene una muy evidente desventaja, que es no poder imprimir un puntito tras cada test realizado de modo elegante. Ni poder llevar la cuenta de los tests y asserts ejecutados. Digo de manera elegante, siempre se puede llenar todo de printfs y globales...

Otra limitación, no es que no se pueda hacer con C, es que de haber usado un lenguaje orientado a objetos, quedaría muy prolijo encapsular todo con setters y getters en lugar de acceder directamente a los registros y memorias y así poder recopilar algunas estadísticas como:

  • uso de registros
  • uso de instrucciones
  • uso del stack
  • uso de botones
  • área de memoria leída y escrita

Esto serviría para hacer una versión reducida, como para portar a FPGA chica o lo que me interesaría, el core RISC-V icicle en la EDU-FPGA.

 

Traza

 

Un recurso es analizar una traza prexistente, se puede obtener de los emuladores octo. Por ejemplo en (https://timendus.github.io/chip8-test-suite/3-corax+.html), poniendo algo como

console.log("op: " + op.toString(16) + " o: "
+ o.toString(16) + " pc: " + this.pc.toString(16));


cerca de la línea 470 cuando descompone el fetch, genera una traza. Si en tu emulador hacés algo compatible, con ./tools/compare comparás.

Esto sirve para las partes donde no hay uso de timer ni lectura de teclado.

 

Headless


Implementé una versión headless, más que todo porque en mi máquina no quería instalar SDL2, entonces hice todo en otra con ssh y una ventana de VNC, solo en esa ventana se puede ejecutar código con SDL. Medio en vano pues con ssh -X funciona casi ok.

 

Dump ascii

 

Motivado por la posibilidad de testear la imagen rederizada, implementé que se haga un dump por terminal. Para ser franco no es ascii, es unicode, pero bueno. Es la primera imagen de este post, se puede ejecutar en cualquier momento o al finalizar.

 

Autostop

 

Aunque ningún programa baremetal como son estos termina, quizás queremos que terminen al terminar de ejecutar. Esto tiene sentido en los tests y la manera de implementarlo es hacer un salto incondicional a esta misma instrucción, o sea al PC actual. Esto se detecta y finaliza la emulación.

  if (ins.opcode == 1 && soc->pc == ins.NNN) {
     return 1;
  }


Mi implementación en C

 

Está en cpantel/CHIP-8 y te cuento que no me maté mucho en que los componentes respetaran la jerarquía, fijate que debió haber sido así:
 

struct typeSOC
  pc            -> soc.cpu.pc
  i             -> soc.cpu.i
  v[]           -> soc.cpu.v[]
  stack_pointer -> soc.cpu.stack_pointer
  key[]         -> soc.keyboard.keys[]
  kb_state      -> soc.keyboard.state
  last_key      -> soc.keyborad.last 
  stack[]       -> soc.stack[]
  memory[]      -> soc.memory[]
  screen[]      -> soc.screen.memory[]
  redraw        -> soc.screen.redraw 
  delay_timer   -> soc.timer.delay
  sound_timer   -> soc.timer.sound

 
y la parte de la instruction, quizás así:
 
  count         -> soc.cpu.instruction.count
  fetch         -> soc.cpu.instruction.fetch
  opcode        -> soc.cpu.instruction.opcode
  X/key         -> soc.cpu.instruction.X/key
  Y/N/opcode2   -> soc.cpu.instruction.Y/N/opcode2
  NN            -> soc.cpu.instruction.NN
  address/NNN   -> soc.cpu.instruction.address/NNN
 
 
pero implica agregar archivos y un montón de refactorización, no vale la pena.

Valgrind lo usé para ./tools/compare, no para chip8, así que no sé si

 

Posibilidades de mejora

 

RND: proveerle una seed por línea de comando.

Comparar la memoria de pantalla con una imagen de referencia.

Poder cambiar mediante parámetros de línea los colores de background y foreground.

Que en debug imprima el código disassembly. 

Que pase el test quirks. 

Implementar las validaciones de uso del stack y algunas otras.

Que imprima algún help para las command line options.

Que valide que exista el archivo de ROM y su longitud.

Usar funciones para cada instrucción en lugar del segundo switch.

 

Conclusión

 

Muy buena la explicación de gzalo y su asistencia posterior, me destrabó al menos en un ínfimo pero mortífero error muy difícil de notar en la lectura del teclado.

La experiencia fue muy buena, sin presión, hacer algo que no sirve para nada por fuera de haberlo hecho. Es que los juegos son horribles, inusables, no probé ninguno más allá de cargarlo para ver que funcionara algo.

La mayor parte de las posibilidades de mejora serían aplicables en caso de querer desarrollar programas para esta plataforma, nada más lejos de mis intenciones, se lo dejo a la gente más retro.







2023/07/09

MD5 como IOC apesta

Qué es un IOC

Cuando hay ataque, en sus distintas etapas, se van generando acciones y artefactos, que deberían quedar registradas en algún lado y que son detectables.

Por ejemplo, te envían un link malicioso, para que te descargues un archivo, que al ejecutarse accede a una url para bajar componentes adicionales, exfiltrar información o recibir órdenes. Además, se escribe en el disco como persistencia y agrega una clave en el registro para reiniciarse cuando el sistema lo haga, ¿qué tenemos?

  • Una url maliciosa
  • Un archivo malicioso
  • Más archivos maliciosos
  • Más urls
  • Claves en el registro

Si vos ves este ataque, podés tomar nota de identificadores de cada una de estas piezas y luego, ante cada intento de acceder a urls comparar con las maliciosas, al bajar, escribir o leer un archivo hacerlo con los maliciosos y revisar el registro buscando claves.... maliciosas.


El caso particular del archivo no es muy práctico tomarlo como referencia pues puede ser grande, lo mejor es calcularle un hash y guardar ese Indicator Of Compromise.

 

Pequeña pieza de recomendación

 

Lamentablemente, incluso entre la gente técnica, la falta de conocimiento detallado hace que se cometan errores de concepto, por ejemplo el que paso a reportar, pues lo he visto varias veces con varias personas.

Ponele que tenés dos herramientas que entienden IOCs, el DNS y el proxy saliente.

Te llega este IOC, ¿dónde lo ponés?

https://3984274.aws.com

Muy bien, en cualquiera o ambos.

¿Y este?

https://3984274.aws.com/api/service/34

Si tuvieras la tentación de decir lo mismo, antes mirá este:

https://www.afip.gob.ar/api/service/34

Hay personas que han dicho:

"Pongamos en ambos"

Hay dos problemas, el de seguridad y el conceptual técnico.

El de seguridad es que si para bloquear  

https://www.afip.gob.ar/api/service/34

ponés  "www.afip.gob.ar" en el DNS, generás una denegación de servicio a todo afip sólo por una ruta comprometida. Podría ser válido, razonando "si tiene esa ruta comprometida, puede estar todo el resto, no interactuemos en absoluto con afip hasta que corrijan", ok, es una decisión.

En la conversación de esa decisión es cuando detecté el problema conceptual técnico:

El DNS no entiende ni "https://" ni "/api/service/34"

La única manera de bloquear sin "daños colaterales" es en el proxy.

Sigamos....

 

Qué es MD5


Hashing es una operación que se hace sobre un dato y devuelve un hash, que es un número largo y en teoría cambia ante cualquier modificación sobre el dato. Si calculás un hash de un dato, modificás un bit, recalculás, te da otro hash.

Por alguna falta de visión, pues esto de los IOCs es relativamente reciente, se utilizan como IOCs de archivos hashes MD5, que se diseñó en 1991 y ya desde 1993 se le han hallado problemas criptográficos que en el mejor de los casos, parecería que ya desde 2010 se considera deprecado.

Digo falta de visión pues si buscamos en wikipedia IOC, el artículo nace en 2013, como que ya existían varios SHA para ese entonces, pero seguro que lo de los IOC con MD5 entraron de la mano de gente de seguridad no técnica y programadores sin conocimientos de seguridad que buscaban ahorrar espacio y tiempo de ejecución.

Ojo, siempre es fácil señalar los errores del pasado desde el futuro, pero bueno.

 

La colisión "accidental"

 

Una colisión de hash es cuando dos datos distintos dan el mismo hash. Las colisiones son esperables pues la variedad de datos es infinita y los hashes son números, en el caso de MD5 entre 0 y 2 a la 128, que aunque es un número grande, claramente es muy muy muy inferior a infinito.

 

Se podría argumentar que dada la baja probabilidad de colisión y la ventaja del ahorro de espacio y tiempo de ejecución no importa, aunque ya tenemos al menos un caso conocido.

 

Dice este reporte que no sé de dónde saqué, pero se parece a la situación habitual de malware, en este caso Trojan.Win64.CoinMiner, un minero:

https://itsafety.net/report/20200515-65ade21dc82c01972891285581d85866-servermanager-exe_process-partofthreat

Trojan.Win64.CoinMiner
Trojan.Win64.CoinMiner


Fijate que dice que la mayor parte de los antivirus no lo detecta, ¿por qué será?

Dice Microsoft, que respecto a windows debe tener una cierta autoridad

https://learn.microsoft.com/en-us/windows-server/administration/server-manager/server-manager

Microsoft
Microsoft


Puede ser que estemos hablando de distintas piezas, pero si buscás en virustotal ese hash (65ade21dc82c01972891285581d85866), te trae

 

VirusTotal
VirusTotal

De paso observá que el hash que te muestra es un SHA256, correcto.

No tengo las ganas de buscar cada versión legítima del archivo hasta encontrar el que coincide con esa firma, para demostrar fehacientemente el punto pues su demostración pasa por otro lado.

 

La colisión a propósito


Desde hace años en mis charlas muestro este ejemplo:


angel devil
angel devil

Vemos que dos programas generan distinto output, miden lo mismo, tienen la misma longitud pero son distintos, con sha256 eso no pasa.

 

sha256
sha256


La verdad verdad, no sé bien de dónde lo saqué, tampoco importa muchísimo pues se trata de criptoarqueología y no hay nadie que discuta que MD5 no sirve.

Es un programa en C con un buffer. En ese buffer hay un valor, se compila y al ejecutar se comprueba que sea ese valor. En este caso se ejecuta angel().


Luego, con fastcoll y longEgg se manipula ese buffer en la copia de angel llamada devil hasta que da el mismo hash MD5. Al ejecutar y fallar la comprobación, se ejecuta devil().

 

int main() {
  if (strcmp(dummya, dummyb) != 0) {
    return angel();
  } else {
    return devil();
  }
}



Lo que importa, es que probablemente se puedan tomar dos programas de la misma longitud, manipular una parte y hacerles concidir los hashes, con lo cual para cada ejecutable legítimo del sistema

Digo probablemente pues como script kiddie todo esto parece fácil, pero no lo he comprobado.

Me pregunto por qué no se hace y apuesto a que no vale la pena, es demasiado fácil contrarestar, leyendo el archivo de atrás para adelante para el MD5 o tomando otro SHA cualquiera basta para detectar correctamente, salvo en las herramientas más precarias, para decirles de alguna manera....

lectura invertida
lectura invertida


Para qué puede servir MD5

 

MD5 es útil en situaciones sin adversarios. Por ejemplo, tenés dos archivos del mismo tamaño y no sabés si son iguales. Si están en la misma máquina corrés cmp y listo, te lo dice. Pero si están en distintas máquinas, tendrías que copiar uno a la otra o montar en una una carpeta de otra.... mucho trabajo. El MD5 de cada archivo te puede confirmar si son distintos, no tanto que sean iguales, pero ya dije, sin adversarios.

Me ha servido para hacer unas pruebas con FPGA, tal cual relato en un montón de entradas....

Y por supuesto sirve para demostrar lo inseguro que es.