Salut à tous,

enfin le premier article de ce blog ! Aujourd'hui je vais parler reverse engineering de modem/routeur WiFi (Baudtec RN104VR5H-2T2R-2S).

Afin de vous expliquer le contexte avant d'entrer dans le vif du sujet, on m'a donné un modem/routeur WiFi qui possédait une configuration particulière et il fallait idéalement pouvoir récupérer la configuration en place. Evidemment, pas d'accès à l'interface web ou à telnet/SSH car nous n'avions pas les identifiants de connexion (le matos n'a pas été volé, mais ne nous appartenait pas pour autant :p).

Trouver une porte d'entrée

Premier réflexe, j'ai démonté le boîtier afin de voir s'il ne s'ouvrirait pas à nous via un port série. Bingo ! On retrouve le port série classique (4 broches, Vcc, TX, RX, GND) avec en plus, l'assignation des broches inscrites sur la carte mère. OUF, pas besoin de sortir le multimètre pour faire des mesures, c'est toujours ça de pris.

Je me raccorde au port série, je démarre un screen pour lire/écrire sur la console. Généralement le baudrate de base est souvent 115200 bauds, suffit donc de faire :

screen /dev/ttyUSB 115200

on démarre le routeur et normalement tout s'affiche comme par magie.

Bon c'est bien, on voit le déroulement du démarrage de l'OS qui est un linux minimaliste (kernel + rootfs). Sauf que pas de bol, pas d'accès à un prompt, il demande des identifiants que je n'ai pas. Du coup, impossible de dumper la mémoire flash donc impossible de récupérer une quelconque information. On va quand même regarder du côté du bootloader voir si on peut pas tenter quelque chose.

Hello Bootloader

Au reboot, on peut entrer dans le bootloader en martyrisant la touche ESC. Il semble s'agir d'un bootloader custom conçu par Realtek. Grace à la commande 'help', on peut retrouver plusieurs commandes donc une particulièrement utile : 'd' pour 'dump'.

Cette commande, elle va nous permettre de dumper une zone mémoire à partir de l'adresse donnée. Seul souci, on ne sait pas où démarrent les données.

Cette fois-ci, merci Realtek, il y a une commande 'info' qui affiche l'adresse de début de la mémoire flash (où sont donc stockés le bootloader, le noyau, le rootfs et d'autres choses). Voici un exemple :

<RTL867X>info
(c)Copyright Realtek, Inc. 2013
Project RTL8676/RTL8685 LOADER (LZMA)
Version 00.01.10rc(rtl8685) (Oct 15 2014 17:08:17)

<RTL867X>BootLine: file
MAC Address [0]: 00:13:33:XX:XX:XX
Entry Point: 0x80000000
Load Address: 0x80000000
Application Address: 0xBD040000
HS Offset: 0x00006000
CS Offset: 0x00020000
Flash Size: 16M
Memory Configuration: ROW:2K COL:4K Bank:2Banks
MII Selection: 0 (0: Int. PHY   1: Ext. PHY)
UART is enabled

2 adresses intéressantes ressortent de ce résultat :

  • 0x80000000 : Le point d'entrée
  • 0xBD040000 : Le début de la mémoire flash

Ici le point d'entré ne m'intéresse pas, d'ailleurs, en voulant dumper quelques octets à partir de cette adresse, je me suis fait éjecter du bootloader. J'ai donc utilisé l'autre adresse de départ à 0xBD0400000.

Le problème, c'est que nous ne savons pas comment est mappée la mémoire flash. Heureusement, au démarrage du noyau, il indique lui-même la façon dont il a mappé la mémoire flash. Ici nous avons :

Creating 4 MTD partitions on "Physically mapped flash":
0x000000000000-0x000000020000 : "boot"
0x000000020000-0x000000030000 : "CS"
0x000000030000-0x000000040000 : "config"
0x000000040000-0x000000800000 : "rootfs"

