Ce post est le résultat de mes recherches et de ma démarche sur l’analyse d’un routeur domestique.

TL-WR841N

Je me suis enfin décidé à acheter un routeur Wifi et de le décortiquer. J’ai opté pour un routeur sans fil TP-LINK (TL-WR841N)

C’est un routeur pas très cher (18€ sur Amazon) et il supporte OpenWRT/LEDE des OS alternatifs pour ajouter de nouvelles fonctionnalités.

Firmware

En attendant la livraison de mon nouveau jouet je me suis renseigné sur la partie logicielle de ce routeur.

J’ai donc téléchargé le firmware disponible sur le site de TP-Link. Le fichier compressé au format ZIP contient 3 fichiers :

Celui qui nous intéresse est bien évidement le fichier TL-WR841Nv14_EU_0.9.1_4.16_up_boot[180319-rel57291].bin qu’on renommera en firmware.bin.

Pour voir ce que l’on peut identifier dans le firmware, on peut utiliser binwalk.

Le firmware est divisé en 3 parties :

  • U-Boot : le bootloader qui va démarrer en premier et charger le kernel
  • Le noyau Linux : compressé au format LZMA
  • SquashFS : le système de fichiers compressé sous Linux

C’est à peu près tout ce qu’il faut pour faire tourner un OS embarqué sur un routeur.

U-Boot

Pour commencer je vais extraire U-Boot du firmware grâce à l’utilitaire dd.

Pour vérifier que ce n’est pas corrompu on va regarder les chaines de caractères avec l’utilitaire strings :

La première chaine correspond à la version de U-Boot et sa date de compilation. Donc le fichier n’est pas corrompu. Pour ensuite le charger dans IDA Pro, il va nous falloir l’adresse de chargement du bootloader (Load address). Sur l’image ci-dessus on a deux adresses : 0x80200000 et 0x80100000. Si on Google l’une des deux on tombe sur la doc de U-Boot :

4. The default load and start addresses of the applications are as
   follows:
			Load address	Start address
	x86		0x00040000	0x00040000
	PowerPC		0x00040000	0x00040004
	ARM		0x0c100000	0x0c100000
	MIPS		0x80200000	0x80200000
	Blackfin	0x00001000	0x00001000
	NDS32		0x00300000	0x00300000
	Nios II		0x02000000	0x02000000

Nous avons notre adresse : 0x80200000. Plus qu’a rebaser le programme et le tour est joué.

Il ne reste plus qu’à le reverser. Mais comme le code de U-Boot est sous license GPL, pas besoin puisque TP-Link fourni déjà le code source.

Linux kernel

