FR EN

Bomb Server

<< Development Server

Un seul service sur le port 31337. Il s'agit en réalité d'une interface de gestion de bombe, qui est armée, sur laquelle on peut effectuer plusieurs actions. Désarmer ne marche pas bien entendu, mais on peut télécharger le firmware et flasher le périphérique.

Le but de l'épreuve est donc simple : réécrire le firmware de manière à pouvoir désarmer correctement. On télécharge et on étudie donc le firmware. La commande file nous permet de détermine la nature du firmware : un ELF. On trouve 4 symboles dynamiques (objdump -T firmware.elf) : firmware_init, firmware_arm, firmware_disarm et firmware_status. A partir de là ce n'est qu'un petit reverse de fonctions courtes et un travail de réécriture de l'ELF.

On voit rapidement que 0 est un code de retour normal et 1 un code de retour d'erreur. Normal que le désarmement ne marche pas, il n'y a que return 1. En étudiant la fonction d'armement, on voit qu'elle vérifie si *0x20000 vaut 0. Si c'est le cas, alors la fonction se poursuit, sinon retour d'erreur. Ce byte indique donc si la bombe est déjà armée ou non et le seul travail que nous avons à faire est de mettre ce byte à 0.

Pour ne pas avoir à modifier tous les offsets de l'ELF, il faudrait que la somme de la taille du code des 4 fonctions soit la même dans notre nouveau firmware que dans l'ancien. De cette façon il n'y a qu'à modifier les entrées des symboles dynamiques pour bien mettre leur bon point de départ. Ceci nous permet aussi de ne pas avoir à changer la taille de l'image. En effet, étant un peu dans le flou sur ce qui déclencherait ou pas la bombe, on ne peut pas se permettre trop d'écarts par rapport au firmware original. C'est pour ça aussi que j'ai conservé le code des fonctions d'armement et de status, mais après réflexion je ne pense pas que c'était nécessaire.

Pas grand chose à dire de plus sur cette exploitation, sinon qu'elle aurait pu être faite par hexedit/hexer/ht, mais personnellement je voulais être sûr que la structure de l'ELF était bonne donc j'ai fait un petit programme C. Je récupère simplement le code original de chaque fonction, j'en modifie deux pour gagner un peu d'espace nécessaire pour agrandir la fonction disarm et je change les offsets. J'utilise cette mini-librairie de manipulation d'ELF.

#include <stdio.h>
#include <string.h>
#include "elf.h"

#define FILENAME "firmware.img"

// Uncomment to print the original payloads
//#define DISCOVERY

typedef struct firmware_descr {
char * symname;
Elf32_Sym * symbolEntry;
Elf32_Off start, end;
} FirmwareDescr;

FirmwareDescr firmware_init = { "firmware_init", NULL, 0x1c0, 0x1c5 };
FirmwareDescr firmware_arm = { "firmware_arm", NULL, 0x1c6, 0x1f3 };
FirmwareDescr firmware_disarm = { "firmware_disarm", NULL, 0x1f4, 0x1f9 };
FirmwareDescr firmware_status = { "firmware_status", NULL, 0x1fa, 0x249 };

FirmwareDescr * firmware[] = {&firmware_init, &firmware_arm, &firmware_disarm, &firmware_status };
static int nbDescr = 4;

/* Original payload for firmware_init: "\xb8\x00\x00\x00\x00\xc3"
* Original payload for firmware_arm: "\xb9\x00\x00\x02\x00\x8b\x09\x85\xc9\x74\x07\xb8\x01\x00\x00\x00"
"\xeb\x1b\xb9\x00\x00\x02\x00\xc7\x01\x01\x00\x00\x00\x8b\x4c\x24"
"\x04\xba\x04\x00\x02\x00\x89\x0a\xb8\x00\x00\x00\x00\xc3"
* Original payload for firmware_disarm: "\xb8\x01\x00\x00\x00\xc3"
* Original payload for firmware_status: "\xb8\x00\x00\x02\x00\x8b\x4c\x24\x08\x8b\x00\x89\x01\x85\xc0"
"\x74\x22\x8b\x54\x24\x04\x8b\x4c\x24\x0c\xbb\x04\x00\x02\x00"
"\x8b\x1b\x29\xd3\x89\x19\x8b\x4c\x24\x10\xba\x08\x00\x02\x00"
"\x8b\x12\x89\x11\xeb\x14\x8b\x4c\x24\x0c\xc7\x01\x00\x00\x00"
"\x00\x8b\x4c\x24\x10\xc7\x01\x00\x00\x00\x00\xb8\x00\x00\x00"
"\x00\xc3\xeb\xfe\xeb"
*/