On a donc une mémoire flash de 8Mo divisée en 4 "partitions" :

  • Le bootloader --> "boot"
  • Une bizarrerie (2 ELF LZMA propre à Realtek) --> "CS"
  • La configuration importée au boot --> "config"
  • L'OS (kernel + rootfs) --> "rootfs"

Bon je vous ai spoilé l'utilité de la partition "config" mais on verra comment ça fonctionne un peu après. Première chose que j'ai faite, c'est dumper la partition "rootfs" depuis le bootloader avec la fameuse commande 'd'. Tout à l'heure je parlais de l'adresse de départ, il s'agit d'une adresse relative à laquelle nous allons ajouter l'offset de démarrage de la partition que nous voulons dumper, à savoir 0x40000. Ce qui fait :

d 0xBD0440000 0x7C0000

Pourquoi 0x7C0000 ? Simplement parce que l'adresse de fin de la partition "rootfs" est 0x800000. Pour avoir sa taille, il suffit de "prendre l'écart" entre le début et la fin, soit :

0x800000 - 0x40000 = 0x7C0000

Ok c'est bien mais ça m'affiche le résultat du dump à la hd et c'est pas terrible. La seule solution que j'ai trouvée, c'est de logguer l'output de screen dans un fichier, je vous invite à regarder l'option '-L' du manuel de screen. Soyez patients, j'en ai eu pour plus de 2 heures. Comme je disais, ça produit une sortie au format de hexdump, donc pour reconstituer le rootfs, pas d'autre choix que d'extraire les 16 octets par ligne à coup de cut afin de les réassembler au final.

Etape 1 : Vue dans le log screen

