ECWCTF Quals : Escape Game

Introduction

Durant les qualifications de l’ECWCTF, j’ai trouvé un des challenges, un reverse de rom d’Arduino, plutôt intéressant, c’est pourquoi j’ai décidé de faire un write-up. Je tenais également à remercier Thanat0s pour son article qui m’a aidé à résoudre ce challenge : https://thanat0s.trollprod.org/2014/01/loader-un-binaire-arduino-dans-ida/

Sommaire

Énoncé du challenge :

On your way to join the ECW HeadQuarter room a door blocks you off the road.
Next to it four buttons that seem to be used to open it.
One of your friends accompanying you already seems to know the place and provides you some documents on the device.
It would be based on an electronic card based on Atmel Mega 2560.
Can you open the door?

The flag to validate will be of type 1-3-2-1-2 (the figures representing the number of consecutive buttons to press to open the door)

Link to the circuit diagram: [here]
Link to the embedded code in the microcontroller: [here]
For pin not noted on the diagram:
    - button 1 : pin 22 (on the board) ; pin 78 (sur le µc) : PA0 (AD0)
    - button 2 : pin 23 (on the board) ; pin 77 : PA1 (AD1)
    - button 3 : pin 24 (on the board) ; pin 76 : PA2 (AD2)
    - button 4 : pin 25 (on the board) ; pin 75 : PA3 (AD3)
    - door : pin 5 (on the board) ; pin 5 : (OC3A/AIN1) PE3

Création d’une config Atmel 2560 pour IDA :

La première étape que j’ai effectuée a consisté à créer une config IDA pour Atmel 2560 car elle ne fait pas partie des configurations déjà existante dans IDA et que je n’en ai pas trouvé sur internet.

Donc c’est parti on sort notre clavier et on recopie la datasheet : http://ww1.microchip.com/downloads/en/devicedoc/atmel-2549-8-bit-avr-microcontroller-atmega640-1280-1281-2560-2561_datasheet.pdf

Pour créer une config IDA, rien de trés compliqué, il faut commencer par renseigner les adresses des différentes sections de mémoire. Dans notre cas il suffit de recopier la figure 8-2 de la datasheet :

RAM=8192
ROM=262144
EEPROM=4096

; MEMORY MAP
;See figure 8-2
area DATA GPWR_ 	0x0000:0x001F General registers ( 32 registers )
area DATA FSR_  	0x0020:0x005F 64 I/O registers
area DATA EXTIO_	0x0060:0x01FF 416 External U/O registers
area DATA I_SRAM_	0x0200:0x21FF Internal SRAM ( 8192 * 8 )
area DATA E_SRAM_	0x2200:0xFFFF External SRAM ( 0-64K * 8 )

Ensuite la partie rigolote ( j’en ai passé du temps ), recopier la table Register summary ( il y en a 4 pages ):

Pour cela, rien de bien compliqué, on va tout en bas et on recopie les valeurs jusqu’en haut.

On obtient donc un joli fichier de ~ 400 lignes qu’on ajoute à la fin du fichier config/avr.cfg dans le répertoire d’installation d’IDA. Configuration ATmel 2560

Normalement, notre configuration va se charger dans ida \o/.

Extraction de la RAM à partir de la ROM :

Maintenant que notre ROM se charge dans IDA, on va extraire la RAM de la ROM afin de pouvoir savoir les valeurs assignées aux variables ( utile pour associer les pins à leurs fonctionnement ).

Donc pour cela on recherche la méthode __RESET qui va nous être utile pour trouver les offsets.

On obtient donc cette fonction :

ROM:0114 __RESET:                                ; CODE XREF: ADC__0↑j
ROM:0114                 clr     r1
ROM:0115                 out     SREG, r1
ROM:0116                 ser     r28
ROM:0117                 ldi     r29, 0x21 ; '!'
ROM:0118                 out     SPH, r29
ROM:0119                 out     SPL, r28
ROM:011A                 ldi     r16, 0
ROM:011B                 out     EIND, r16
ROM:011C                 ldi     r17, 2
ROM:011D                 ldi     r26, 0
ROM:011E                 ldi     r27, 2          ; 0x200 -> Adresse dans la RAM
ROM:011F                 ldi     r30, 0xB8
ROM:0120                 ldi     r31, 0x11       ; 0x11B8 -> Adresse dans la ROM
ROM:0121                 ldi     r16, 0
ROM:0122                 out     RAMPZ, r16
ROM:0123                 rjmp    loop_end
ROM:0124 ; ---------------------------------------------------------------------------
ROM:0124
ROM:0124 loop_copy:                              ; CODE XREF: __RESET+14↓j
ROM:0124                 elpm    r0, Z+
ROM:0125                 st      X+, r0
ROM:0126
ROM:0126 loop_end:                               ; CODE XREF: __RESET+F↑j
ROM:0126                 cpi     r26, 0x94
ROM:0127                 cpc     r27, r17
ROM:0128                 brne    loop_copy
ROM:0129                 ldi     r18, 5
ROM:012A                 ldi     r26, 0x94       ; 0x94 -> Taille de la zone à extraire
ROM:012B                 ldi     r27, 2
ROM:012C                 rjmp    loc_12E
ROM:012D ; -----------------------------------------