/*
* Orig init:
* mov eax, 0
* ret
* 
* New init:
* xor eax, eax
* nop
* ret
*/
static char new_init_payload[] = "\x31\xc0\x90\xc3"; /* On gagne 2 bytes */
/* * Orig disarm:
* mov eax, 1
* ret
* 
* New disarm:
* xor eax, eax
* mov ecx, 0x20000
* mov [ecx], eax
* nop
* nop
* ret
*/
static char new_disarm_payload[] = "\x31\xc0\xb9\x00\x00\x02\x00\x89\x01\xc3"; /* On perd 4 bytes */

/*
* Orig arm
* [...]
* mov eax, 0
* ret
* 
* New arm:
* [...]
* xor eax, eax
* nop
* ret
*/
static char new_arm_payload[] = "\xb9\x00\x00\x02\x00\x8b\x09\x85\xc9\x74\x07"
"\xb8\x01\x00\x00\x00\xeb\x1b\xb9\x00\x00\x02"
"\x00\xc7\x01\x01\x00\x00\x00\x8b\x4c\x24\x04"
"\xba\x04\x00\x02\x00\x89\x0a\x31\xc0\x90\xc3"; /* On gagne 2 bytes */

static char orig_status_payload[] = "\xb8\x00\x00\x02\x00\x8b\x4c\x24\x08\x8b\x00\x89\x01\x85\xc0"
"\x74\x22\x8b\x54\x24\x04\x8b\x4c\x24\x0c\xbb\x04\x00\x02\x00"
"\x8b\x1b\x29\xd3\x89\x19\x8b\x4c\x24\x10\xba\x08\x00\x02\x00"
"\x8b\x12\x89\x11\xeb\x14\x8b\x4c\x24\x0c\xc7\x01\x00\x00\x00"
"\x00\x8b\x4c\x24\x10\xc7\x01\x00\x00\x00\x00\xb8\x00\x00\x00"
"\x00\xc3\xeb\xfe\xeb";

typedef struct new_firmware_descr {
FirmwareDescr * oldDescr;
Elf32_Off start, end;
char * payload;
} NewFirmwareDescr;

NewFirmwareDescr new_firmware_init = { &firmware_init, 0x1c0, 0x1c3, new_init_payload };
NewFirmwareDescr new_firmware_arm = { &firmware_arm, 0x1c4, 0x1ef, new_arm_payload };
NewFirmwareDescr new_firmware_disarm = { &firmware_disarm, 0x1f0, 0x1f9, new_disarm_payload };
NewFirmwareDescr new_firmware_status = { &firmware_status, 0x1fa, 0x249, orig_status_payload };

NewFirmwareDescr * newFirmware[] = {&new_firmware_init, &new_firmware_arm, &new_firmware_disarm, &new_firmware_status};

int main() {
	Elf_obj * elf = elf_open(FILENAME);
	Elf32_Sym * sym;
	int i,j;

	for(sym=elf_firstdsym(elf);sym;sym=elf_nextdsym(elf, sym)) {
		for (i=0;i<nbDescr;i++) {
			if (!strcmp(firmware[i]->symname, elf_dsymname(elf, sym))) {
				firmware[i]->symbolEntry = sym;
			}
		}
	}

#ifdef DISCOVERY
	for (i=0;i<nbDescr;i++) {
		printf("Original payload for %s: \"", firmware[i]->symname);
		for (j=firmware[i]->start; j <= firmware[i]->end; j++) {
			printf("\\x%02x", *((unsigned char*)(elf->maddr) + j));
		}
		printf("\"\n");
	}
#else
	for (i=0;i<nbDescr;i++) {
		if (!strcmp(newFirmware[i]->oldDescr->symname, elf_dsymname(elf, newFirmware[i]->oldDescr->symbolEntry))) {
			printf("%s: Updating symlink from 0x%x to 0x%x\n",
			newFirmware[i]->oldDescr->symname,
			newFirmware[i]->oldDescr->symbolEntry->st_value,
			newFirmware[i]->start);
			newFirmware[i]->oldDescr->symbolEntry->st_value = newFirmware[i]->start;
			printf("%s: Copying new code\n\n", newFirmware[i]->oldDescr->symname);
			memcpy((char*)(elf->maddr) + newFirmware[i]->start, newFirmware[i]->payload, newFirmware[i]->end - newFirmware[i]->start + 1);
		}
	}
#endif

	elf_close(elf);
	return 0;
}

Une autre solution aurait été de démarrer un shell dans n'importe laquelle des fonctions et d'ensuite modifier le fichier image de la bombe à la main.

<< Development Server