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 |
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 potenciómetro está para calibrar los tiempos de descarga. Con respecto al PMOD explico un poquito en otra entrada.