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
- Création d’une config Atmel 2560 pour Ida
- Extraction de la RAM depuis la ROM
- Reverse final
É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