Cet article détaille l'architecture d'une nouvelle implémentation de mon outil de fuzzing « Fusil » : un système multi-agents qui devrait éviter la lourdeur des applications monolithiques.

Système multi-agents

Pour avoir mis les doigts dans de grosses applications (plus de 10.000 lignes de codes), je peux dire qu'une application monolithique est difficile à déboguer et à faire évoluer. L'architecture d'un logiciel repose sur un graphe d'objets liés les uns et autres à la sauce spaghetti. Modifier une classe ou le prototype d'une fonction impacte des dizaines d'autres classes et fonctions. Bien que des outils de refactoring facilitent la répercution des changements sur l'ensemble du projet, une architecture plus modulaire nous délesterait de cette tâche ingrate.

Un système multi-agents (SMA) est une simulation qui implique des dizaines d'agents simples qui communiquent entre eux par des événements. Le gros avantage est qu'un agent est (quasiment) aveugle : sa vision de l'environnement est volontairement limitée. En particulier, un agent n'a pas accès aux données des autres agents. Ce concept issu de la recherche en intelligence artificielle peut être adapté à des programmes plus communs. Comme j'ai repris mon projet Fusil depuis zéro (pour la 3e fois), j'ai décidé d'implémenter un SMA simple en Python pour mes besoins.

Agents dans Fusil

Pour rappel, Fusil est un générateur d'erreurs servant à rechercher des bugs dans un logiciel (Fusil est un fuzzer). Dans Fusil, tout est agent. Lorsqu'un agent émet un événement, ce dernier est mis en attente dans le MTA (Mail Transfer Agent). L'événement n'est transmis aux destinataires qu'au cycle de simulation suivant. L'agent Project est responsable d'exécuter la simulation : il demande à chaque agent de lire sa boîte de messages puis d'exécuter leur méthode live(). MTA et Project sont eux-même des agent et peuvent donc émettre et recevoir des événements. Exemples d'événements : « session_start », « process_create », etc.

Les agents de type Session sont crées pendant une session et sont automatiquement détruits à la fin d'une session. Les agents de type Project sont conservés toute la durée de vie du projet. Par contre, ils sont désactivés quand aucune session n'est active : ils ne peuvent plus communiquer (ni émettre ni recevoir de messages) et leur méthode live() n'est plus appelée. Enfin, lorsque le projet est fermé, les agents Project sont détruits. Ce découpage (Project/Session) permet de gérer simplement la durée de vie des données. En particulier, on s'assure que les données sont détruites au bon moment.

Exemples d'agent

  • CreateProcess : lance un programme. Tue le processus en cas de timeout (10 secondes par défaut).
  • WatchProcess: surveille un processus et en particulier sa mort (le processus a quitté ou a été tué par un signal fatal)
  • Syslog : surveille les logs systèmes (/var/log/message et /var/log/syslog)
  • TimeWatch : surveille la durée d'une session
  • MangleFile : injecte des erreurs dans un fichier

Système de notation

Les agents de surveillance attribuent une note à la session entre -100% et +100%. La note globale est la somme de toutes les notes. Si elle dépasse un seuil (+50% par défaut), la session est considérée comme un succès et est stoppée. Certains agents peuvent demander explicitement la fin d'une session : après un timeout ou la mort du processus par exemple.

Projets de fuzzing

Un projet Fusil est une configuration pour fuzzer une application. Une douzaine de projets sont disponible : ClamAV, printf, MySQL, rpm, etc. Les projets sont beaucoup plus diversifiés que les exemples des précédentes implémentations de Fusil. La nouvelle architecture plus modulaire et générique permet de configurer très finement chaque composant (agent).

Le moteur du SMA de Fusil est encore en gros chantier. Il risque encore de beaucoup évoluer ces prochaines semaines pour s'adapter au mieux aux besoins du logiciel. Note : Fusil s'autorise de multiple digressions par rapport au SMA parfait pour simplifier la vie du programmeur.