00000000 68 73 71 73 f6 02 00 00 8d 52 51 54 00 00 02 00 hsqs.....RQT....
00000010 3b 00 00 00 02 00 11 00 e0 00 03 00 04 00 00 00 ;...............
00000020 28 1e 23 0d 00 00 00 00 57 38 42 00 00 00 00 00 (.#.....W8B.....
00000030 4f 38 42 00 00 00 00 00 ff ff ff ff ff ff ff ff O8B.............
00000040 21 04 42 00 00 00 00 00 80 19 42 00 00 00 00 00 !.B.......B.....

Etape 2 : Vue après nettoyage de l'output hexdump

68 73 71 73 F6 02 00 00 8D 52 51 54 00 00 02 00
3B 00 00 00 02 00 11 00 E0 00 03 00 04 00 00 00
28 1E 23 0D 00 00 00 00 57 38 42 00 00 00 00 00
4F 38 42 00 00 00 00 00 FF FF FF FF FF FF FF FF
21 04 42 00 00 00 00 00 80 19 42 00 00 00 00 00

Etape 3 : Reconstruction dans HxD (Windows) en copiant simplement la suite d'octet dedans.

Une fois que le fichier était réassemblé, j'avais donc un "rootfs" entre les mains.

Analyse

On se lance dans la quête de l'analyse du bloc "rootfs". Comme dit plus haut, il contient au moins le kernel et le système de fichier. Pour les manipulations suivantes, j'ai utilisé 'binwalk' qui est très performant et permet d'extraire depuis un fichier les parties qu'il reconnaît comme pouvant être extraites. Il n'y a pas de magie derrière, il recherche simplement des signatures connues et s'il en trouve une, il applique la fonction d'extraction adéquate.

Bref, un coup de binwalk et hop, il me sort le système de fichier décompressé (on y retrouve /etc, /bin, /var, etc...). Et c'est à ce moment que j'ai failli perdre tout espoir. Absolument rien dans les fichiers de conf, que du générique comme si le modem venait d'être reset. Même pas de fichier contenant le mot de passe du compte root (où es-tu /etc/passwd ?). En réalité, il y a bien un fichier /etc/passwd mais c'est un lien symbolique pointant sur /var/passwd qui n'existe évidemment pas.

J'ai donc cherché à trouver le fichier /etc/passwd|/var/passwd. L'idée était de scruter les binaires dans /bin voir s'il y en avait un qui faisait référence à un de ces fichiers. Heureusement, au moins 2 sont ressortis :

  • /bin/login
  • /bin/configd

Je dégaine IDA pour regarder les entrailles de /bin/login. Déception... ce n'est rien de plus que le login basique que l'on retrouve un peu partout. On entre un login, un mot de passe, le soft s'occupe de déchiffrer le mot de passe entré, et compare dans sa base de données (à savoir /etc/passwd) si le mot de passe chiffré correspond à l'entrée de l'utilisateur.

J'abandonne /bin/login pour passer à /bin/configd. Je commence à comprendre petit à petit comment ça fonctionne. En gros, /bin/configd va manger un fichier XML stocké dans /tmp/config.xml décrivant la configuration système à appliquer (PPP, WiFi, Firewall, users, etc...) et ce fichier, il est enregistré dans la partition nommée "config" que je vous ai montré plus tôt.

Comment je sais ça ? Eh bien en parcourant un peu les références vers la partition "config" ou plutôt son bloc MTD attribué par le noyau, à savoir 'mtd2'. Même principe que pour le "rootfs", j'ai donc dumpé la partition "config" et je l'ai recontruite pour pouvoir extraire le ficher XML.

TOC TOC

La partition contient des données textes brutes, ce n'est donc pas compliqué de lire le fichier XML contenu dedans. Mais si on essayait de comprendre un peu plus le format de cette partition "config" ? En effet, elle démarre bizarrement :

00000000  ca fe 23 45 00 59 26 d9  00 00 00 09 00 00 00 10  |..#E.Y&.........|
00000010  2e 66 6c 61 74 66 73 64  00 00 00 00 00 00 81 a4  |.flatfsd........|
00000020  74 69 6d 65 20 31 34 34  36 37 34 36 38 39 34 0a  |time 1446746894.|
00000030  00 00 00 06 00 00 00 16  68 6f 73 74 73 00 00 00  |........hosts...|
00000040  00 00 81 a4 31 39 32 2e  31 36 38 2e 31 2e 31 09  |....192.168.1.1.|
00000050  64 73 6c 64 65 76 69 63  65 0a 00 00 00 00 00 0f  |dsldevice.......|
00000060  00 00 e8 b0 6f 6c 64 73  65 74 74 69 6e 67 2e 78  |....oldsetting.x|
00000070  6d 6c 00 00 00 00 81 a4  3c 43 6f 6e 66 69 67 5f  |ml......<Config_|
00000080  49 6e 66 6f 72 6d 61 74  69 6f 6e 5f 46 69 6c 65  |Information_File|
00000090  5f 38 36 37 31 3e 0a 3c  56 61 6c 75 65 20 4e 61  |_8671>.<Value Na|

En réalité, la partition "config" utilise un système de fichier appelé "flatfs" dans sa version 1 et dans son header, on peut retrouver un certain nombre de fichiers qui seront copiés de la partition "config" dans le rootfs (via /tmp). Il y a un démon nommé 'flatfsd' qui va se charger de lire le bloc MTD correspondant à la partition "config" :

Vue de flatfsd dans IDA

Je vous avoue que j'ai bien gratté dans IDA pour déduire le fonctionnement de ce système de fichier avant de me dire que c'était peut-être un système de fichier opensource (et c'en est un) : flatfs1.

En parsant le fichier XML, on retrouve ce que j'expliquais juste avant, à savoir les différentes configurations LAN, WAN, PPP, FW, etc... ainsi que la configuration utilisateur avec le mot de passe en clair ! Une fois tout ça en main, j'avais accès à l'interface web et console et je pouvais donc voir la configuration de manière plus 'user-friendly' qu'un XML.

Pour conclure rapidement, la cible n'était pas très complexe. Dès le début j'avais les clefs en main pour me connecter au port série, dumper les données qui m'intéressaient, qui plus est, étaient en clair. Voilà, c'est terminé pour ce premier article qui j'espère vous aura plu.

A+ !