0% ont trouvé ce document utile (0 vote)
181 vues22 pages

Rapport Final

Transféré par

messaoudtoto19
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
181 vues22 pages

Rapport Final

Transféré par

messaoudtoto19
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Sécurité des logiciels Université Laval

GLO-4009/GLO-7009 2024/2025

__________________________________________________________________________________________

Titre du Projet
Création d'un outil de détection et correction de Buffer Overflows
ARGOS
__________________________________________________________________________________________

Présenté à

Djedjiga Mouheb

Nom Prénom Matricule Programme

Messaoud Taieb 537 008 452 Génie informatique

Lamlih Houssam 536 768 426 Maîtrise en informatique

1
Résumé

Ce rapport illustre la conception et le développement d'un outil d'analyse statique innovant


pour la détection des buffer overflows dans les applications en C. Développé en Rust et
s'appuyant sur le parseur Clang, cet outil effectue une analyse syntaxique approfondie pour
repérer les utilisations potentiellement dangereuses des fonctions critiques. L'outil évalue les
risques en examinant les tailles de buffers et prévient les débordements de tampon,
augmentant ainsi la sécurité des applications C. Le document fournit une analyse complète de
la conception, de l'implémentation et du fonctionnement de l'outil, mettant en évidence son
architecture, ses techniques d'analyse, et son apport à la sûreté des applications en C.

2
TABLE DES MATIÈRES

INTRODUCTION 4
Pourquoi la détection est-elle essentielle ? 4
Approches de solution 5
Raisons d'utilisation de Rust comme langage 5
A) Fond Théorique et Travaux Connexes 6
Définition et exemples des buffers overflows 6
1) Conception Globale 7
1.1) Intégration avec Clang 7
1.2) Étapes d'Identification 8
1.3) Analyse des Appels de Fonctions 10
Répartition des tâches 11
Travaux à compléter 12
B) Fond Pratique et Travaux Connexes 14
Mise en oeuvre d’ARGOS 14
Méthodologie et Présentation des résultats 15
1) Analyse Statique 15
2) Analyse Dynamique 16
Limitations et Propositions pour les Améliorations Futures 19
CONCLUSION 21
BIBLIOGRAPHIE 22

3
INTRODUCTION

Les buffer overflows sont une préoccupation majeure en sécurité informatique, notamment dans le
développement en C, où ils peuvent conduire à l'exécution de code arbitraire, la compromission de
systèmes et l'accès à des données sensibles. Un buffer overflow survient lorsqu'un programme écrit
des données au-delà des limites d'un tampon alloué, risquant de corrompre des données vitales ou
de modifier le flux d'exécution du programme. Ce rapport explique pourquoi la détection précoce de
ces vulnérabilités est cruciale pour la prévention d'exploits malveillants et pour la stabilité et la
performance des applications.

Pourquoi la détection est-elle essentielle ?

Sécurité : Les buffer overflows représentent des vulnérabilités de sécurité parmi les plus
communes et les plus graves. Leur détection précoce est essentielle pour empêcher l'exécution de
code malveillant et la prise de contrôle des systèmes compromis.

Stabilité : Ces vulnérabilités peuvent entraîner des comportements imprévisibles, des plantages et
des corruptions de données, menaçant la stabilité des applications.

Performance : Identifier et résoudre ces failles contribue également à l'optimisation de la gestion


de la mémoire et à l'amélioration de la performance des applications.

Les défis associés à la détection automatique des buffer overflows sont nombreux, notamment la
diversité des modèles d'utilisation et la nécessité d'une analyse à la fois rapide et précise. L'outil que
nous décrivons emploie des méthodes d'analyse avancées pour scruter le code source, avec une
attention particulière portée aux fonctions couramment liées aux vulnérabilités de buffer overflow et
en examinant leur utilisation des buffers.

Exemples de défis dans la détection

La détection des buffer overflows est particulièrement épineuse dans le développement en C,


confrontée à des défis distincts :

Hétérogénéité des patterns d’utilisation : Les tampons en C sont manipulés de multiples façons,
et identifier les schémas potentiellement vulnérables requiert une compréhension approfondie des
différentes pratiques de codage. Chaque programmeur peut interagir avec les tampons d'une
manière qui lui est propre, rendant la détection des séquences de code dangereuses loin d'être
triviale.

4
Performance de l’analyse : Pour s'intégrer efficacement au cycle de développement logiciel,
l'analyse statique se doit d'être à la fois rapide et précise. Elle ne doit pas ralentir le processus de
développement en ajoutant un surcoût temporel. Cependant, atteindre cet équilibre entre rapidité et
précision est complexe, car une analyse minutieuse tend à nécessiter plus de temps.

Ces défis soulignent l'importance de développer des outils d'analyse qui non seulement
comprennent les subtilités du langage C mais qui sont également optimisés pour s'adapter aux
contraintes de temps du développement moderne. L'outil que nous développons cherche à
surmonter ces obstacles en utilisant des algorithmes avancés et une intégration profonde avec des
systèmes d'analyse de code existants.

Approches de solution

