NETGEAR, déchiffrer fichier configuration

 druide

NETGEAR


La borne WiFi NETGEAR WNR1000v3 permet la sauvegarde de la configuration dans un fichier. De prime abord, ce fichier n'est pas lisible. C'est un fichier au contenu binaire. Oui, mais...

Binaire

Il existe une multitude de fichiers binaires différents. Par exemple un fichier ELF, un exe etc... mais tous ces fichiers contiennent malgré tout des chaînes de caractères, or, dans ce fichier de configuration, rien... pas la moindre chaîne. J'en déduis intuitivement qu'il s'agit d'un fichier chiffré. Ça semble tout à fait normal d'avoir chiffré ce fichier puisqu'il contient sûrement le mot de passe de connexion à la borne ou encore, le mot de passe du WiFi.

Comment en être sûr et surtout, comment déchiffrer ce fichier ? La piste la plus prometteuse à ce moment-là du projet, c'est le firmware...

NETGEAR Open Source Code for Programmers

Les fournisseurs d'accès Internet tels que Swisscom, UPC et autres, souhaitent fournir des bornes d'accès à l'Internet personnalisées.


Mais comme ils ne sont pas eux-mêmes fabricants de matériel, ils doivent passer des accords avec des entreprises telle que NETGEAR. Ces dernières fournissent des bornes d'accès à l'Internet (hardware) ainsi que le nécessaire pour personnaliser le firmware (software). On peut trouver tout le package pour la borne WNR1000v3 à cette adresse: NETGEAR Open Source Code for Programmers. On obtient un fichier zip WNR1000v3-V1.0.2.68_60.0.93.zip


En fouillant un peu dans tous ces fichiers, peut-être qu'on peut trouver quelque chose d’intéressant. Par exemple, une clé (KEY) de chiffrement utilisée pour les sauvegarde (BACKUP).

