Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
zipdefrag — Présenté à Recon Montréal 2018 | Kitploit
Outils/GitHubGitHub/nccgroup/zipdefrag
Sécurité des Systèmes EmbarquésCriminalistique MémoireRétro-ingénierieRécupération de DonnéesCriminalistique NumériqueAnalyse de Micrologiciel
GitHubnccgroup/zipdefrag

zipdefrag

Présenté à Recon Montréal 2018

Voir le dépôt
7422il y a 8 ansPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Ce Dump Est Un Puzzle

ou Analyse Avancée au Fusil à Pompe pour Imbéciles et Rebelles

il était une fois

Il était une fois, cher lecteur, par une nuit sombre et orageuse, votre fidèle auteur est tombé sur une situation perplexe et mystérieuse — un système où la seule chose empêchant l'analyse mémoire chip-off et le reversing était un système de fichiers propriétaire fragmenté inconnu, combiné à de la compression due à l'utilisation de Java embarqué, réduisant l'efficacité des outils existants de carving de fichiers.

Une solution ad hoc avait peut-être été bricolée à l'époque en concaténant manuellement les blocs qui semblaient s'assembler, avec un travail de console Python horrible et d'affreux scripts bash ad hoc. C'était assez bon, mais très chronophage.

Si extraire des données simples non compressées lors d'une analyse chip-off est un travail assez banal dans la journée d'un hacker matériel, la compression pose de sérieux problèmes lorsque ses morceaux sont éparpillés un peu partout, de manière irrationnelle et désagréable, même lorsqu'aucune autre protection réelle n'est en place contre l'extraction.

Mais il doit sûrement y avoir une meilleure solution ?

Il y a des choses intéressantes à propos des fichiers Zip en particulier (qui est le format de base utilisé pour les fichiers JAR). En tant qu'étudiant assidu du International Journal of PoC||GTFO depuis pas mal de temps maintenant, et en suivant particulièrement les travaux sur les acrobaties de formats de fichiers d'Ange Albertini, j'ai estimé qu'il pourrait y avoir assez de données à l'intérieur d'un fichier zip à propos du fichier zip lui-même pour pouvoir faire un travail correct de reconstruction.

Bon, maintenant que les références sont en place, passons aux détails techniques.

Tout d'abord, nous ne connaissons peut-être pas les spécificités du système de fichiers (et du point de vue de ma recherche, indépendamment du système sur lequel j'avais rencontré le problème, j'ai décidé qu'il était simplement préférable de ne pas s'en soucier). Mais nous savons une chose ou deux sur la façon dont la plupart des systèmes de fichiers sont implémentés. En particulier, nous savons qu'ils ont tendance à être écrits par blocs. Ces blocs ont une taille minimale, appelée pages, et nous pouvons identifier cette taille de page en parcourant le dump et en repérant la taille de bloc minimale écrite.

Certains de ces blocs peuvent être contigus, d'autres non, sans schéma clair pour savoir quand les blocs sont contigus.

Tout cela pour dire que le problème qui se pose est de réordonner les pages de données de manière à obtenir des images valides (ou suffisamment proches) des fichiers que nous voulons extraire.

Les fichiers Zip sont écrits de manière à implémenter une sorte de hiérarchie inversée. D'abord les données de fichiers compressées (enveloppées dans des en-têtes de fichier locaux qui les décrivent). Ensuite un répertoire central (qui liste les offsets des en-têtes de fichier locaux), puis un enregistrement de fin de répertoire central (qui décrit, entre autres, le nombre de fichiers stockés dans le zip, l'offset où commence le répertoire central et la taille du répertoire central).