Pour relever ces défis, nous avons conçu un outil d'analyse statique qui utilise une méthodologie
d'examen du code source à la fois rigoureuse et innovante. Concentrant notre attention sur les
fonctions fréquemment impliquées dans les vulnérabilités de buffer overflow telles que scanf,
strcpy, et strcat, nous exploitons le parseur Clang pour créer un arbre syntaxique abstrait. Ce
procédé nous permet d'analyser avec précision le flux de données et la dimension des buffers, et
donc de détecter les risques de buffer overflow de manière proactive. Ainsi, notre approche vise à
renforcer significativement la sécurité des applications écrites en C, tout en respectant les
contraintes de performance du développement logiciel moderne.

Raisons d'utilisation de Rust comme langage

L'adoption de Rust pour concevoir notre outil d'analyse statique spécialisé dans la détection des
buffer overflows dans le code C repose sur des atouts distincts de ce langage :

​ Sécurité de la mémoire : Au cœur de Rust se trouve l'engagement envers la sécurité de la


mémoire, une qualité indispensable pour les outils de sécurité. Cette particularité réduit
considérablement le risque d'introduire les mêmes vulnérabilités que celles qu'il est supposé
repérer, conférant à notre outil une base solide et fiable.

​ Performances optimales : Rust offre une performance comparable à celle de C/C++, un atout
majeur pour un outil d'analyse statique devant traiter efficacement de larges bases de code. Sa
gestion minutieuse de la mémoire et son contrôle à bas niveau (low-level control) permettent à
Rust de mener des analyses complexes sans nuire à la vitesse d'exécution. Ce compromis entre
performance et précision s'assure que l'intégration de l'outil dans le cycle de développement
logiciel ne cause pas de ralentissements significatifs.

​ Système de types avancé : Rust se caractérise par un système de types riche et expressif,
contribuant à la création d'abstractions solides et sécurisées. Cela est particulièrement avantageux
pour modéliser des structures de données complexes telles que les arbres syntaxiques abstraits
(AST), qui sont essentiels dans l'analyse de code source.

5
​ De plus, le système de types de Rust aide à prévenir les erreurs de logique dans le développement
de l'outil, renforçant la fiabilité de l'analyse.

​ Écosystème moderne : Rust profite également d'un écosystème contemporain avec Cargo, son
système intégré de gestion de paquets et de build, qui simplifie l'intégration de bibliothèques
tierces telles que Clang. Cette intégration permet d'exploiter pleinement les fonctionnalités
avancées des bibliothèques tout en conservant une gestion aisée des dépendances et en assurant la
reproductibilité des constructions logicielles.

​ Ces caractéristiques font de Rust le choix idéal pour le développement d'outils d'analyse de sécurité
qui sont non seulement efficaces mais également intégrables dans les pratiques de développement
modernes sans compromis sur la qualité ou la performance.

A) Fond Théorique et Travaux Connexes
Cette section approfondit la théorie des buffer overflows, présente des exemples notables, et discute
des méthodes de détection avant de plonger dans la conception détaillée de l'outil et les tâches
attribuées à chaque membre de l'équipe.


​ Définition et exemples des buffers overflows

​ Les buffer overflows représentent une classe de vulnérabilités critiques résultant de l'écriture de
données au-delà de la capacité d'un buffer, une zone de mémoire spécifiquement allouée pour les
contenir. Ce débordement peut entraîner la corruption de données adjacentes, une altération du
contrôle de flux du programme, voire son arrêt inopiné. Ces vulnérabilités sont particulièrement
courantes dans les langages de programmation de bas niveau comme le C, qui n'intègrent pas de
mécanismes automatiques de vérification des limites pour les buffers. Comprendre et prévenir les
buffer overflows est donc un enjeu majeur dans la programmation sécurisée en C, où la gestion
manuelle de la mémoire est monnaie courante et source potentielle d'erreurs critiques.

​ Exemples célèbres de Buffer Overflows

Les buffer overflows ont marqué l'histoire de la sécurité informatique par leur impact notable. Voici
deux exemples célèbres qui soulignent leur gravité et l'importance de les prévenir :

● The Morris Worm : L'un des premiers worms à se propager à grande échelle sur l'Internet, le
Morris Worm, exploitait un dépassement de tampon dans le service de noms de domaine UNIX
(BIND). Cet exploit a entraîné une perturbation considérable d'une large portion de l'Internet en
1988, mettant en évidence la nécessité de sécuriser les systèmes contre de telles vulnérabilités.

6
● Ghost (2015) : Une vulnérabilité majeure surnommée "Ghost" a été découverte dans la
bibliothèque GNU C (glibc), touchant spécifiquement la fonction gethostbyname(). Cette faille
permettait à un attaquant d'exécuter du code arbitraire à distance en exploitant des dépassements de
tampon. L'impact potentiel était immense, car de nombreuses applications et services utilisant cette
fonction pour résoudre les noms d'hôtes étaient vulnérables.

Ces incidents démontrent clairement que les buffer overflows sont des vecteurs d'attaque puissants,
exploitables pour compromettre des systèmes entiers. Ils soulignent l'impératif d'adopter des
pratiques de codage sécurisées et de mettre en place des mécanismes de détection et de prévention
efficaces pour protéger les infrastructures informatiques.

