Astuces de variables d'environnement

Variables d'environnement intéressantes à charger dans les interpréteurs de scripts



introduction



Dans un récent projet de piratage, nous avons eu la possibilité de spécifier des variables d'environnement, mais pas un processus en cours d'exécution. Nous ne pouvions pas non plus contrôler le contenu du fichier sur le disque, et la force brute des ID de processus (PID) et des descripteurs de fichier n'a pas donné de résultats intéressants, à l'exclusion des exploits LD_PRELOAD à distance . Heureusement, un interpréteur de langage de script a été exécuté, ce qui nous a permis d'exécuter des commandes arbitraires en définissant certaines variables d'environnement. Ce blog explique comment des commandes arbitraires peuvent être exécutées par une gamme d'interprètes de langage de script sous des variables d'environnement malveillantes.



Perl



Une lecture rapide de la section de la ENVIRONMENTpage de manuel perlrun(1)révèle de nombreuses variables d'environnement qui méritent d'être explorées. La variable d'environnement PERL5OPTvous permet de définir des options de ligne de commande, mais se limite à n'accepter que les options CDIMTUWdmtw. Malheureusement, cela signifie un manque -e, ce qui permet de charger du code perl à exécuter.



Tout n'est pas perdu, cependant, comme le montre l' exploit pour CVE-2016-1531 de Hacker Fantastic . L'exploit écrit un module Perl malveillant dans un fichier /tmp/root.pmet fournit des variables d'environnement PERL5OPT=-Mrootet PERL5LIB=/ tmppour exécuter du code arbitraire. Cependant, il s'agissait d'un exploit pour une vulnérabilité d'escalade de privilèges locale, et la méthode générique ne devrait idéalement pas nécessiter l'accès au système de fichiers. Regarderexploité par blasty pour le CVE même, il n'a pas nécessité la création d'un fichier, utiliser des variables d'environnement PERL5OPT=-det PERL5DB=system("sh");exit;. Les mêmes variables ont été utilisées pour résoudre le problème du FCT en 2013.



La dernière subtilité de la méthode générique est d'utiliser une variable d'environnement au lieu de deux. @justinsteven a constaté que cela était possible avec PERL5OPT=-M. Pendant le téléchargement du module perl, vous pouvez utiliser soit -mou -M, mais une option -Mvous permet d'ajouter du code supplémentaire après le nom du module.



Preuve de concept



Exemple 0: exécution de code arbitraire avec une variable d'environnement versus perl exécutant un script vide (/ dev / null)



$ docker run --env 'PERL5OPT=-Mbase;print(`id`)' perl:5.30.2 perl /dev/null
uid=0(root) gid=0(root) groups=0(root)


Python



A en juger par la section ENVIRONMENT VARIABLESde mana on python(1), cela PYTHONSTARTUPressemble au départ à une solution simple. Il vous permet de spécifier le chemin d'accès à un script Python qui sera exécuté avant que l'invite ne s'affiche de manière interactive. L'exigence du mode interactif ne semble pas être un problème, car une variable d'environnement PYTHONINSPECTpeut être utilisée pour entrer en mode interactif, tout comme -isur la ligne de commande. Cependant, la documentation de l'option -iexplique ce PYTHONSTARTUPqui ne sera pas utilisé lorsque python sera lancé avec un script à exécuter. Cela signifie que les PYTHONSTARTUPdeux PYTHONINSPECTne peuvent pas être combinés et PYTHONSTARTUPn'ont d'effet que lorsque le Python REPL est immédiatement démarré. Cela signifie finalement quePYTHONSTARTUPnon viable car il n'a aucun effet lorsqu'un script Python normal est exécuté.



Variables d'environnement PYTHONHOMEet semblait prometteur PYTHONPATH. Les deux permettent l'exécution de code arbitraire, mais vous obligent également à créer des répertoires et des fichiers sur le système de fichiers. Il peut être possible d'assouplir ces exigences en utilisant un système de fichiers virtuel / proc et / ou des fichiers ZIP.



La plupart des autres variables d'environnement sont simplement vérifiées pour une chaîne non vide, et si c'est le cas, incluent un paramètre généralement bénin. L'une des rares exceptions est PYTHONWARNINGS.



Travailler avec PYTHONWARNINGS



La documentation de PYTHONWARNINGSdit que cela équivaut à spécifier un paramètre -W. Ce paramètre est -Wutilisé pour la gestion des alertes pour spécifier les alertes et la fréquence de leur affichage. La forme complète de l'argument est action:message:category:module:line. Bien que la surveillance des alertes ne semble pas être un indice prometteur, cela a changé rapidement après avoir testé la mise en œuvre.



Exemple 1: Python-3.8.2 / Lib / warnings.py



[...]
def _getcategory(category):
    if not category:
        return Warning
    if '.' not in category:
        import builtins as m
        klass = category
    else:
        module, _, klass = category.rpartition('.')
        try:
            m = __import__(module, None, None, [klass])
        except ImportError:
            raise _OptionError("invalid module name: %r" % (module,)) from None
