Insomni’hack 2013 – Life is hard(ware)

Intro

For this challenge, I wanted the attendees to reverse a microcontroller firmware, but most of all, I wanted them to actually see the result “live” to prove that the code actually works on a real device. The main idea was to use a keypad and a small screen to display the flag once the correct code has been entered.

I initially started writing the firmware on a Teensy++ 2.0 I had at hand. The firmware was almost complete when I received the new Teensy 3.0 at home. As I was playing with it, I found one cool feature :

Teensy 3.0 pinout

“Touch”, by the use of capacity sensors, really means that it can recognize a user’s finger. I found this so cool that I changed my plans and modified the firmware to use the touch inputs.

Touch input

One of the biggest advantages I saw with this feature is that I didn’t need push buttons nor direct access to the electronics. After some tests, I found that even with 5mm of material between my finger and the touch sensor, the returned value is large enough to correctly detect which sensor was activated.

As I had nine touch input pins available (I didn’t want to use the back of the Teensy), I used a keypad with nine inputs, from 1 to 9. Each digit having its own input on the Teensy. Since each sensor had its own base, value, I used threshold values to detect which number has been touched. The corresponding code is the following :


int inputs[] = {0, 1, 15,16,17,18,19, 22,23};
int thresholds[] = {800,800,800,800,800,1000,1000,900,1000};

int readStatus(){
while (1) {
for (int i=0;i<10;i++){ //Test each input
if (touchRead(inputs[i]) > thresholds[i] ){ //Does the input value go over the threshold ?
delay(300); // Delay
return i+1; //Return the keypad value
}
}
}
delay(10);
}

Case

I wanted the case to be as wide open as it can be, so people could actually see how it looks like on the inside and something that could resist 200 hackers that would play with it. I chose to use glass, as I had access to all the stuff I needed to create this case (Thanks, Mom 😉 )

The front face is made of 4mm float glass. I tried to engrave the digits on the glass, but the digits were not quite readable, so I used black glass powder that I melted in the front plate. To provide a wide touch zone for each number, I used a copper sheet that I sticked at the back of the glass. I then soldered a wire at the back of the copper sheets to get to the Teensy input :

Keypad

The screen used is a 2×16 LiquidCrystal that I wired to pins 7 to 12 on the Teensy. Using sugru, I glued the screen to the front plate and let all the wires flowing to give a “hacked” look to the whole thing.

Full

Full source


int inputs[] = {0, 1, 15,16,17,18,19, 22,23};
int thresholds[] = {800,800,800,800,800,1000,1000,900,1000};

#include <LiquidCrystal.h>

LiquidCrystal lcd(12,11,10,9,8,7);

void setup(){

lcd.begin(16,2);

for (int i=0;i<10;i++){
pinMode(inputs[i], INPUT);
}
}

int readStatus(){
while (1) {
for (int i=0;i<10;i++){
if (touchRead(inputs[i]) > thresholds[i] ){
delay(300);
return i+1;
}
}
}
delay(10);
}

void printFlag(){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Flag is b2sum of");
delay(2000);
lcd.setCursor(0,1);
lcd.print("XXXXXXXXXXXXXXXX");
delay(5000);
lcd.clear();
}

void printWrong(){
lcd.clear();
lcd.setCursor(0,0);
lcd.print(" WRONG");
delay(2000);
lcd.clear();
}

//Serial must be 598264
boolean checkSerial(int serial){
if ( (serial % 10) != 4) {
return false;
}
int tmp = 1;
for (int i=0;i<6;i++){
tmp = tmp << 1;
}
if ( (serial % 100) != tmp) {
return false;
}
if ( (serial & 0xff) != 248) {
return false;
}
if ( ((serial >> 8) & 0xff) != tmp/2) {
return false;
}
if ( (serial >> 16) != 9 ) {
return false;
}
return true;
}

void loop(){
int serial = 0;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Enter code :");
for (int i=0;i<6;i++) {
serial = 10*serial + readStatus();
lcd.setCursor(0,1);
lcd.print(serial);
}
if ( checkSerial(serial) ) {
printFlag();
}else{
printWrong();
}
lcd.clear();
}

Challenge

The arduino GUI creates a temporary folder containing all the compiled files, so I took the .elf file and gave it to the contestants. As the binary is not stripped, reversing it is not too complicated :

disassembly

Video

Here is the device working, once you have the correct PIN code (598264) :