1) Conception Globale

Notre outil est architecturé pour analyser les codes sources en C en exploitant Clang, une interface
avancée du compilateur LLVM dédiée à l'analyse syntaxique et sémantique. Cette intégration
permet à notre outil d'offrir une inspection détaillée et précise, capitalisant sur la puissance de Clang
pour décortiquer la structure du code C et identifier les zones potentiellement vulnérables aux
débordements de tampon.

1.1) Intégration avec Clang

L'intégration de notre outil avec Clang transforme l'analyse du code source en C, la rendant à la fois
précise et efficiente. Clang, en générant un Arbre Syntaxique Abstrait (AST) pour le code analysé,
devient un pilier central de notre méthode de détection. L'outil navigue à travers cet AST pour
identifier les points critiques comme les déclarations de variables et les appels de fonctions, zones
où les dépassements de tampon sont susceptibles de se produire.

● Utilisation du Parseur Clang : En s'appuyant sur Clang en tant que moteur d'analyse, l'outil
bénéficie d'une vision détaillée et structurée du code, ce qui est indispensable pour déceler les
structures de données complexes et les flux de contrôle. Cette capacité à discerner les aspects subtils
du code est cruciale pour une identification fiable des vulnérabilités potentielles.

Pour démontrer l'intégration de Clang, examinons un fragment de code C qui illustre un cas typique
de vulnérabilité liée au buffer overflow :

7
}

Dans cet exemple, la chaîne "Really long text", nettement plus longue que la capacité du buffer buf,
entraîne un dépassement de tampon. Notre outil, grâce à l'AST généré par Clang, peut identifier
cette utilisation risquée de strcpy et signaler la vulnérabilité.

1.2) Étapes d'Identification

Génération de l'AST avec Clang

Pour illustrer le processus d'identification, prenons l'exemple où notre outil utilise Clang pour
analyser et générer l'Arbre Syntaxique Abstrait (AST) d'un code source en C. Clang décompose le
code en une structure arborescente, où chaque nœud représente différents composants du code, tels
que les déclarations de variables, les appels de fonctions, et les littéraux de chaînes. Cette
représentation en AST permet une analyse détaillée et granulaire du programme. Lorsque nous
exécutons clang-check -ast-dump -ast-dump-filter=main file.c sur le code fourni précédemment,
Clang produit un AST qui met en évidence la structure du programme. Dans cet AST, le nœud
correspondant à l'appel de la fonction strcpy révèle un potentiel dépassement de tampon, car la
chaîne "Really long text" dépasse la capacité du buffer buf.

Voici un aperçu de l'AST généré par Clang, utilisant la commande


clang-check -ast-dump -ast-dump-filter=main file.c

Dumping main:
`-CompoundStmt 0x1586c1f9b70 <col:12, lin[Link]>
|-DeclStmt 0x1586c1f9810 <lin[Link], col:17>
| `-VarDecl 0x1586c1f97a8 <col:5, col:16> col:10 used buf 'char[10]'
|-CallExpr 0x1586c1f9950 <lin[Link], col:36> 'char *'
| |-ImplicitCastExpr 0x1586c1f9938 <col:5> 'char *(*)(char *, const char *)'
<FunctionToPointerDecay>