[...]


Ce code montre que tant que notre catégorie spécifiée contient un point, nous pouvons commencer à importer un module Python arbitraire.



Le problème suivant est que la grande majorité des modules de la bibliothèque standard Python exécutent très peu de code lors de l'importation. Ils définissent généralement simplement les classes à utiliser plus tard, et même lorsqu'ils fournissent du code à exécuter, le code est généralement protégé en vérifiant la variable __main__ (pour déterminer si le fichier a été importé ou exécuté directement).



Une exception inattendue à cette règle est le module antigravité . Les développeurs Python en 2008 ont inclus un œuf de Pâques qui peut être appelé en exécutantimport antigravity... Cet import ouvrira immédiatement une bande dessinée xkcd dans votre navigateur en plaisantant que l'importation antigravité en Python permet de voler.



Quant à la façon dont le module antigravityouvre votre navigateur, il utilise un autre module de la bibliothèque standard appelé webbrowser. Ce module vérifie votre PATH pour une grande variété de navigateurs, y compris mosaic, opera, skipstone, konqueror, chrome, chrome, firefox, links, elinks et lynx. Il accepte également une variable d'environnement BROWSERindiquant le processus à exécuter. Aucun argument ne peut être fourni au processus dans une variable d'environnement, et l'url xkcd de la bande dessinée est le seul argument codé en dur pour la commande.



La possibilité de transformer cela en exécution de code arbitraire dépend des autres exécutables disponibles sur le système.



Utiliser Perl pour exécuter du code arbitraire



Une approche consiste à utiliser Perl, qui est généralement installé sur le système et est même disponible dans l'image standard de Python Docker. Cependant, vous ne pouvez pas utiliser le binaire perlseul, car le premier et unique argument est l'url xkcd de la bande dessinée. Cet argument générera une erreur et le processus se terminera sans utiliser de variable d'environnement PERL5OPT.



Exemple 2: PERL5OPT n'a aucun effet lorsqu'une URL est passée à perl



$ docker run -e 'PERL5OPT=-Mbase;print(`id`);exit' perl:5.30.2 perl https://xkcd.com/353/
Can't open perl script "https://xkcd.com/353/": No such file or directory


Heureusement, lorsque Perl est disponible, les scripts Perl par défaut tels que perldoc et perlthanks sont souvent également disponibles. Ces scripts échoueront également avec un argument non valide, mais l'erreur dans ce cas se produit plus tard que le traitement de la variable d'environnement PERL5OPT. Cela signifie que vous pouvez utiliser la charge utile de la variable d'environnement Perl détaillée plus haut dans ce blog.



Exemple 3: PERL5OPT fonctionne comme prévu avec perldoc et perlthanks



$ docker run -e 'PERL5OPT=-Mbase;print(`id`);exit' perl:5.30.2 perldoc https://xkcd.com/353/
uid=0(root) gid=0(root) groups=0(root)
$ run -e 'PERL5OPT=-Mbase;print(`id`);exit' perl:5.30.2 perlthanks https://xkcd.com/353/
uid=0(root) gid=0(root) groups=0(root)


Preuve de concept



Exemple 4: exécution de code arbitraire à l'aide de plusieurs variables d'environnement avec Python 2 et Python 3



$ docker run -e 'PYTHONWARNINGS=all:0:antigravity.x:0:0' -e 'BROWSER=perlthanks' -e 'PERL5OPT=-Mbase;print(`id`);exit;' python:2.7.18 python /dev/null
uid=0(root) gid=0(root) groups=0(root)
Invalid -W option ignored: unknown warning category: 'antigravity.x'

$ docker run -e 'PYTHONWARNINGS=all:0:antigravity.x:0:0' -e 'BROWSER=perlthanks' -e 'PERL5OPT=-Mbase;print(`id`);exit;' python:3.8.2 python /dev/null
uid=0(root) gid=0(root) groups=0(root)
Invalid -W option ignored: unknown warning category: 'antigravity.x'


NodeJS



Michal Bentkowski a publié la charge utile de l'exploit Kibana (CVE-2019-7609) sur son blog . Un prototype de vulnérabilité de pollution a été utilisé pour définir des variables d'environnement arbitraires qui ont entraîné l'exécution arbitraire de commandes. La charge utile de Michal a utilisé la variable d'environnement NODE_OPTIONSet le système de fichiers proc en particulier /proc/self/environ.



Bien que la technique de Michal soit créative et fonctionne très bien dans son cas, elle n'est pas toujours garantie de fonctionner et présente certaines limites qu'il serait bon de résoudre.



La première limitation est qu'il utilise/proc/self/environuniquement si le contenu peut être rendu syntaxiquement valide par JavaScript. Pour ce faire, vous devez être capable de créer une variable d'environnement et de la faire apparaître en premier dans le contenu du fichier, /proc/self/environou connaître / tricher le nom de la variable d'environnement qui apparaît en premier et écraser sa valeur.