Du coup on créé un fichier de RAM contenant :

Ce qui nous donne le fichier suivant RAM.bin

Maintenant il n’y a plus qu’à l’importer dans IDA :)

Dans la subview Segments on les supprime tous sauf la ROM :

Et on charge notre fichier de RAM:

Voilà les valeurs sont associées aux bons offsets :

Reverse Final

Maintenant plus qu’à reverser \o/

Pour commencer on va chercher la fonction setup() qui initialise les variables , celle-ci se trouve à l’offset 0x143:

ROM:0143                 ldi     r20, 0x80
ROM:0144                 ldi     r21, 0x25 ; '%'
ROM:0145                 ldi     r22, 0
ROM:0146                 ldi     r23, 0
ROM:0147                 ldi     r24, 9
ROM:0148                 ldi     r25, 3
ROM:0149                 rcall   sub_6E9
ROM:014A                 ldi     r22, 2
ROM:014B                 lds     r24, unk_200210
ROM:014D                 rcall   pin_mode
ROM:014E                 ldi     r22, 2
ROM:014F                 lds     r24, pin_23
ROM:0151                 rcall   pin_mode
ROM:0152                 ldi     r22, 2
ROM:0153                 lds     r24, pin_24
ROM:0155                 rcall   pin_mode
ROM:0156                 ldi     r22, 2
ROM:0157                 lds     r24, pin_25
ROM:0159                 rcall   pin_mode
ROM:015A                 ldi     r22, 1
ROM:015B                 lds     r24, pin_door
ROM:015D                 rcall   pin_mode
ROM:015E                 ldi     r22, 0x12
ROM:015F                 ldi     r23, 2
ROM:0160                 ldi     r24, 9
ROM:0161                 ldi     r25, 3
ROM:0162                 rjmp    sub_897

Ce qui une fois traduit en code Arduino donne :

int button1 = 22;
int button2 = 23;
int button3 = 24;
int button4 = 25;
int pin_door = 5;

void setup(){
	pinMode(button1,INPUT_PULLUP);
	pinMode(button2,INPUT_PULLUP);
	pinMode(button3,INPUT_PULLUP);
	pinMode(button4,INPUT_PULLUP);
	pinMode(pin_door,INPUT);
}

Maintenant que l’on sait comment sont initialisées les valeurs, on recherche la fonction qui va vérifier le code pin. Notre jolie fonction, un peu velue se trouve à l’offset 0x0163, je vous donne un petit aperçu :

Alors là je vais simplifier l’explication, mais on peut ignorer une partie complète qui vérifie le bouton appuyé :

Here comes the interesting part :

Ici on a 4 blocs qui vont effectuer des opérations en fonction des boutons pressés.

Ici on a les étapes qui vont réinitialiser ou incrémenter un compteur en fonction du bouton appuyé.

Et enfin un bloc qui vérifie si notre compteur est égal à 12 et qui dans ce cas affiche le message de succès.

Donc ici on va chercher un moyen de faire passer notre compteur à 12 pour cela il faut passer par ce bloc qui incrémente le compteur et réinitialise le reste des variables de la fonction.

Nos 4 blocs du haut vérifient chacun une condition de la façon suivante :

Ce qui, traduit approximativement nous donne :

if(valeur_bouton + table_pass[compteur*2] == 0 ):
	goto success
else:
	goto fail

Donc maintenant on n’a plus qu’a trouver cette fameuse table, c’est là que notre extraction de RAM est utile, puisque qu’on la retrouve facilement à l’adresse 0x026B de la RAM :

seg001:026B                 .db 0xFF
seg001:026C                 .db 0xFF
seg001:026D                 .db 0xFF
seg001:026E                 .db 0xFF
seg001:026F                 .db 0xFF
seg001:0270                 .db 0xFF
seg001:0271                 .db 0xFD
seg001:0272                 .db 0xFF
seg001:0273                 .db 0xFC
seg001:0274                 .db 0xFF
seg001:0275                 .db 0xFE
seg001:0276                 .db 0xFF
seg001:0277                 .db 0xFF
seg001:0278                 .db 0xFF
seg001:0279                 .db 0xFD
seg001:027A                 .db 0xFF
seg001:027B                 .db 0xFC
seg001:027C                 .db 0xFF
seg001:027D                 .db 0xFD
seg001:027E                 .db 0xFF
seg001:027F                 .db 0xFD
seg001:0280                 .db 0xFF
seg001:0281                 .db 0xFD
seg001:0282                 .db 0xFF

Il n’y a plus qu’à en prendre 1 sur 2 et on a le flag :

1-1-1-3-4-2-1-3-4-3-3-3