8
| | `-DeclRefExpr 0x1586c1f9828 <col:5> 'char *(char *, const char *)' Function 0x1586c1e42d0 |
|-ImplicitCastExpr 0x1586c1f9980 <col:12> 'char *' <ArrayToPointerDecay>
| | `-DeclRefExpr 0x1586c1f9848 <col:12> 'char[10]' lvalue Var 0x1586c1f97a8 'buf' 'char[10]'
| `-ImplicitCastExpr 0x1586c1f99b0 <col:17> 'const char *' <NoOp>
| `-ImplicitCastExpr 0x1586c1f9998 <col:17> 'char *' <ArrayToPointerDecay>
| `-StringLiteral 0x1586c1f98a8 <col:17> 'char[18]' lvalue "Really long text!"
|-CallExpr 0x1586c1f9ac0 <lin[Link], col:23> 'int'
| |-ImplicitCastExpr 0x1586c1f9aa8 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0x1586c1f99c8 <col:5> 'int (const char *, ...)' Function 0x1586c1b86e8 '|
|-ImplicitCastExpr 0x1586c1f9b08 <col:12> 'const char *' <NoOp>
| | `-ImplicitCastExpr 0x1586c1f9af0 <col:12> 'char *' <ArrayToPointerDecay>
| | `-StringLiteral 0x1586c1f9a28 <col:12> 'char[4]' lvalue "%s\n"
| `-ImplicitCastExpr 0x1586c1f9b20 <col:20> 'char *' <ArrayToPointerDecay>
| `-DeclRefExpr 0x1586c1f9a48 <col:20> 'char[10]' lvalue Var 0x1586c1f97a8 'buf' 'char[10]'
`-ReturnStmt 0x1586c1f9b60 <lin[Link], col:12>
`-IntegerLiteral 0x1586c1f9b38 <col:12> 'int' 0

En naviguant à travers cet AST, notre outil identifie les points critiques du code. Par exemple, il
détecte l'appel à strcpy, une fonction notoirement connue pour ses risques de buffer overflow si mal
utilisée. L'outil examine les arguments passés à strcpy et compare la longueur de la chaîne source
avec la capacité du buffer de destination. Si la source excède la taille du buffer, cela signale un
dépassement potentiel, alertant ainsi les développeurs d'un risque de sécurité critique.

Analyse de strcpy : L'outil analyse l'appel à strcpy, extrayant et comparant les tailles des
arguments. Si la chaîne "Really long text" est plus longue que le buffer buf, cela est identifié
comme un dépassement de tampon potentiel. Grâce à cette analyse, l'outil met en lumière la
vulnérabilité, permettant aux développeurs de prendre les mesures correctives nécessaires pour
renforcer la sécurité du code.

Identification des Déclarations de Variables

En explorant l'AST, notre outil repère la déclaration char buf[10];. Il procède ensuite à
l'enregistrement de chaque variable détectée dans une HashMap, stockant les noms des variables et
leurs attributs (taille et type) sous forme de structures:
pub struct Variable {
pub name: String,
pub size: usize,
pub var_type: String,
}

9
pub struct CodeParser {
variables: HashMap<String, Variable>,
}

Cette approche optimise la recherche et l'accès aux informations concernant les variables, facilitant
ainsi l'analyse et la détection des vulnérabilités potentielles.

À travers ce processus, notre outil peut efficacement identifier les zones à risque dans le code
source, comme les appels à strcpy avec des arguments susceptibles de provoquer un dépassement de
tampon. En déduisant la longueur de la chaîne passée en argument et la comparant à la taille du
buffer déclaré, l'outil peut signaler de manière proactive les instances potentielles de buffer
overflow, contribuant ainsi significativement à la sécurité du code. Les variables sont représentées
par la structure suivante:

1.3) Analyse des Appels de Fonctions

Après l'identification des déclarations de variables, notre outil se concentre sur l'analyse des appels
de fonctions, en particulier ceux qui sont susceptibles de conduire à des buffer overflows, comme
strcpy. Voici les étapes détaillées de cette analyse :

Extraction des Arguments : L'outil extrait les arguments de la fonction strcpy, déterminant le
buffer de destination (buf) et la chaîne source ("Really long text!"). Cette étape est cruciale pour
comprendre l'intention du code et les opérations effectuées sur les buffers.
Comparaison des Tailles : Avec les données extraites, l'outil évalue la longueur de la chaîne source
et la compare à la capacité du buffer de destination. Dans notre exemple, "Really long text!" est
nettement plus long que le buffer buf, qui a une capacité de 10 caractères. L'outil identifie que la
chaîne dépasse cette capacité, tenant compte du caractère nul de terminaison \0 nécessaire en C pour
marquer la fin d'une chaîne.

L'outil utilise les informations stockées dans la HashMap pour effectuer cette comparaison
efficacement. Lorsqu'il détecte que la taille de la chaîne source excède celle du buffer de
destination, il signale un potentiel dépassement de tampon. Cette capacité de détection préventive
aide les développeurs à identifier et à corriger les vulnérabilités avant que le code ne soit déployé ou
exploité, renforçant ainsi la sécurité du logiciel. Voici la fonction qui parse un appel de fonction
‘strcpy’

10
fn parse_strcpy(&self, args: &Vec<Entity<'_>>) -> bool {
let dest = args[0];
let srce = args[1];
let var_dest =
[Link](&dest.get_display_name().unwrap()).expect("Variable not
declared");
match srce.get_display_name() {
Some(var_name) => {// Cas où la source est un pointeur
let var_srce =[Link](&var_name).expect("Variable not
declared");
var_srce.size >= var_dest.size
},
None => {// Cas où la source est une valeur littérale
let value = get_litteral(srce);
let size = [Link]() - 2;
size >= var_dest.size
},
}
}

Détection de la vulnérabilité

Après avoir constaté que la longueur de la chaîne dépasse la capacité du buffer buf, l'outil marque
cette situation comme un dépassement de tampon potentiel. Ce type de vulnérabilité, où le
programme tente d'écrire au-delà des limites d'un tampon alloué, peut entraîner la corruption de la
mémoire adjacente ou perturber le flux de contrôle du programme, menant à des erreurs
imprévisibles ou même à des failles de sécurité exploitées. Cet exemple souligne l'importance d'une
analyse minutieuse et de la détection proactive pour prévenir les risques associés aux buffer
overflows dans les applications en C.

Répartition des tâches

Dans le développement de notre outil, une phase initiale cruciale a consisté à effectuer des
recherches approfondies pour identifier les outils, les langages de programmation, et les
bibliothèques les plus adaptés à nos objectifs. Cette phase a impliqué l'exploration de diverses
technologies d'analyse de code et de parseurs, ainsi que la consultation de ressources techniques et
de discussions sur des forums spécialisés. Suite à cette phase de recherche, nous avons procédé à la
répartition des tâches comme suit :

11
Lamlih Houssam: Ma responsabilité était de développer le parseur pour la fonction scanf. Cette
tâche a nécessité non seulement une compréhension technique des différentes manières dont les
entrées utilisateur peuvent être traitées en C, mais aussi une analyse de comment ces interactions
peuvent affecter la sécurité du programme. L'enjeu était de détecter des utilisations de scanf
pouvant potentiellement conduire à des vulnérabilités.
Tayeb Messaoud: Mon domaine de concentration était l'analyse des fonctions strcat et strcpy. Ces
fonctions, essentielles pour la manipulation de chaînes en C, sont également connues pour leur
vulnérabilité aux buffer overflows si elles ne sont pas utilisées avec précaution. Ma tâche consistait
à élaborer des mécanismes permettant de détecter et d'alerter sur les usages risqués de ces fonctions.

Pour assurer l'intégrité et la qualité du projet, nous avons instauré un processus de révision
mutuelle. Ce processus impliquait des échanges réguliers sur le code développé par chacun,
permettant ainsi de bénéficier de perspectives variées et d'assurer une cohérence dans l'ensemble du
projet. Cette collaboration étroite a joué un rôle déterminant dans la réussite de notre initiative de
renforcer la sécurité des applications en C à travers l'analyse statique du code.

Travaux à compléter

Inclure d’autres fonctions vulnérables: Étendre la détection aux autres fonctions connues pour
leurs vulnérabilités. L’outil détecte actuellement les vulnérabilités reliées aux fonctions scanf,
strcpy, strcat.

Correction automatique des vulnérabilités: Développer une fonctionnalité qui permet non
seulement d’identifier les dépassements de tampon mais aussi de proposer et d’appliquer des
corrections directement dans le code source. Cette amélioration viserait à réduire le temps
nécessaire pour rendre le code sûr, en automatisant la correction des problèmes courants de sécurité.

Voici les approches envisagées:

– Substitution de Fonctions: Remplacer automatiquement les fonctions à risque comme strcpy par
des alternatives plus sûres telles que strncpy, en ajustant les paramètres pour respecter les limites de
taille des tampons.

– Ajout de Vérifications: Insérer des vérifications de taille avant les opérations critiques sur les
tampons pour s’assurer que les limites ne seront pas dépassées.

Détection dynamique: Rajouter un module d’analyse dynamique, exécutant le code dans un


environnement contrôlé pour identifier les dépassements de tampon qui se produisent à l’exécution.

12
Cette approche permettrait de détecter des vulnérabilités qui ne sont pas évidentes à l’analyse
statique seule. Voici l’approche envisagée:

– Tests Fuzzing: Utiliser des techniques de fuzzing pour générer automatiquement des entrées de
test aléatoires ou semi-aléatoires dans le but de provoquer des comportements inattendus, y compris
des dépassements de tampon.

Arguments de ligne de commande: Rajouter des arguments de ligne de commande pour pouvoir
bien organiser les différentes fonctionnalités de l’outil.

Gestion des structures: Gérer les buffer overflows reliés aux structures complexes.

13
B) Fond Pratique et Travaux Connexes
​ Cette section est dédiée à la présentation détaillée des étapes concrètes de mise en œuvre de l'outil
développé, ainsi qu'à l'illustration de son fonctionnement à travers des cas d'usage réels ou simulés.

Mise en oeuvre d’ARGOS

Dans le domaine du développement logiciel, la sécurité des systèmes informatiques est une
préoccupation centrale, surtout en ce qui concerne les vulnérabilités susceptibles de compromettre
des systèmes entiers. Parmi elles, les débordements de tampon (buffer overflows) représentent une
menace sérieuse. Ces failles surviennent lorsqu'un programme écrit des données au-delà des limites
de mémoire allouées, souvent en raison de contrôles insuffisants des limites sur les entrées ou les
opérations de copie de données. Les conséquences peuvent être graves, incluant la perte de données,
la corruption de systèmes, ou la création de brèches de sécurité par lesquelles des attaquants
peuvent prendre le contrôle des systèmes affectés.

Dans ce contexte critique, notre projet s'est attaché à développer un outil, nommé ARGOS1,
utilisant le langage de programmation Rust pour identifier et prévenir les débordements de tampon
dans les applications écrites en C. Le nom "ARGOS" tire son inspiration de la mythologie grecque,
où Argos Panoptès était un géant aux cent yeux, toujours vigilant et impossible à surprendre. De
manière similaire, notre outil se veut être d'une vigilance inégalée dans la surveillance des
vulnérabilités de sécurité, offrant une vue panoramique sur les potentiels débordements de tampon
et intervenant de manière proactive pour les contrer. De plus, "ARGOS" est également l'acronyme
de "Automated Rust Guard for Overflow Security", reflétant son rôle protecteur automatisé
dans la programmation en Rust. Notre outil combine des approches d'analyse statique et dynamique
pour une protection robuste contre cette classe de vulnérabilités. L'analyse statique, à travers
l'utilisation du parseur de Clang, construit un arbre syntaxique abstrait du code source, détectant les
usages de fonctions à risque. Parallèlement, l'analyse dynamique intercepte les appels système
critiques dans les binaires Linux, ajustant le comportement des allocations de mémoire et des
opérations de copie pour fournir une défense en temps réel.

Cette partie du rapport explore en détail le développement d'ARGOS, depuis sa conception jusqu'à
son implémentation, en passant par les phases de test et de validation. Nous discuterons des
résultats obtenus, de l'efficacité de l'outil dans divers scénarios d'application, ainsi que de ses limites

1
Dans la mythologie grecque, Argos (en grec ancien Ἄργος / Árgos et en latin Argus), fils d'Arestor — ou du dieu
fleuve Inachos, ou d'Argos (fils de Zeus), selon les versions — et de Mycène (ou de Gaïa), est un Géant ayant cent
yeux.
[Link]
os,un%20G%C3%A9ant%20ayant%20cent%20yeux.
14
actuelles et des améliorations futures envisageables, en mettant en perspective le rôle crucial
d'ARGOS dans la sécurisation des environnements informatiques modernes.

###Structure du projet
.
├── [Link]
├── [Link]
├── [Link]
├── [Link]
src/
├── intercept.c
├── [Link]
├── [Link]
└──[Link]
test/
├── dynamic_demo.c
└── static_demo.c

Méthodologie et Présentation des résultats

La méthodologie de notre projet ARGOS se divise en deux principaux volets : l'analyse statique et
l'analyse dynamique. Chacun de ces volets est conçu pour utiliser des approches et des technologies
spécifiques afin de détecter et de prévenir les débordements de tampon. Cette section fournit une
explication détaillée des techniques et des processus mis en œuvre dans chaque partie de l'outil,
soulignant leur complémentarité et leur efficacité.

1) Analyse Statique

L'analyse statique dans ARGOS commence par la construction d'un arbre syntaxique abstrait (AST)
du code source en C, utilisant le parseur de Clang. Cette étape est cruciale car elle permet de
représenter structurellement le code source, facilitant l'identification des points critiques
susceptibles de générer des vulnérabilités.

Construction de l'arbre syntaxique abstrait (AST)

● Parseur Clang : Utilisé pour analyser le code source et construire l'AST, offrant une
représentation précise et manipulable des éléments programmés.
● Extraction des variables : Identification et extraction des informations sur les buffers (taille et
type), stockées dans une structure de données de type HashMap pour un accès rapide et efficace
pendant l'analyse.

15
Détection des fonctions vulnérables

Analyse des appels de fonctions : Le système scrute l'AST pour repérer les appels à des
fonctions à risque telles que strcpy, strcat, et scanf. Il évalue également les arguments passés à ces
fonctions pour s'assurer que les tailles des buffers sont respectées.
Propositions de corrections automatiques : Lorsqu'une vulnérabilité est détectée, l'outil génère
un rapport d'erreur précisant la localisation du code, la nature de la vulnérabilité, et propose des
correctifs automatiques pour prévenir le débordement. ##Par exemple:
char buf[8];
strcpy(buf, "Really long text!");
Pour ce code le correcteur va afficher le rapport d’erreur suivant:

2) Analyse Dynamique

L'analyse dynamique dans ARGOS est conçue pour compléter l'analyse statique en intervenant
directement lors de l'exécution du programme. Cette méthode permet de contrer les vulnérabilités
en temps réel, offrant ainsi une couche supplémentaire de sécurité qui réagit immédiatement aux
actions potentiellement dangereuses. Voici les principales composantes et fonctionnalités de cette
analyse :

Interception des appels système

1. Utilisation de LD_PRELOAD : Pour permettre une interception efficace, ARGOS utilise la


variable d’environnement LD_PRELOAD, qui force le chargement préalable d'une bibliothèque
partagée spécifique. Cette bibliothèque est conçue pour se charger avant toutes les autres
bibliothèques utilisées par l'application, permettant ainsi de redéfinir certaines fonctions clés du
système. ## Nous avons définit cette bibliothèque dans le fichier intercept.c, cette bibliothèque
utilise des fonctions exportées de la dll conçue dans Rust. La shared library ainsi compilé du
fichier intercept.c grâce à la commande:
clang -shared -fPIC src/intercept.c -o src/[Link]
-L./target/release -lrust_overflow_sentinel
Le flag -shared signifie qu’on veut compiler intercept.c en une shared library. Le flag -fPIC
signifie Position Independent Code, ce qui précise au compilateur que ce code peut être exécuté
correctement sur n’importe quelle plage mémoire. [Link] est le nom de la librairie
résultante. et -L./target/release -lrust_overflow_sentinel précise que intercept.c utilise des
fonctions exportées de la bibliothèque rust_overflow_sentinel située à ./target/release
Après la compilation de la shared library, nous devons éxecuter dans notre [Link] le binaire qu’on

16
veut tester grâce à la fonction suivante:
let result_output = std::process::Command::new(target_binary)
.env("LD_PRELOAD", library_path).output();

2. Fonctions redéfinies : Des fonctions systèmes telles que malloc, free, et strcpy sont redéfinies
dans notre bibliothèque préchargée. Cette redéfinition permet à ARGOS de surveiller et de
modifier le comportement de ces fonctions pour prévenir les débordements de tampon. Par
exemple, lors d'un appel à strcpy, ARGOS vérifie d'abord que la taille de la destination est
suffisante pour accueillir les données copiées, bloquant l'opération si ce n'est pas le cas. ## On
peut voir la redéfinition de la fonction strcpy dans les fichier intercept.c et [Link] comme exemple:

// intercept.c
extern int strcpy_intercept(char* dest, const char* src);
char* strcpy(char* dest, const char* src) {
static char* (*real_strcpy)(char*, const char*) = NULL;
if (!real_strcpy) {
real_strcpy = dlsym(RTLD_NEXT, "strcpy");
}
printf("[::ARGOS::] Intercepted strcpy\n");
int stat = strcpy_intercept(dest, src);
char* p = NULL;
if (stat == 1)
p = real_strcpy(dest, src);
return p;
}
// [Link]
#[no_mangle]
/// Intercept strcpy calls
pub unsafe extern "C" fn strcpy_intercept(dest: *mut libc::c_char, src:
*const libc::c_char) -> i32 {
let shm_key = 42;
let mut tst_struct = read_from_shmem::<DynamicPtrTracker>(shm_key);

let dest_size = libc::strlen(dest);


let srce_size = libc::strlen(src);
let mut stat = 1;
if srce_size > dest_size {
tst_struct.bounds_violated += 1;
stat = 0;
}
write_to_shmem(tst_struct, shm_key);
return stat;
}
On utilise l’attribut #[no_mangle] de rust pour indiquer au compilateur de ne pas “mangler”

17
“modifier” le nom de la fonction strcpy_intercept lors de la compilation afin de faciliter
l'intégration avec le code de la libraire intercept. Nous utilisons aussi la librairie libc dans le but
d’avoir des équivalents des types primitifs de C, *mut libc::c_char est l’équivalent de char* en C.

Surveillance et contrôle en temps réel

1. Suivi des allocations de mémoire : ARGOS maintient un registre de toutes les allocations de
mémoire effectuées par le programme. Ce suivi permet de détecter les anomalies telles que les
fuites de mémoire ou les tentatives d'accès à de la mémoire déjà libérée.
2. Vérification des bornes à l'exécution : À chaque opération de copie de données, ARGOS
compare les tailles des tampons source et destination. Si le tampon de destination est plus petit que
le tampon source, l'opération est interrompue et une alerte est générée, prévenant ainsi un potentiel
débordement de tampon.

Utilisation de la mémoire partagée pour la communication inter-processus (IPC)

1. Shared Memory : ARGOS utilise la mémoire partagée pour avoir une communication entre le
binaire cible et notre programme Rust (ARGOS). On peut voir dans la fonction strcpy_intercept
définie précédemment, les opérations d’écriture et de lecture effectuées sur la shared memory.
La procédure est standard, on définit ces fonctions de la manière suivante en utilisant encore une
fois la crate “librairie dans rust” libc:

/// Read data from shared memory with key shm_key


pub fn read_from_shmem<T>(shm_key: i32) -> T where T: Copy + Debug {
let mem_size = std::mem::size_of::<T>() as libc::size_t;
let shmem_id = unsafe {
libc::shmget(shm_key, mem_size, 0o666 |
libc::IPC_CREAT)
};
let ptr = unsafe { libc::shmat(shmem_id,ptr::null() as
*const libc::c_void, 0) } as *mut T;
if ptr.is_null() || (ptr as isize) == -1 {
panic!("Failed to attach to shmem on read");
}
let data = unsafe { *ptr };
// println!("Data {:?}", data);
unsafe {
libc::shmdt(ptr as *const libc::c_void);
libc::shmctl(shmem_id, libc::IPC_RMID,
ptr::null_mut());
}
data }

18
Dans notre approche, nous utilisons un type générique T, muni des traits Copy et Debug, ce qui rend
notre code non seulement générique mais également scalable. Le processus débute par la
récupération de l'identifiant de la mémoire partagée via une clé unique, shm_key. Si aucune
mémoire partagée n'est associée à cette clé, elle est alors créée avec des permissions de lecture et
d'écriture (0o666) et est dimensionnée pour accueillir le type T. Une fois la mémoire partagée
établie, elle est attachée à un pointeur, qui nous sert de lien direct pour accéder aux données. Nous
procédons ensuite à la copie des données du pointeur vers une variable data, opération cruciale pour
extraire et manipuler les informations stockées. Finalement, le pointeur est détaché de la mémoire
partagée, assurant ainsi que les ressources sont correctement libérées et évitant les fuites de
mémoire. Ce processus garantit une manipulation sécurisée et efficace des données en mémoire,
conformément aux standards de programmation en Rust.

Pour illustrer comment l’analyse dynamique de notre outil ARGOS fonctionne en pratique, nous
allons examiner un exemple sur un binaire vulnérable. Le code C ci-dessous a été compilé avec
Clang pour créer le binaire cible:

for (int i = 0; i < 80; i++) {


char* values = malloc(sizeof(char) * 6);
strcpy(values, "Really long text!");
}

Voici le résultats de l’analyse dynamique sans avoir accès au code source


initial:

Limitations et Propositions pour les Améliorations Futures

Bien que notre outil ARGOS utilise une combinaison puissante d'analyses statique et dynamique
pour détecter et prévenir les débordements de tampon dans les applications C/C++, il est confronté
à des défis spécifiques qui nécessitent une attention continue pour améliorer son efficacité et sa
portée.

Limitations Actuelles

​ Complexité des Structures de Données en C:

19
● Problème: Le parseur statique peut avoir des difficultés à interpréter des structures de
données complexes ou des pointeurs profondément imbriqués, ce qui peut diminuer la
précision de la détection.
● Conséquence: Cela peut entraîner des faux négatifs, où des vulnérabilités potentielles ne
sont pas identifiées.

​ Performance en Environnement Multithread:
● Problème: L'analyse dynamique n'est pas entièrement optimisée pour des environnements
multithreads, ce qui peut conduire à des problèmes de synchronisation et de performance.
● Conséquence: Les performances peuvent être impactées négativement lors de la
surveillance intensive en environnements complexes.

​ Compatibilité Limitée aux Environnements:
● Problème: Actuellement, notre utilisation de la variable d'environnement LD_PRELOAD
limite l'outil aux systèmes Linux.
● Conséquence: Cela restreint son applicabilité dans des environnements Windows ou
d'autres systèmes d'exploitation.

Améliorations Futures et Solutions

Amélioration de l'Analyse des Structures Complexes:


● Solution: Développer des algorithmes plus sophistiqués pour le parseur statique afin
d'améliorer la reconnaissance et l'analyse des structures de données complexes.
● Avantages: Cela augmenterait la précision et réduirait les risques de faux négatifs.

Optimisation pour Multithreading:
● Solution: Adapter les mécanismes de surveillance pour une gestion optimale des threads,
possiblement par l'intégration de techniques avancées de verrouillage ou de gestion de
concurrence.
● Avantages: Amélioration des performances et de la fiabilité en environnements
multithreads.

Extension de la Compatibilité Inter-plateformes:
● Solution: Explorer d'autres méthodes d'interception d'appels système qui ne dépendent pas
exclusivement de LD_PRELOAD pour une compatibilité étendue à d'autres systèmes
comme Windows.
● Avantages: Expansion du marché potentiel de l'outil et augmentation de sa flexibilité.

20
CONCLUSION

Pour conclure le rapport sur le projet "Automated Rust Guard for Overflow Security (ARGOS)",
nous avons développé un outil sophistiqué en Rust destiné à identifier et prévenir les vulnérabilités
de débordement de tampon dans les applications écrites en C. Inspiré par Argos Panoptès, le géant
vigilant de la mythologie grecque, notre outil combine une analyse statique proactive avec une
analyse dynamique précise, offrant une surveillance exhaustive et une intervention en temps réel
pour renforcer la sécurité des logiciels.

L'analyse statique utilise le parseur de Clang pour construire un arbre syntaxique abstrait,
permettant de détecter les usages potentiellement dangereux des fonctions, tandis que l'analyse
dynamique intercepte et modifie les opérations de mémoire critiques à travers des appels système
sous Linux. Ensemble, ces méthodologies fournissent une défense robuste contre les attaques
potentielles, réduisant le risque de compromission de systèmes via des débordements de tampon.

Bien que l'outil présente des innovations marquantes, il est aussi sujet à certaines limitations, telles
que des défis dans l'analyse de structures de données complexes et un manque d'optimisation pour
les environnements multi-thread. Ces défis sont reconnus comme des opportunités pour des
améliorations futures, notamment l'optimisation des algorithmes d'analyse et l'extension de la
compatibilité de l'outil à divers systèmes d'exploitation.

En somme, ARGOS se distingue comme une avancée significative dans le domaine de la sécurité
des logiciels, marquant une étape importante vers la protection efficace contre les vulnérabilités de
débordement de tampon.

21
BIBLIOGRAPHIE
​ Aleph One. (1996). Smashing The Stack For Fun And Profit. Phrack Magazine. Récupéré de
[Link]

​ The Rust Programming Language. (n.d.). Récupéré de [Link]

​ Lattner, C. et al. (2021). Clang: a C language family frontend for LLVM. The LLVM Project.
Récupéré de [Link]

​ Szekeres, L. et al. (2013). SoK: Eternal War in Memory. 2013 IEEE Symposium on Security and
Privacy. IEEE. Récupéré de [Link]

​ Computerphile. (2016). Video Tutorial on Buffer Overflow. Récupéré de
[Link]

​ Vikas, A. (s.d.). Evaluation of Inter-Process Communication Mechanisms. Récupéré de
[Link]
f

​ Matz, M. et al. (2018). Effective Static Analysis of C/C++ Code with Clang Checkers.
Proceedings of the 5th Workshop on LLVM Compiler Infrastructure in HPC. LLVM. Récupéré de
[Link]

​ Shacham, H. et al. (2007). The Geometry of Innocent Flesh on the Bone: Return-into-libc without
Function Calls (on the x86). Proceedings of the 14th ACM Conference on Computer and
Communications Security. ACM. Récupéré de [Link]

​ IEEE Security & Privacy. (n.d.). Recent Advances in Intrusion Detection. Récupéré de
[Link]

22

Vous aimerez peut-être aussi