[youtube=http://www.youtube.com/watch?v=VOQBLBCgZtU]

MagicBox Project

As you may know from our previous posts – and specially if you were there – Insomni’hack 2011 is now over. As every year we had a lot of fun (and work) creating and developing the challenges and then looking at the teams (mentally) sweating to solve them while we were quietly drinking beer.

Despite the hacking challenges being the important part of the event (with beer drinking, of course) this topic has already been covered on SCRT’s website as well as on various participant’s blogs through solution write-ups and comments and is not our main topic here.

Indeed, like every year, we also try to design a few “side-challenges” aimed at amusing visitors and people having a break from the actual contest. These challenges do not provide any points (but can provide free beer) but allow us to explore and present a few other “hack-tivities” that cannot be easily incorporated in the main contest.

By doing these, we have over the years organised lock picking workshops, social engineering challenges and other alike events. This year however we wanted to add a more hardware related challenge. Something that would be fun for us to design and build and that could then be fun for visitors (or any other participant) to solve. That’s how the “Magic Box” project is born! As the project’s codename does not suggest, the original idea was to build a box stuffed with traps that the challengers would have to open.

Before going any further, we have to make very clear that none of the people that have been building this project (actually two SCRT engineers) is an electronics engineer. This will explain (and we hope) excuse some of our choices as well as some implementation details that would certainly cause a heart attack to any “real” electronics guy. This being said, the idea behind this post is to present this “box” and explain how it was built. Extracted from its context it is certainly not the most useful project to reproduce but it may actually give you any ideas for your projects.

So let’s start from the beginning and present the box itself as is was presented to the challengers. It’s a wooden box having on it’s cover a two-digit display a LED and three potentiometers. The display was continuously counting down seconds, starting from 90 and each countdown step was underlined by a “beep”. Based on this, the challengers were given three attempts to open the box (by non-destructive means) without firing the alarm… simple, no?

So, before you ask, YES it was meant to look like some “Hollywood” inspired action movie bomb. And despite Bruce Willis did not show up to deactivate it, one participant succeeded!

Let’s now go deeper into the details and show what the participants could not see (unless they suceeded opening it) : the inside of the box !

At that point you certainly have already noticed the Arduino board. Yes, this project is based on Arduino! Why? Because it is simple, fun to work with and well documented! It would certainly have been interesting and cleaner, after the development phase, to port the project on a “naked” AVR microcontroller but we did not have time to do so. Maybe for Insomni’hack 2011.

So, how does it work? First of all, the Arduino is used to drive the display board fixed to the box cover.

This board uses a 74HC595 shift register to drive two 7-segment elements. It also has a buzzer used to do the countdown “beeps” and the alarm sound. In order to spare one shift register, the display is multiplexed between the two elements. The whole process (countdown and display multiplexing) is implemented using one of the ATMEGA328 (the AVR microcontroller used in Arduino Uno boards) 8-bit timers : Timer 2. In more details, the timer is clocked it a prescaler of CPU_FREQ/256 which causes it to overflow approximately 244 times in one second. At each overflow the display is refreshed (by displaying the proper value on one of the two 7-segment elements thus causing each element to be powered on and off 122 times per second) and every 244 overflows the timer value is decreased. Note that all this process is driven by the timer and timer interrupts thus being completely independent from the main code.

[sourcecode language=”c”]
TCCR2A = 0; // Timer 2, normal mode (no PWM, no OC0A)
TCCR2B = 1 << CS22 | 1 << CS21 | 0 << CS20; //Timer2 Prescaler CPU_FREQ/256
TIMSK2 = 1 << TOIE2; //Timer2 Overflow Interrupt Enable
[/sourcecode]

[sourcecode language=”c”]
//Timer2 overflow interrupt vector handler
ISR(TIMER2_OVF_vect) {
overflows++;
if (overflows == CST_OVFLSEC) {
if (onDisplayValue > 0) {
onDisplayValue–;
if (onDisplayValue < 6)
do_bip(TONE_HIGH);
else
do_bip(TONE_LOW);
}
else {
expired = true;
}
overflows = 0;
}
do_refreshDisplay();
}
[/sourcecode]

This board is then fixed to ther box cover using a properly shaped piece of foam (recycled from some hardware delivery packaging) and … double-sided adhesive tape.

Aside from that board, the box cover was populated with the three potentiometers and the RGB LED all wired together. What for? That’s the real question! Indeed, These pots were used as (logical) unlocking mechanism. Here are the details.

A constantly monitored (by the Arduino) contact fires the alarm if the box is open … except if the proper unlocking code was previously set. This code was however not given in the form of a conventional numbered code but instead as a color! By turning the pots (which unsurprisingly control the R, G and B channels of the RGB LED) the participant changes the color of the LED. The state of these pots is monitored by the Arduino and if set to the proper values (with, of course some tolerance) allow the opening of the box.

At this point, you may ask how can people know that the chosen color is the right one? First of all, a color indication was given to the participant in order to narrow his search (after all, 90 seconds is not much time). Then when the correct color was reached a visual indication was given: the decimal dot on the rightmost 7-segment display shuts off. This indication was not given to participants they thus had to observe it by themselves.

By now, you should have a good idea of how the box should be opened. However, having no indication about it’s details the participants spent a lot of time trying to figure out how to open it. But this would still be too simple if they could take the box and observe it in details (even closed). To prevent this we decided to make it a little bit more complicated by preventing the box from being messed with. The idea was simple: if the box is lifted from the table the alarm screams. This was pretty effective as it was one of the first participants’ reflexes. Fortunately they were given three attempts in order to learn from their mistakes.

From an implementation point of view, this was done by embedding a photocell under the box and monitoring it (through a voltage divider schema) from the Arduino. This way if the box was lifted the increased luminosity was detected by the Arduino and used to fire the alarm. This also provided an additional (bonus) trap as the photocell is sensitive enough to detect light coming from above, even when the box is on the table. Consequently it tends to fire the alarm if the box is open too wide, even if the proper unlock code is set 🙂

From an implementation point of view, all the fix elements were glued or taped to the box and connections were made to the Arduino. But as we wanted to retain the possibility to easily remove the Arduino itself, all the connection have been soldered to some hand-made “shield” (I apologize to the whole Arduino community for using the word “shield” for such a piece of crappy soldering on a strip-board…) that could the be plugged into the Arduino.

Finally, having described all the major internals of the box (and having omitted all the details that are too dirty to be posted), just remains to show the result. The two videos below show how it could go wrong and how the box should be opened.

In the end one participant could manage to successfully open it (we were less regarding to the number of re-attempts as our beer level was increasing) and has been given a free Arduino Uno board as a reward. As we had a lot of fun preparing this challenge it is most probable that Insomni’hack 2012 will have it’s “Magic Box” challenge again with new ideas and dirty soldering 🙂

[youtube=http://www.youtube.com/watch?v=dXjjjJ46ejA]

[youtube=http://www.youtube.com/watch?v=h_6BjGjsxeA]

 

 

Mi[fare|fun]: Recyclez vos Abonnements de Transports

Introduction

Le but de ce petit article est de montrer comment interagir avec une carte (RFID) Mifare Classic à l’aide d’un simple bout de code Python afin de, par exemple, s’en servir au sein de sa propre application. De plus, nous allons prendre comme exemple, non pas une carte vierge, mais une carte normalement dédiée à une autre application – un abonnement de transports publics – et démontrer comment il est possible de s’en servir également pour ses propres besoins sans altérer sa fonction originale.

Toutefois, avant de commencer, notons deux remarques importantes: (1) Cet article n’a pas pour but de parler des attaques menées sur les cartes Mifare Classic au cours de ces dernières années. Si ce sujet vous intéresse, de nombreuses publications sont disponibles sur la toile. (2) Le sujet de cet article n’est pas de casser la clé protégeant l’accès au contenu de l’abonnement lui-même ni même d’accéder d’une quelconque manière à ces données protégées.

Ceci étant dit, nous allons voir qu’il y a tout de même de quoi s’amuser avec une de ces cartes, et notamment qu’il est possible de “recycler” un ancien abonnement pour d’autres applications.

La Carte

La carte que nous allons utiliser est un abonnement de transports publics de la région lausannoise. Il s’agit en fait d’une carte “Mifare Classic 1K”, à savoir une des cartes RFID HF (13.56 MHz) les plus utilisées, notamment dans des application liées aux transports publics. L’organisation interne de la mémoire de ce type de cartes est largement documentée, nous allons donc nous limiter au strict minimum, à savoir que la mémoire est divisée en 16 secteurs indépendants, chacun d’eux protégé par une paire de clés d’accès.

Chacun de ces secteurs est divisé en 4 blocs, les 3 premiers servant à stocker des données et le dernier stockant les conditions d’accès ainsi que les clés elles-mêmes. Ces clés sont au nombre de deux – A et B – et les condition d’accès peuvent être définies en fonction de l’une ou l’autre (ce qui permet, par exemple, d’avoir une clé permettant uniquement de lire les données et une autre permettant de les modifier). Une carte Mifare Classic 1K dispose donc de 64 blocs, de 16 bytes chacun, ce qui fait un total de 1024 bytes (d’où le 1K).

Avant de pouvoir procéder à une opération de lecture ou d’écriture sur un bloc, il est nécessaire de s’authentifier pour ce bloc auprès de la carte. Ainsi, une seule carte Mifare Classic 1K pourrait être simultanément utilisée par 16 applications indépendantes, chacune utilisant un secteur (et donc une paire de clés) distinctes (pour autant que l’espace de stockage proposé par un seul bloc soit suffisant pour chaque application). Il est toutefois à noter que le tout premier bloc de la carte est un peu spécial et contient notamment l’identifiant unique (UID) de la carte.

Outils

Divers outils (software) disponibles sur le web permettent d’interagir avec une carte Mifare Classic, notamment l’excellent outil RFIDIOt. Dans notre cas, nous avons utilisé un petit script Python spécialement écrit pour l’occasion, que nous avons nommé pymfc.py. Ce script repose sur le module pyscard, prévu pour permettre d’écrire facilement des applications Python utilisant des cartes à puce au travers de l’interface PC/SC (incluse dans Windows) ou PCSCLite (systèmes *nix).

Pour ce qui est du matériel, nous utilisons un simple lecteur USB OMNIKEY 5321, capable d’interagir avec les cartes à puce “standard” ainsi qu’avec les cartes RFID (13.56 MHz). Ce lecteur – en plus d’être facile à trouver dans le commerce – a l’avantage de supporter l’API PC/SC nous permettant ainsi d’utiliser le module pyscard.

Lecture

Equipés de nos outils – pymfc.py et le lecteur USB – nous allons essayer de lire le contenu de notre abonnement de transports publics. Bien entendu nous ne connaissons pas les clés secrètes utilisées par la compagnie de transports pour protéger ses données. Ce que nous connaissons, en revanche, c’est les clés par défaut utilisées par le constructeur lors de la fabrication des cartes (appelées clés “de transport”). Ces clés sont publiques et disponibles dans la documentation:

Clé A : A0A1A2A3A4A5
Clé B : B0B1B2B3B4B5

Nous pouvons donc essayer de lire la carte à l’aide d’une de ces clés, par exemple la clé A. En tentant de lire l’intégralité de la carte (blocs 0 à 63) nous obtenons le résultat ci-dessous (tronqué). A noter que l’identifiant de la carte ainsi que le contenu du premier bloc ont été masqués afin d’éviter toute identification.

[sourcecode language=”text”]
$> python pymfc.py -c r -k A0A1A2A3A4A5 -t A -b 0-63
[#] Connected to reader: OMNIKEY CardMan 5×21 00 01
[#] Reading card UID: XX XX XX XX
[#] Reading card data using key: A0A1A2A3A4A5 (A)
[#] ========== CARD DATA ==========
[*] Sector 00 | block 00 : XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
[*] Sector 00 | block 01 : XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
[*] Sector 00 | block 02 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 00 | block 03 : 00 00 00 00 00 00 69 67 89 00 00 00 00 00 00 00
[*] Sector 01 | block 00 : — NO ACCESS —
[*] Sector 01 | block 01 : — NO ACCESS —
[*] Sector 01 | block 02 : — NO ACCESS —
[*] Sector 01 | block 03 : — NO ACCESS —
[*] Sector 02 | block 00 : — NO ACCESS —
[*] Sector 02 | block 01 : — NO ACCESS —
[*] Sector 02 | block 02 : — NO ACCESS —
[*] Sector 02 | block 03 : — NO ACCESS —
[*] Sector 03 | block 00 : — NO ACCESS —
[*] Sector 03 | block 01 : — NO ACCESS —
[*] Sector 03 | block 02 : — NO ACCESS —
[*] Sector 03 | block 03 : — NO ACCESS —
[*] Sector 04 | block 00 : — NO ACCESS —
[*] Sector 04 | block 01 : — NO ACCESS —
[*] Sector 04 | block 02 : — NO ACCESS —
[*] Sector 04 | block 03 : — NO ACCESS —
[*] Sector 05 | block 00 : — NO ACCESS —
[*] Sector 05 | block 01 : — NO ACCESS —
[*] Sector 05 | block 02 : — NO ACCESS —
[*] Sector 05 | block 03 : — NO ACCESS —
[*] Sector 06 | block 00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 06 | block 01 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 06 | block 02 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 06 | block 03 : 00 00 00 00 00 00 7F 07 88 69 00 00 00 00 00 00
[*] Sector 07 | block 00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 07 | block 01 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 07 | block 02 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 07 | block 03 : 00 00 00 00 00 00 7F 07 88 69 00 00 00 00 00 00
…continued…
[/sourcecode]

On constate donc que les secteurs 1 à 5 sont illisibles et on peut donc en déduire qu’il s’agit des secteurs utilisés par la compagnie de transports. Par contre, tous les autres secteurs ont été laissés en configuration par défaut et sont donc lisibles et à notre entière disposition! On notera au passage qu’il est impossible de lire les clé de chiffrement (stockées dans le dernier bloc de chaque secteur). Ainsi les 6 premiers (clé A) et 6 derniers (clé B) bytes de ses blocs retournent toujours 0x00 lorsqu’on tente de les lire.

Ecriture

Prenons donc possession d’un secteur, par exemple le dernier (secteur 15, donc les blocs 60 à 63). Pour cela, commençons par choisir deux clés “secrètes” pour notre application:

Clé A: 0A0A0A0A0A0A
Clé B: 0B0B0B0B0B0B

Maintenant définissons ces clés comme clés de protection pour le secteur 15. Pour cela, il faut ré-ecrire le dernier bloc de ce secteur (conformément au conditions d’accès par défaut,  il est nécessaire de s’authentifier avec la clé B pour pouvoir ré-ecrire ce bloc). Nous prendrons toutefois garde à ne pas altérer les conditions d’accès mais uniquement les clés en elles-mêmes.

[sourcecode language=”text”]
$> python pymfc.py -c w -k B0B1B2B3B4B5 -t B -b 63 -d 0A0A0A0A0A0A7F0788690B0B0B0B0B0B

[#] Connected to reader: OMNIKEY CardMan 5×21 00 01
[#] Reading card UID: XX XX XX XX
[#] Writing data to card using key: B0B1B2B3B4B5 (B)
[/sourcecode]

On note alors qu’il n’est plus possible de lire le contenu de ce secteur avec la clé de transport utilisée précédemment. Par contre il est possible de le lire avec notre nouvelle clé “secrète”. Nous sommes maintenant “propriétaires” de ce secteur qui ne pourra être accédé que par les lecteurs disposant de notre clé.

[sourcecode language=”text”]
$> python pymfc.py -c r -k A0A1A2A3A4A5 -t A -b 60-63

[#] Connected to reader: OMNIKEY CardMan 5×21 00 01
[#] Reading card UID: XX XX XX XX
[#] Reading card data using key: A0A1A2A3A4A5 (A)
[#] ========== CARD DATA ==========
[*] Sector 15 | block 00 : — NO ACCESS —
[*] Sector 15 | block 01 : — NO ACCESS —
[*] Sector 15 | block 02 : — NO ACCESS —
[*] Sector 15 | block 03 : — NO ACCESS —
[#] ========== END DATA ==========

$> python pymfc.py -c r -k 0A0A0A0A0A0A -t A -b 60-63

[#] Connected to reader: OMNIKEY CardMan 5×21 00 01
[#] Reading card UID: XX XX XX XX
[#] Reading card data using key: 0A0A0A0A0A0A (A)
[#] ========== CARD DATA ==========
[*] Sector 15 | block 00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 15 | block 01 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 15 | block 02 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 15 | block 03 : 00 00 00 00 00 00 7F 07 88 69 00 00 00 00 00 00
[#] ========== END DATA ==========
[/sourcecode]

A partir de là il ne nous reste plus qu’à stocker sur les trois premiers blocs de ce secteur les données “utiles” à notre application. On pourrait, par exemple, y stocker des données d’identification de l’utilisateur ou encore un mot de passe afin d’utiliser la carte comme token d’authentification.

[sourcecode language=”text”]
$> python pymfc.py -c w -k 0A0A0A0A0A0A -t A -b 60 -d 68656C6C30776F726C64000000000000
[#] Connected to reader: OMNIKEY CardMan 5×21 00 01
[#] Reading card UID: XX XX XX XX
[#] Writing data to card using key: 0A0A0A0A0A0A (A)

$> python pymfc.py -c r -k 0A0A0A0A0A0A -t A -b 60-63
[#] Connected to reader: OMNIKEY CardMan 5×21 00 01
[#] Reading card UID: XX XX XX XX
[#] Reading card data using key: 0A0A0A0A0A0A (A)
[#] ========== CARD DATA ==========
[*] Sector 15 | block 00 : 68 65 6C 6C 30 77 6F 72 6C 64 00 00 00 00 00 00
[*] Sector 15 | block 01 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 15 | block 02 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[*] Sector 15 | block 03 : 00 00 00 00 00 00 7F 07 88 69 00 00 00 00 00 00
[#] ========== END DATA ==========
[/sourcecode]

Bien entendu, au vu des attaques présentées ces dernières années sur Mifare Classic il vaut certainement mieux ne pas s’en servir pour des applications trop sensibles 😉

Reverse engineering de binaires pour GPGPU

Introduction

L’utilisation des GPGPU (General Purpose Graphical Processing Unit) n’est pas un sujet nouveau dans le monde de la sécurité. De nombreux projets de récupération de mot-de-passe utilisant d’hors et déjà cette technologie dans le but d’améliorer leurs performances (pyrit, CUDA multihash bruteforcer, …). Lors de la conférence RUXCON 2008, Daniel Reynaud a fait une présentation sur l’utilisation des GPGPU par des codes malveillants. Ces derniers délocaliseraient l’exécution de certains algorithmes (par exemple celui générant les noms de domaines permettant de se connecter à un canal de contrôle) sur la carte graphique afin d’empêcher une analyse standard de code ia32.

Deux ans après cette présentation de tels codes malveillants se font rares notament du fait que les auteurs cherchent à infecter le plus de machines possibles, ce qui est plus compliqué quand le code est compilé pour un type de chipset graphique précis. Néanmoins, si l’on regarde les caractéristiques de la gamme Apple, plateforme devenant une cible grandissante pour les auteurs de malwares,  on s’aperçoit que la majorité des machines sont équipées d’une carte graphique NVIDIA et que le framework OpenCL est inclu dans l’installation de base. On pourrait imaginer que ces deux propriétés favoriseront l’utilisation des GPGPU lors d’attaques ciblant un système OSX.

La possible utilisation des GPGPU par des codes malveillants ou des solutions de protection logicielle sont des motifs poussant à se pencher sur l’analyse du code compilé pour ces plateformes (et également pour la beauté de l’Art comme diraient certains).

Les bases

Différents frameworks existent pour le développement de code pour GPGPU, comme par exemple CUDA (NVIDIA) et OpenCL (sur-couche permettant d’obtenir du code compilable sur différentes architectures). Le développement se fait dans une extension du C et le passage d’arguments est réalisé via des appels à des fonctions prédéfinies depuis le code exécuté sur le CPU (ex: CUDA!cudaMemcpy, openCL!clEnqueueReadBuffer, …). Dans le cas d’un code utilisant le GPGPU pour décoder des informations, la récupération du texte-clair lors d’une analyse serait simplifiée via la mise en place de breakpoints sur ces API. Ci-dessous, un exemple de kernel CUDA réalisant un parcours de chaine de caractères.

[sourcecode language=”cpp”]
// not so usefull function
__global__ void mystrlen(char* str, int* length)
{
int i = 0;

while (str[i])
++i;
*length = i;
}
[/sourcecode]

L’appel à cette fonction peut se faire de la manière suivante depuis le code C:

[sourcecode language=”cpp”]
cudaMalloc((void**)&devLen, sizeof (int));
cudaMalloc((void**)&devStr, MAX_SIZE * sizeof (char));
cudaMemcpy(devStr, argv[1], strlen(argv[1]) + 1, cudaMemcpyHostToDevice);
mystrlen <<< 1, 1 >>> (devStr, devLen);
cudaMemcpy(&len, devLen, sizeof (int), cudaMemcpyDeviceToHost);
[/sourcecode]

Dans le cas de CUDA, le code est compilé dans une forme intermédiaire, PTX, qui sera ensuite inclu dans le binaire. En spécifiant une architecture donnée, le PTX est compilé en CUBIN et optimisé pour cette dernière.

Extraction du code PTX

Dans le cas où l’application a été compilée sans spécifier d’architecture, le code PTX sera directement présent dans le binaire et compilé pour l’architecture virtuelle sm_10 par défaut. Une recherche sur des mots clefs comme .target permet de le localiser. Travis Goodspeed a développé un outil réalisant l’extraction du code PTX depuis ce type de binaire.

[sourcecode language=”bash”]
$ nvcc mystrlen.cu
$ cudaDump < a.out
.version 1.4
.target sm_10, map_f64_to_f32
// compiled with /usr/local/cuda/bin//../open64/lib//be
// nvopencc 3.2 built on 2010-11-11

.entry _Z8mystrlenPcPi (
.param .u32 __cudaparm__Z8mystrlenPcPi_str,
.param .u32 __cudaparm__Z8mystrlenPcPi_length)
{
.reg .u32 %r<10>;
.reg .pred %p<4>;
.loc 28 16 0
$LDWbegin__Z8mystrlenPcPi:
ld.param.u32 %r1, [__cudaparm__Z8mystrlenPcPi_str];
ld.global.s8 %r2, [%r1+0];
mov.u32 %r3, 0;
setp.eq.s32 %p1, %r2, %r3;
@%p1 bra $Lt_0_2306;
ld.param.u32 %r1, [__cudaparm__Z8mystrlenPcPi_str];
mov.s32 %r4, %r1;
mov.s32 %r5, 0;
$Lt_0_1794:
//<loop> Loop body line 16, nesting depth: 1, estimated iterations: unknown
.loc 28 21 0
add.s32 %r5, %r5, 1;
add.u32 %r4, %r4, 1;
ld.global.s8 %r6, [%r4+0];
mov.u32 %r7, 0;
setp.ne.s32 %p2, %r6, %r7;
@%p2 bra $Lt_0_1794;
bra.uni $Lt_0_1282;
$Lt_0_2306:
mov.s32 %r5, 0;
$Lt_0_1282:
.loc 28 22 0
ld.param.u32 %r8, [__cudaparm__Z8mystrlenPcPi_length];
st.global.s32 [%r8+0], %r5;
.loc 28 23 0 exit;
$LDWend__Z8mystrlenPcPi:
} // _Z8mystrlenPcPi
[/sourcecode]

Dans le cas d’un binaire compilé pour une architecture spécifique, comme dans l’exemple ci-dessous, l’outil decuda permet à partir du CUBIN d’obtenir le code PTX, excepté pour du code compilé à partir de CUDA 3.x du à un changement de format (CUBIN > ELF). La solution consiste à utiliser le script elfToCubin.py pour réaliser la conversion.

[sourcecode language=”bash”]
$ nvcc mystrlen.cu -arch=compute_10 -code=sm_10,sm_13
[/sourcecode]

Le binaire compilé à l’aide de la ligne de commande précédente encapsule les 2 binaires ELF correspondants à sm_10 et sm_13 dans le segment nommé __const.

ELF dans la section __const du binaire

Analyse du code

Une des premières choses que l’on peut remarquer en lisant le code PTX ci-dessus est le fait qu’une partie des symboles a été conservée. On retrouve mystrlen et ses deux arguments str et length.

Les instructions se décomposent de la manière suivante: opération.<espace>.type. La première instruction de la fonction mystrlen lit (ld)  la valeur de type entier non-signé (u32) contenue dans le paramètre (param) str et la stocke dans le registre r1.

A la ligne 19, l’opération de comparaison setp est utilisée afin de vérifier si les registres r2 et r3 sont égaux (eq) et stocke le résultat dans le registre de prédiction p1. La ligne suivante correspond à une opération de modification du flux d’exécution. Ici, l’opération à réalisée (bra) est précédée d’un “predicate guard” (@[!]<predicate register>). Dans ce cas, le saut au label $Lt_0_2386 sera exécuté si le registre p1 contient la valeur vrai (r2 == r3). On retrouve le corps de la boucle while entre les lignes 27 et 33 avec les registres r4 et r5 qui représentent respectivement un pointeur sur str et i.

La documentation fournie par NVIDIA permet d’en apprendre plus sur ce langage, mais le jeu d’instructions reste limité et assez proche de langages assembleurs courants pour qu’il soit facilement compréhensible.

En plus d’une analyse statique, il est possible de réaliser une analyse dynamique de kernel CUDA à l’aide de gdb-cuda.

Analyse du iStorage diskGenie

Le diskGenie de la societé iStorage est un disque dur externe USB qui a la particularité de chiffrer son contenu et d’autoriser l’accès aux données uniquement lorsque l’utilisateur à saisi un mot de passe.

Afin de tester la résistance du disque, de nombreuses pistes ont été explorées afin de valider le fonctionnement correct du disque.

Analyse fonctionnelle

Lecture avec un autre contrôleur

La première idée qui vient à l’esprit sur ce genre de matériel est de confirmer le chiffrement des données en ouvrant le boîtier et en connectant le disque à un contrôleur SATA-USB classique. Si le chiffrement n’est pas opérant, les données peuvent être lues directement.

Dans le cas du diskGenie, les données sont entièrement chiffrées et ne permettent donc pas de lire le contenu du disque de cette manière.

Gestion du chiffrement

Comme indiqué dans la documentation, le disque utilise un chiffrement AES-256. Les données sont donc chiffrées avec une clé unique qui est utilisée pour le chiffrement et le déchiffrement du disque. Pour autant que la clé de chiffrement soit fixe, la réinitialisation aux valeurs d’usine du boîtier pourrait alors permettre de déchiffrer le disque avec le mot de passe initial (123456). Cette attaque a été tentée, mais à nouveau sans succès.

Analyse électronique

La prochaine étape est de valider le fonctionnement électronique du boîtier afin d’écarter un possible bypass électronique du mot de passe.

Comme on peut le voir ici, le circuit fait appel à peu de composants intégrés, ce qui va faciliter la suite de l’analyse.

Analyse des composants

Les composants principaux du circuit sont les suivants :

INITIO INIC-1607E

Il s’agit du composant principal de ce circuit. Le constructeur en donne une description assez explicite : “Bridge Controller IC SATA to USB with AES”.

Après plusieurs recherches, impossible de trouver la datasheet du produit, il a fallu prendre contact avec iStorage pour récupérer une partie de la documentation qui ne soit pas sous NDA. Le pinning de la puce est donné dans la documentation comme suit :

PIC16F883

Il s’agit d’un microcontrôleur fabriqué par Microchip. Très utilisé dans l’industrie et par les amateurs d’électronique (du moins avant la démocratisation de l’Arduino).

La documentation étant disponible librement sur le site de Microchip, trouver le pinning du circuit est facile :

Analyse du circuit

Une fois les composants identifiés, il est nécessaire de découvrir les connexions entre eux afin de déterminer les tâches de chaque puce. A l’aide d’un multimètre et de (beaucoup de) patience, le schéma global du circuit peut être obtenu :

Comme on peut le voir, le PIC est utilisé pour l’interprétation du code entré sur le clavier. Il est également relié au INIC-1607E. Si ce lien entre le microcontrôleur et le contrôleur de disque permet l’activation du disque, il y a deux options :

  1. Le PIC envoie un signal “OK” au INIC-1607E
  2. Le PIC envoie la clé de chiffrement au contrôleur de disque.

Dans le premier cas, il est possible de rejouer ce signal à l’aide d’un second microcontrôleur.

Afin de tester cela, l’utilisation d’un oscilloscope numérique est obligatoire. Plusieurs séries de mesures ont été effectuées :

  • Saisie d’un mot de passe incorrect
  • Saisie du bon mot de passe
  • Réinitialisation et saisie du bon mot de passe

Les mesures effectuées montrent un signal entièrement différent des signaux émis entre deux connexions au boîtier. Preuve qu’au moins une partie de la clé est transmise au contrôleur.

Conclusion

Après avoir testé de nombreuses attaques, ce boîtier semble apte à protéger correctement les données qu’il contient. Mention très bien pour la gestion du chiffrement, perfectible dans d’autres boîtiers du même type.

Cependant, il reste une possibilité pour un attaquant déterminé de démonter le microcontrôleur et ainsi d’accéder directement au contenu de celui-ci. Il n’a pas été possible de tenter cette attaque par manque de matériel.

Dernière recommandation, choisir un bon mot de passe. Le clavier étant exclusivement numérique, un mot de passe facilement devinable détruit complètement la protection. Sachant qu’il est possible de faire jusqu’a 100 essais avant de bloquer complètement le disque, cela laisse une fenêtre suffisamment grande pour tenter des valeurs familières au propriétaire du disque.