2020/12/27

Hardware Malware: bit supervisor

 

 

La idea de esta demo está basada en un paper que no recuerdo si he leido en su completitud o no. Si no lo he leido ha sido a propósito, pues una vez comprendida la idea central, no quería spoilearme cómo hacerlo.

Repasemos: hay que instalar subrepticiamente en el proceso de diseño y  construcción de un chip antes de que llegue al silicio, un mínima lógica activada por las órdenes legítimas o por un capacitor que se carga cuando hay mucho tráfico en una pista donde habitualmente hay poco.

 

Activación
Activación


O sea, normalmente el capacitor se está descargando, pero si hay una sucesión suficientemente sostenida en el tiempo tal que la carga supere  a la descarga y llegue al punto tal que su valor represente un 1 lógico, la mínima lógica prenderá el bit supervisor.

¿Qué es el bit supervisor? El que permite proteger a nivel de hardware recursos como la memoria, IO y ciertas instrucciones y separar los procesos del usuario común del sistema operativo

Esto va desde el simple deseo de que las cosas funcionen bien, como que un programa no pise la memoria de otro hasta la seguridad del sistema, donde un programa no puede ver la memoria de otro.

 

Tenía varios caminos, del cual implementar el ataque como en el paper con un ASIC quedaba completamente fuera de mi alcance.

La siguiente posibilidad era obtener el VHDL o Verilog de una CPU, por ejemplo 6809 o 68000, teniendo que cumplir con las siguiente condiciones:

  • implemente bit supervisor: obvio.
  • la pueda hacer funcionar con sencillez: para mi nada es sencillo.
  • entienda yo cómo se interactúa con el bit supervisor
  • pudiera yo encontrar el punto donde colocar el capacitor

Comprenderás que el capacitor debe ser externo, con VHDL o Verilog lo único que puedo hacer es agregar un contador, lo cual pierde toda la gracia.

A esto se le suma que tengo que aprender una parte del lenguaje de máquina de esa CPU y como hacerla arrancar, probablemente canibalizando un programa existente... demasiado. Esas son todas super capacidades que no tengo o no puedo adquirir en un tiempo razonable y, ¿quién quiere dedicar meses de su vida para aprender a usar una arquitectura obsoleta hace décadas?

No voy a mentir, yo querría, pero prefiero desperdiciar mis recursos en otras cosas igualmente improductivas pero más interesantes.


Hasta acá es lo que expuse en la charla de H4CK3D 2020, pero por falta de tiempo no entré en algunos detalles jugosos del diseño de la demo.

El camino más fácil y bastante productivo en términos educativos es hacerme mi propia CPU, tal que implemente lo mínimo necesario para la POC. Tuve que pensar el programa más corto posible que sirva y sólo implementar esas instrucciones.

Necesito:

  • Una operación que Output que sólo funcione cuando está el bit supervisor prendido (out).
  • Una operación tipo SVC (Super Visor Call), para pasar al modo supervisor (svc).
  • Una operación de retorno de SVC, para retomar el programa y salir del modo supervisor (ret)
  • Una operación para cargar un valor en el único registro (set)
  • Una operación de salto condicional para poder hacer un loop. (jnz)
  • Una operación de incremento para poder hacer un loop (inc)

Para logra la máxima sencillez, en los MSBits puse el opcode y en los LSBbits el operando de haberlo. Todas instrucciones de un byte. El Program Counter arranca en 16 debido a una evolución que no vale la pena que muestre.

Tanto el inc (1000xxxx) como el nop (1001xxxx), tienen el MSB observado por el capacitor.

La SVC tiene hardcodeada una dirección a la cual saltar, donde reside el código con priviliegios, no se si ha existido alguna arquitectura tan primitiva como la mía en ese aspecto, pero no olvidemos que esto tiene otro objetivo, no es el estudio de arquitecturas de microprocesadores.

El programa de ejemplo, en verde el funcionamiento legítimo, en rojo se bloquea la operación por falta de privilegios, en amarillo el ataque y en aqua la explotación.

Código usuario

  16: h90;  // nopC 17: h90;  // nop  
  18: h90;  // nop
  19: h90;  // nop
  20: h90;  // nop
  21: h90;  // nop
  22: h25;  // set   0101 en registro
  23: h40;  // svc   0101 en leds
  24: h2A;  // set   1010 en registro
  25: h40;  // svc   1010 en leds
  26: h2F;  // set   1111 en registro
  27: h40;  // svc   1111 en leds
  28: h25;  // set   0101 en registro
  29: h30;  // out   falla
  30: h2A;  // set   1010 en registro
  31: h30;  // out   falla
  32: h2F;  // set   1111 en registro
  33: h30;  // out   falla
  34: h20;  // set   Este es setup del loop
  35: h80;  // inc   incremento del registro
                    
inicio del loop
  36: h90;  // nop   nada
  37: h1F;  // jnz   salta al inicio del loop
  38: h25;  // set   0101 en registro
  39: h30;  // out   0101 en leds
  40: h2A;  // set   1010 en registro
  41: h30;  // out   1010 en leds
  42: h2F;  // set   1111 en registro
  43: h30;  // out   1111 en leds
  44: h90;  // nop
  45: h70;  // hlt

código privilegiado


  46: data = 8'h30;  // out legítimo
  47: data = 8'h00;  // ret


Si tuvieras ganas de mirar el repo  en la carpeta javascript podrías apreciar la evolución desde una simulación más abstracta a una más concreta considerando la microarquitectura, completamente en vano pues a la hora de implementar en Verilog usé la primera.

Si tuvieras ganas de mirar el Verilog verías el comportamiento legítimo del bit supervisor en svc y ret como se prende y apaga:

OPCODE_SVC: begin
  supervisor = 1;
  error= 0;
end

OPCODE_RET: begin
  supervisor = 0;
end

Luego, en out, si el bit está prendido transfiere el registro al puerto de salida, si está apagado tirar error:

OPCODE_OUT: begin
  if (supervisor) begin
    gpio = A[3:0];
  end else begin
    error = 1;   
  end
end

 

El malware lo podemos ver  en la interfaz de board.v

  output malware_activation_signal_out
  input  malware_activation_signal_in

que son los cables que van y vienen del capactor.

Luego en la implementación:

   assign malware_activation_signal_out =
           cpu.opcode[3] ? 1 : 1'bz;

dice que si el MSB del opcode es 1 ponga un 1 y si no ponga en alta impedancia para que no se descargue tan rápido el capacitor. Sabiendo más electrónica seguramente se puede reemplazar la condición por un diodo.

Finalmente, en cpu.v

OPCODE_NOP: begin
  if ( board.malware_switch &&
      (  board.malware_input ||
         board.malware_activation_signal_in
      ) )
     supervisor = 1;
end

Lo que dice es que si está habilitado el malware y ya sea porque está activado por un switch o por el capacitor, prenda el bit supervisor.

Mi conocimiento de electrónica analógica es ínfimo, así que prefiero no explicar mucho, en parte por vergüenza, en parte para no brindar conocimiento erróneo.

 



El capacitor que usé es gigante por los tiempos de un clock tal que se pueda apreciar en una demo. En la realidad sería minúsculo.

 

El PMOD con los leds y el bichito
El PMOD con los leds y el bichito

 

El potenciómetro está para calibrar los tiempos de descarga. Con respecto al PMOD explico un poquito en otra entrada.



 





No hay comentarios:

Publicar un comentario