Place au kernel, tout comme U-Boot on va l’extraire avec dd. Comme on le voit avec l’image ci-dessous le fichier LZMA est corrompu lors de l’extraction, on ne peut donc pas y extraire le kernel :(.

Heureusement, comme le bootloader, le noyau Linux est sous licence GPL et il est donc obligatoire pour TP-Link de publier les sources du noyau Linux qu’il utilisent si il y a eu modification. Ceci étant le cas on peut directement accéder aux sources du noyau Linux et tous les autres logiciels utilisé avec ce routeur.

SquashFS

Comme dit plus haut, SquashFS est un système de fichier compressé pour le noyau Linux. Il est en lecture seule et ne permet pas d’écrire dessus. Ce système est principalement utilisé pour les OS embarqués comme OpenWRT. De plus il est aussi installé sur les version Live de la plupart des distributions Linux.

On va pouvoir l’extraire du firmware comme le bootloader et le kernel.

Ci-dessus je l’utilise unsquashfs pour décompresser le fichier. On retrouve l’arbre de fichiers du routeur.

Un coup de ls et on peut voir que /bin/ls (comme tous les autres exécutables des FS) est un lien symbolique vers /bin/busybox

λ ~/Desktop/fw/squashfs-root » ls -la bin/ls 
lrwxrwxrwx 1 mathieu mathieu 7 May 12 22:21 bin/ls -> busybox

Busybox est considéré comme le couteau suisse des systèmes Linux embarqué car un seul binaire contient la plupart des utilitaires GNU généralement disponibles sur les distributions Linux.

De plus avec la commande file on peut déterminer l’architecture du processeur utilisé par le routeur :

λ » file bin/busybox 
bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

Hardware

J’ai reçu mon routeur. La première chose que j’ai faite était d’ouvrir le boitier pour voir ce qu’il y a dedans.

Connexion série

Comme attendu il y a les PINs UART pour ouvrir une connexion série sur le routeur. Il est temps de souder. J’ai soudé 3 fils sur les PINs à disposition :

  • bleu : GND (j’avais pas de fil noir)
  • blanc : TX
  • vert : RX

A l’aide d’un convertisseur USB vers TTL on peut donc ouvrir une sortie série sur le routeur et obtenir un shell.

Voici le setup :

On peut lancer PuTTY et avec le baudrate à 115200, puis démarrer le routeur et on voit le bootlog défiler. Comme prévu U-Boot démarre puis il va charger le noyau Linux.

D’après les logs on peut voir que c’est un noyau Linux 2.6.36. On aurait aussi pu voir ça dans /proc/sys/kernel :

/proc/sys/kernel # cat osrelease
2.6.36

Dès que le routeur a entièrement démarré, il suffit d’appuyer sur la touche entrée, et nous avons un shell. Contrairement à d’autres routeurs, celui-ci n’a pas d’identifiant ni de mot de passe.

LEDs

Ce routeur comporte 3 LEDs. Mais d’après le code source du kernel, certains routeurs TP-Links peuvent en avoir plus que 3 en fonction des fonctionnalité de ceux-ci.

Dans /proc/tplink on peut voir à quoi ces LEDs correspondent à l’aide des fichiers suivants :

  • led_internet : statut de la connexion internet
  • led_wlan_24G : statut de la connexion sans fil
  • led_sys : statut du système du routeur

Il y a aussi le fichier let_status qui permet d’activer ou désactiver les LEDs. Pour changer le comportement d’une LED il suffit d’éditer le fichier en question. Exemple : echo 0 > led_internet.

Voici le code dans le noyau Linux qui va lire et écrire dans le fichier /proc/tplink/led_internet pour changer le statut de la LED en question :

static int led_internet_read_proc(char *page, char **start, off_t off,
                                  int count, int *eof, void *data)
{
	return count;
}
static int led_internet_write_proc(struct file *file, const char *buffer,
                                   unsigned long count, void *data)
{
	char val_string[16];
	int val, val1;

	if (count > sizeof(val_string) - 1)
			return -EINVAL;
	if (copy_from_user(val_string, buffer, count))
		return -EFAULT;

	if (sscanf(val_string, "%d %d", &val, &val1) != 2)
	{
		printk("usage: <action> <unused>\n");
		return count;
	}

	wan_status = val;
	if (val && ((NULL != fp_ethTestWan) && (1 == fp_ethTestWan()))) {
		led_setWanOn(1);
	} else {
		if ((NULL != fp_ethTestWan) && (1 == fp_ethTestWan())) {
			led_setWanOff(1);
		} else {
			led_setWanOff(0);
		}
	}

	return count;
}

C’est à peu près le même code pour les autres LEDs.


C’est tout pour le moment. Pour la suite j’aimerais mettre en place l’environnement de développement pour créer un nouveau firmware pour cet appareil et tester dans QEMU et pourquoi pas trouver et exploiter quelques bugs à distance. Les routeurs domestiques sont bourrés de bugs et les applicatifs ne sont pas maintenu très longtemps, il est donc probable qu’on puisse y trouver quelque chose d’interessant.

Si vous avez besoin d’infos ou souhaitez corriger des erreurs, vous pouvez me ping sur Twitter : @matteyeux

Github : matteyeux


Sources :