Prenons cela à l'envers, en creusant un peu plus les détails :

  • La fin du répertoire central (EOCD) nous indique :

    • L'emplacement exact, dans le fichier Zip, de l'enregistrement EOCD (l'offset du CD, plus la longueur du CD, qui précède l'EOCD)
    • Combien de fichiers (et donc d'enregistrements CD) chercher.
    • L'emplacement précis dans le fichier Zip du premier enregistrement CD.
  • Chaque enregistrement du répertoire central nous indique :

    • Le CRC32 des données de fichier compressées
    • L'horodatage
    • Beaucoup d'autres métadonnées (méthode de compression, drapeaux, version de l'OS utilisée/nécessaire...)
    • Un index dans le fichier pour le bloc LF correspondant
    • Point crucial : suffisamment de données pour reconstruire une image du bloc LF correspondant.
  • Chaque enregistrement de fichier local nous indique :

    • L'emplacement dans notre dump du début d'un fichier
    • Si le fichier est assez petit, on obtient tout le fichier dans la même page, ou grâce à l'en-tête de fichier suivant apparaissant dans le prochain bloc paginé du fichier zip !
    • si assez de petits fichiers sont regroupés dans assez de pages, on peut utiliser l'emplacement des pages et les valeurs connues du répertoire pour créer un ordre pour les pages (avec des trous connus !)

Tout ce qui précède nous permet d'avoir reconstruit la grande majorité du fichier.

Retournement de situation — il faut réellement gérer plus d'un firmware JAR !

Tout d'abord, nous avons besoin d'une étape pour distinguer les données des différents firmwares. La raison est que tous les offsets ne sont pertinents qu'au sein de leurs fichiers zip respectifs — tout conflit mènera à des flux zip incompatibles et à de la corruption, et nous voulons absolument récupérer le plus de données non corrompues possible. Nous voulons aussi de bonnes garanties que, par exemple, les éventuelles vulnérabilités que nous diagnostiquons dans le firmware cible affectent bien celui que nous voyons normalement s'exécuter, et pas un autre fichier qui traîne simplement quelque part.

La solution nécessaire ici est l'algorithme kmeans (alias « algorithme de Lloyd »). Il y a une excellente vidéo ici qui explique son fonctionnement. SciPy avait une bonne version prête à l'emploi, mais j'ai dû identifier/ patcher le seul crate de clustering/analyse implémentant l'algorithme pour que cela fonctionne avec l'implémentation Rust. Heureusement, je n'ai pas eu à l'écrire de zéro.

Après ça, le travail est bien engagé.

Nous pouvons utiliser plusieurs caractéristiques pour cela. Les champs Flags, Method et Version varient tous selon la pile Zip utilisée pour compresser le fichier. De plus, les en-têtes contiennent des horodatages, et il est généralement peu probable que tous les firmwares aient été compilés et compressés exactement au même moment.

Soit dit en passant, il convient de noter que les fichiers Zip utilisent des horodatages au format MS-DOS, qui sont des entiers courts compressés en bits représentant année-mois-jour et heure-minute-deux-secondes. Si ceux-ci n'étaient pas convertis en une valeur scalaire absolue avant d'être utilisés comme données de classification, vous pourriez tout aussi bien accorder autant de poids à une différence d'année qu'à une différence d'une seconde, et ce n'est pas bien du tout !

Nous convertissons ceux-ci en un vecteur euclidien (ce qui est un mot savant pour désigner un tableau à n dimensions de valeurs dans ℝ, ou de coordonnées flottantes, mais la vidéo liée ci-dessus est probablement l'explication la plus simple) et l'algorithme de clustering fait à peu près tout le reste pour nous, rassemblant tous les en-têtes analysés dans le nombre de buckets attendu.

Une petite remarque sur le parsing

Bien que j'aie écrit cela dans un script Python assez bancal pour prototyper cette méthode, étant parvenu à un taux de récupération du contenu JAR d'environ 70-80 % avec la PoC, j'ai décidé de m'arrêter là et de passer à l'implémentation d'une version rapide en Rust.

Rust a un crate appelé nom qui est absolument fantastique pour écrire des parseurs-vérificateurs. C'était l'une des principales raisons de le réécrire en Rust, pour ce que ça vaut. La possibilité d'écrire des parseurs clairs et extrêmement stricts rend cela bien plus facile, à certains égards, que d'essayer de tout gérer en Python (qui a tendance à être beaucoup plus tolérant, à tel point qu'il est parfois un peu difficile d'être certain de ne pas négliger des échecs erronés et de rater un cas limite.

Télécharger l’outil