druide@druide:~/bcm5356$ grep -R 'BACKUP' *
ap/gpl/zebra/vtysh/vtysh.c: + strlen (CONF_BACKUP_EXT) + 1);
ap/gpl/zebra/vtysh/vtysh.c: strcat (integrate_sav, CONF_BACKUP_EXT);
ap/gpl/zebra/ospfd/ospf_snmp.c:#define OSPFIFBACKUPDESIGNATEDROUTER 14
ap/gpl/zebra/ospfd/ospf_snmp.c: {OSPFIFBACKUPDESIGNATEDROUTER, IPADDRESS, RONLY, ospfIfEntry,
ap/gpl/zebra/ospfd/ospf_snmp.c: case OSPFIFBACKUPDESIGNATEDROUTER: /* 14 */
ap/gpl/zebra/lib/vty.c: fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
ap/gpl/zebra/lib/vty.c: strcat (fullpath_sav, CONF_BACKUP_EXT);
ap/gpl/zebra/lib/command.c: config_file_sav = malloc (strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
ap/gpl/zebra/lib/command.c: strcat (config_file_sav, CONF_BACKUP_EXT);
ap/gpl/zebra/lib/command.h:#define CONF_BACKUP_EXT ".sav"
project/acos/include/ambitCfg_WW_WNR1000v3.h:#define BACKUP_FILE_KEY "NtgrBak"
project/acos/include/ambitCfg_NA_WNR1000v3.h:#define BACKUP_FILE_KEY "NtgrBak"
src/router/config/zconf.tab.c_shipped:#define YYBACKUP(Token, Value) \
src/linux/linux/drivers/char/ftape/compressor/zftape-compress.c:#define ZFT_FAST_SEEK_BACKUP 10 /* segments */
src/linux/linux/drivers/char/ftape/compressor/zftape-compress.c: new_seg -= ZFT_FAST_SEEK_BACKUP;
src/linux/linux/drivers/char/ftape/compressor/zftape-compress.c: new_seg -= ZFT_FAST_SEEK_BACKUP;
src/linux/linux/drivers/usb/uhci.c:#ifdef I_HAVE_BUGGY_APC_BACKUPS

src/linux/linux/drivers/net/bonding.c: slave->state = BOND_STATE_BACKUP;

src/linux/linux/drivers/net/bonding.c: if (mode == BOND_MODE_ACTIVEBACKUP) {
src/linux/linux/drivers/net/bonding.c: (mode == BOND_MODE_ACTIVEBACKUP) ? "backup":"other");
src/linux/linux/drivers/net/bonding.c: if (mode == BOND_MODE_ACTIVEBACKUP) {
src/linux/linux/drivers/net/bonding.c: (mode == BOND_MODE_ACTIVEBACKUP) ? "backup":"other");
src/linux/linux/drivers/net/bonding.c: ? ((mode == BOND_MODE_ACTIVEBACKUP)
src/linux/linux/drivers/net/bonding.c: if (mode == BOND_MODE_ACTIVEBACKUP) {
src/linux/linux/drivers/net/bonding.c: if (mode == BOND_MODE_ACTIVEBACKUP) {

src/linux/linux/drivers/net/bonding.c: slave->state = BOND_STATE_BACKUP;

src/linux/linux/drivers/net/bonding.c: if (mode == BOND_MODE_ACTIVEBACKUP) {

src/linux/linux/drivers/net/bonding.c: slave->state = BOND_STATE_BACKUP;

src/linux/linux/drivers/net/bonding.c: if (mode == BOND_MODE_ACTIVEBACKUP) {
src/linux/linux/drivers/net/bonding.c: case BOND_MODE_ACTIVEBACKUP:
src/linux/linux/drivers/net/bonding.c: if (mode == BOND_MODE_ACTIVEBACKUP) {
src/linux/linux/drivers/net/bonding.c: if (mode == BOND_MODE_ACTIVEBACKUP) {
src/linux/linux/drivers/scsi/README.ibmmca: Andreas Kaiser, "SCSI TAPE BACKUP for OS/2 2.0", Version 2.12, Stuttgart
src/linux/linux/drivers/scsi/advansys.c:#define QHSTA_M_SGBACKUP_ERROR 0x47 /* Scatter-Gather backup error */
src/linux/linux/fs/udf/ecma_167.h:#define FTE_BACKUP 0x00000002
src/linux/linux/fs/udf/osta_udf.h:#define UDF_ID_BACKUP "*UDF Backup"
src/linux/linux/fs/isofs/rock.h:#define TF_BACKUP 16
src/linux/linux/include/linux/if_bonding.h:#define BOND_MODE_ACTIVEBACKUP 1
src/linux/linux/include/linux/if_bonding.h:#define BOND_STATE_BACKUP 1 /* link is backup */
src/linux/linux/include/linux/hfs_fs.h:#define HFS_AFP_BACKUP 0x040 /* backup needed bit (f/d) */


À la ligne 12 on trouve


#define BACKUP_FILE_KEY         "NtgrBak"


Si on ouvre le fichier correspondant, on trouve tout en bas

/* The following definition is to used as the key when doing des
* encryption/decryption of backup file.
* Have to be 7 octects.
*/
#define BACKUP_FILE_KEY "NtgrBak"


Maintenant on sait le chiffrement utilisé ainsi que la clé. Reste plus qu'à déchiffrer... ? Et bien non. Un déchiffrement DES standard ne fonctionne pas. Impossible d'obtenir un texte tangible... Retour sur l'Internet et plus précisément sur exploit-db.

Exploit-db

En fouillant un peu la toile, je tombe sur un article intéressent qui date de 2013. Roberto Paleari a découvert le moyen de contourner l'authentification de cette borne pour obtenir le fichier de configuration. Son intérêt se portait sur le mot de passe de l'admin de la borne. Pour prouver son exploit, il a même écrit un script python déchiffrant le fichier de configuration. Bingo!

La dérivation de la clé

Le déchiffrement DES standard ne fonctionnait pas car, chez NETGEAR, ils ont décidé de modifier la clé de chiffrement à chaque tour. Ils augmentent le premier octet de 8 et lorsqu'il y a débordement, ils incrémentent l'octet suivant de 1. De plus, ils créent une dérivation de clé qui leur est, apparemment, personnelle.

Pour comprendre cela, je pense que l'auteur s'est plongé dans les binaires fournies dans le code source du firmware (voir ci-dessus) et plus précisément dans le binaire « httpd ». En effet, c'est dans ce binaire qu'on trouve la clé "NtgrBak" mais également les appels de fonctions setkey, encrypt ou encore le message d'erreur "can't do des encrypt, the length has to".


Après, comme il s'agit d'une architecture MIPS, il faut se plonger dans l'assembleur correspondant...

Pour ma part, je choisis la solution simple, je vais simplement traduire le code python en code C. Je suis beaucoup plus à l'aise avec le code C...
[collapse collapsed title="Code C"]


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>

#define KEY_SIZE 8

void printHex(const char* title, unsigned char* buf, int len) {
	int i=0;
	printf("%s:\n", title); 
	for(i=0;i<len;i++) {
		if(i > 0 && i % 8 == 0)
			printf("\n");
		printf("%02x", *(buf+i));
	}
    printf("\n");
}

unsigned char extract_by_offset(unsigned char offset, unsigned char* key) {
	unsigned char byte_index = offset >> 3;
	unsigned char bit_index  = byte_index << 3;
	
	unsigned int v0 = (key[byte_index] << 8) | key[byte_index+1];
	unsigned int v1 = 8 - (offset - bit_index);
	v0 = v0 >> v1;
	
	return (v0 & 0xfe);
}

void derive_des_key(unsigned char *key, unsigned char *new_key) {
	int i = 0;
	int j = 0;
	for(i=0;i<7*8;i+=7, j++) {
		new_key[j] = extract_by_offset(i, key);
	}
}

int main(int argc, char** argv)
{
	unsigned char 		*in			= NULL;
	unsigned char 		*out		= NULL;
	FILE 				*fp 		= NULL;
	int					i			= 0;
	int					fsize		= 0;
	DES_cblock 			current_key	= {0x56-8, 0x74, 0x67, 0x72, 0x42, 0x61, 0x6b, 0x00};
	DES_cblock 			key;
	DES_key_schedule 	keysched;


	if(argc != 2) {
		fprintf(stderr, "Vous devez fournir un nom de fichier *.cfg\n");
		exit(EXIT_SUCCESS);
	}

	fp = fopen(argv[1], "r");
	if(fp == NULL) {
		fprintf(stderr, "Impossible d'ouvrir le fichier %s\n", argv[1]);
		exit(EXIT_FAILURE);
	}
	
    fseek(fp, 0, SEEK_END);
    fsize = ftell(fp);
    fseek(fp, 0, SEEK_SET);

	in  = (unsigned char*)malloc(sizeof(unsigned char)*fsize);
	out = (unsigned char*)malloc(sizeof(unsigned char)*fsize);
	if(in == NULL || out == NULL) {
		fprintf(stderr, "Impossible d'allouer la mémoire pour les buffers\n");
		exit(EXIT_FAILURE);
	}

	memset(out, fsize, 0);
	
	fread(in, sizeof(unsigned char), fsize, fp);
	fclose(fp);

    for (i = 0;i < fsize;i+=8) {
		if ((int)(current_key[0] + 8) > 0xff) {
			current_key[1] += 1;		
		}
		current_key[0] += 8;

		derive_des_key(current_key, key);
		
		DES_set_odd_parity(&key);
		if (DES_set_key_checked((C_Block *)key, &keysched)) {
			fprintf(stderr, "Impossible de préparer la clé de déchiffrement\n");
			exit(EXIT_FAILURE);
		}

		DES_ecb_encrypt((DES_cblock *)(in + i),(DES_cblock *)(out + i), &keysched, DES_DECRYPT);
	}

	// skip header
	i = 44;
	for(;i < fsize;i++) {
		char c = *(out + i);
		if(c == 0x00)
			fprintf(stdout, "\n");
		if(c >= 0x20 && c < 0x7f)
			fprintf(stdout, "%c", *(out + i));
	}

    free(in);
    free(out);

    exit(EXIT_SUCCESS);
}

[/collapse]

La compilation se fait simplement


$ gcc -Wall -o des des.c -lcrypto



Reste plus qu'à tester


$ ./des NETGEAR.cfg
wl_radius_port=1812
wlan_acl_dev24=
wan2_dns=
gui_check_enable=1
wlan_acl_dev25=
l2tp_user_passwd=
wla_ssid_2=NETGEAR-Guest
wlan_acl_dev26=
wan_unit=0
wlan_acl_dev27=
wl0_wmf_bss_enable=0
as_genie=0

...

apmode_dns1=0.0.0.0
wl_antdiv=-1
wla_temp_secu_type=WPA2-PSK
wlan_acl_dev23=


Conclusion

On peut légitimement se demander pourquoi la clé de chiffrement se trouve dans les sources du firmware ? En réalité, si elle ne s'y trouvait pas, on aurait pu la trouver dans le binaire :



Le travail aurait simplement pris plus de temps.

Alors, pourquoi fournir le code source ? Comme je l'ai dit plus haut, je pense que c'est pour permettre au FAI de personnaliser l'interface de gestion. Je pense également qu'en dérivant la clé DES « à leur sauce », ils se sentaient protégés contre les déchiffrements sauvages.

En tout cas, merci NETGEAR! Grâce à cela, la correction des examens s'avère maintenant nettement plus simple 😊.

That's all folks

  • 3 years 9 months before
  • |