Une autre limitation est que la valeur de la première variable d'environnement se termine par un commentaire d'une ligne (//). Par conséquent, tout caractère de nouvelle ligne dans d'autres variables d'environnement est susceptible de provoquer une erreur de syntaxe et d'empêcher l'exécution de la charge utile. L'utilisation de commentaires sur plusieurs lignes (/ *) ne résoudra pas le problème, car ils doivent être fermés pour être syntaxiquement corrects. Par conséquent, dans les rares cas où une variable d'environnement contient un caractère de nouvelle ligne, il est nécessaire de connaître / annuler la définition du nom de la variable d'environnement et de remplacer sa valeur par une nouvelle valeur qui ne contient pas de nouvelle ligne.



Nous laisserons la suppression de ces limitations comme un exercice pour le lecteur.



Preuve de concept



Exemple 5. Exécution de code arbitraire avec des variables d'environnement contre NodeJS de Michal Bentkowski



$ docker run -e 'NODE_VERSION=console.log(require("child_process").execSync("id").toString());//' -e 'NODE_OPTIONS=--require /proc/self/environ' node:14.2.0 node /dev/null
uid=0(root) gid=0(root) groups=0(root)


PHP



Si vous l'exécutez ltrace -e getenv php /dev/null, vous constaterez que PHP utilise une variable d'environnement PHPRC. La variable d'environnement est utilisée lors de la tentative de recherche et de chargement d'un fichier de configuration php.ini. L'exploit neex pour CVE-2019-11043 utilise un certain nombre de paramètres PHP pour forcer l'exécution de code arbitraire. Dans Orange, Tsai a également un excellent article sur la création de votre propre exploit pour la CVE, qui utilise une liste de paramètres légèrement différente. En utilisant ces connaissances, ainsi que les connaissances acquises avec les techniques NodeJS précédentes, et l'aide de Brendan Scarwell , une solution PHP a été trouvée avec deux variables d'environnement.



Cette méthode a les mêmes limitations que les exemples NodeJS.



Preuve de concept



Exemple 6: exécution de code arbitraire avec des variables d'environnement contre PHP



$ docker run -e $'HOSTNAME=1;\nauto_prepend_file=/proc/self/environ\n;<?php die(`id`); ?>' -e 'PHPRC=/proc/self/environ' php:7.3 php /dev/null
HOSTNAME=1;
auto_prepend_file=/proc/self/environ
;uid=0(root) gid=0(root) groups=0(root)


Rubis



Aucune solution universelle pour Ruby n'a encore été trouvée. Ruby accepte une variable d'environnement RUBYOPTpour spécifier les options de ligne de commande. La page de manuel indique que RUBYOPT ne peut contenir que des fichiers -d, -E, -I, -K, -r, -T, -U, -v, -w, -W, --debug, --disable-FEATURE --enable-FEATURE. L'option la plus prometteuse est de -rforcer Ruby à charger la bibliothèque en utilisant require. Cependant, cela est limité aux fichiers avec l'extension .rbou .so.



Un exemple de fichier relativement utile que j'ai trouvé .rbprovient tools/server.rbdu gem json, qui est disponible après l'installation de Ruby sur les systèmes Fedora. Lorsque ce fichier est requis, le serveur Web est démarré comme indiqué ci-dessous:



Exemple 7: Utilisation de la variable d'environnement RUBYOPT pour démarrer le processus ruby ​​et démarrer le serveur Web



$ docker run -it --env 'RUBYOPT=-r/usr/share/gems/gems/json-2.3.0/tools/server.rb' fedora:33 /bin/bash -c 'dnf install -y ruby 1>/dev/null; ruby /dev/null'
Surf to:
http://27dfc3850fbe:6666
[2020-06-17 05:43:47] INFO  WEBrick 1.6.0
[2020-06-17 05:43:47] INFO  ruby 2.7.1 (2020-03-31) [x86_64-linux]
[2020-06-17 05:43:47] INFO  WEBrick::HTTPServer#start: pid=28 port=6666


Une autre approche de Fedora consiste à tirer parti du fait qu'il /usr/bin/rubyexiste en fait un script Bash qui se lance /usr/bin/ruby-mri. Le script appelle des fonctions Bash qui peuvent être écrasées par des variables d'environnement.



Preuve de concept



Exemple 8: Utilisation d'une fonction Bash exportée pour exécuter une commande arbitraire



$ docker run --env 'BASH_FUNC_declare%%=() { id; exit; }' fedora:33 /bin/bash -c 'dnf install ruby -y 1>/dev/null; ruby /dev/null'
uid=0(root) gid=0(root) groups=0(root)


Conclusion



Cet article a examiné quelques cas d'utilisation intéressants pour les variables d'environnement qui pourraient aider à exécuter du code arbitraire via divers interpréteurs de langage de script sans écrire de fichiers sur le disque. J'espère que vous avez aimé lire ceci et que vous vouliez trouver et partager des charges utiles améliorées pour ces langages de script et d'autres. Si vous trouvez une technique générique qui fonctionne contre Ruby, il sera très intéressant d'en entendre parler.



Voir aussi: " Dotfile Madness "



All Articles