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.

Aprovechando pantallas obsoletas: la implementación

 

Esto viene de una idea.

Obviamente la experiencia que transité no corresponde a lo que he dejado registrado, al ser una investigación ha habido una ida y vuelta y varias de las pruebas infructuosas no quedaron registradas

 

Implementación

Lo que terminé haciendo es VNC al Remoto, para el momento de iniciar la aplicación y hacer ssh -X al principal. Luego se puede minimizar y volver a maximizar cuando hace falta interactuar. En el caso de xwindows pelado como es con una tablet, la interacción es desde la tablet.


Los pasos del setup


Principal

sudo apt install remmina-plugin-vnc

 

Remoto linux

Como es i32, tengo un Linux Mint 19 con XFCE.

 

Autologin

sudo vi /etc/lightdm/lightdm.conf

    [Seat:*]
    autologin-session=xfce
    autologin-user=YourDesiredAutoLoginUserName
    autologin-user-timeout=0


VNC server

sudo apt install x11vnc
x11vnc -storepasswd somefile


Cuando te conectás te aparece un botón efímero en Secundario. Para evitarlo, le ponemos autenticación más que por seguridad.

Autostart de VNC

  Settings ->
    Session and startup ->
      Application autostart ->
         add x11vnc
            x11vnc -rfbauth somefile


Ver IP

sudo apt install xfce4-genmon-plugin

 
panel -> add new items -> generic monitor
armar script con este contenido y ponerlo en "command"


ip a | grep inet.*global | cut -d" " -f 6

 


Remoto android


samsung tab 3 lite sm-t110 android 4.2.2

Hay que instalar XServer-XSDL en su versión v1.20.41_apkpure de
https://sourceforge.net/projects/libsdl-android/files/apk/XServer-XSDL/

Cuando falla con "Error: X server failed to launch", hay que reiniciar y configurar seleccionar

resolución nativa
x1
device configuration ->
   mouse emulation ->
      mouse emulation mode ->
         desktop, no emulation

Cuando aparece la cuenta regresiva, tocar hasta tener display number = 3

Sacado de https://www.youtube.com/watch?v=TcgTrkA8Oj0

Me falta autostart, por ahora puedo prescindir. Estaría bueno un vnc server pero no hay, quizás lo mejor sería ver de meter un linux, no vale la pena.



Los pasos de la conexión

 

A linux

Via VNC, conectar a Secundario, la IP debe estar en el Panel

Abrir una terminal

ssh -X a Principal

Ejecutar el comando deseado, por ejemplo, "top"

 

A android

En la pantalla de la tablet debe haber instrucciones tipo:

export DISPLAY=192.168.1.105:3

export PULSE_SERVER=tcp:192.168.1.105:4713


Se invoca así:

xwindows-manager & mate-terminal -e top


o así:

mate-terminal --display=$DISPLAY -e top

 

Detalles de uso


xed 

en linux Secundario hace el render en el principal, debe ser por su integración con Mate, así que hay que usar otro, yo puse /opt/sublime_text/sublime_text


screensaving y powersaving

En todos los Secundarios hay que apagar los salvapantallas y poner al máximo o infinito los tiempos de activación de bajo cosumo.

Firefox

En linux:

firefox -P remoto https://www.google.com

En android, muy difícil de interactuar:

firefox -P remoto --display=$DISPLAY  https://www.google.com

 

En tablet 

 

teclado

Para hacer aparecer el teclado en la tablet que tengo, hay que apretar el botón del medio para elegir la xserver y ahí aparece con el teclado que luego se quita con el botón de volver.


move y resize

se puede mover, pero no conseguí cambiar el tamaño, voy a ver uno de estos días si al menos se puede setear al invocar el comando.


Evidencias


En esta imagen podemos ver en la conexión de VNC a una netbook, una terminal con ssh -X de retorno al sistema principal y una tablet con x-windows aguardando conexiones.


Conexiones abiertas
Conexiones abiertas

En esta, hemos abierto un xcalc en el principal con su render en la netbook y el top de la principal con su render en la tablet.


Programas en ejecución
Programas en ejecución