python模板注入_BUUCTF/护网杯 easy_tornado 模板注入
首先简单认识一下模板注入
模板注入涉及的是服务端Web应用使用模板引擎渲染用户请求的过程,这里我们使用 PHP 模版引擎 Twig 作为例子来说明模板注入产生的原理。考虑下面这段代码:
require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php'; Twig_Autoloader::register(true); $twig= new Twig_Environment(new Twig_Loader_String()); $output= $twig->render("Hello {{name}}", array("name" => $_GET["name"])); // 将用户输入作为模版变量的值 echo $output;
?>
使用 Twig 模版引擎渲染页面,其中模版含有 {{name}} 变量,其模版变量值来自于 GET 请求参数 $_GET["name"] 。显然这段代码并没有什么问题,即使你想通过 name 参数传递一段 JavaScript 代码给服务端进行渲染,也许你会认为这里可以进行 XSS,但是由于模版引擎一般都默认对渲染的变量值进行编码和转义,所以并不会造成跨站脚本攻击:
但是,如果渲染的模版内容受到用户的控制,情况就不一样了。修改代码为:
require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php'; Twig_Autoloader::register(true); $twig= new Twig_Environment(new Twig_Loader_String()); $output= $twig->render("Hello {$_GET['name']}"); // 将用户输入作为模版内容的一部分 echo $output;
上面这段代码在构建模版时,拼接了用户输入作为模板的内容,现在如果再向服务端直接传递 JavaScript 代码,用户输入会原样输出,测试结果显而易见:
对比上面两种情况,简单的说服务端模板注入的形成终究还是因为服务端相信了用户的输出而造成的(Web安全真谛:永远不要相信用户的输入!)。
模板注入检测
上面已经讲明了模板注入的形成原来,现在就来谈谈对其进行检测和扫描的方法。如果服务端将用户的输入作为了模板的一部分,那么在页面渲染时也必定会将用户输入的内容进行模版编译和解析最后输出。
借用本文第二部分所用到的代码:
require_once dirname(FILE).'/../lib/Twig/Autoloader.php'; Twig_Autoloader::register(true);
output=
_GET['name']}"); // 将用户输入作为模版内容的一部分 echo $output;
在 Twig 模板引擎里,{{ var }} 除了可以输出传递的变量以外,还能执行一些基本的表达式然后将其结果作为该模板变量的值,例如这里用户输入 name={{2*10}} ,则在服务端拼接的模版内容为:
Hello {{2*10}}
Twig 模板引擎在编译模板的过程中会计算 {{210}} 中的表达式 210 ,会将其返回值 20 作为模板变量的值输出,如下图:
现在把测试的数据改变一下,插入一些正常字符和 Twig 模板引擎默认的注释符,构造 Payload 为:
IsVuln{# comment #}{{2*8}}OK
实际服务端要进行编译的模板就被构造为:
Hello IsVuln{# comment #}{{2*8}}OK
这里简单分析一下,由于 {# comment #} 作为 Twig 模板引擎的默认注释形式,所以在前端输出的时候并不会显示,而 {{2*8}} 作为模板变量最终会返回 16 作为其值进行显示,因此前端最终会返回内容 Hello IsVuln16OK ,如下图:
重点来了,不同引擎有不同的测试以及注入方式!
模板注入
flask/jinja2模板注入
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2
Flask框架中提供的模版引擎可能会被一些无量开发者利用引入一个服务端模版注入漏洞,如果对此感到有些困惑可以看看James Kettle在黑帽大会中分享的议题(PDF),简而言之这个漏洞允许将语言/语法注入到模板中。在服务器的context中执行这个输入重现,根据应用的context可能导致任意远程代码执行(远端控制设备)
PHP/模版引擎Twig注入
可以参考本博客文章 Flask从零到无 。
这里给出一个漏洞环境代码,本地测试
from flask import Flask
from flask import render_template
from flask import request
from flask import render_template_string
app = Flask(__name__)
@app.route('/test',methods=['GET', 'POST'])
def test():
template = '''
Oops! That page doesn't exist.
%s
''' %(request.url)
return render_template_string(template)
if __name__ == '__main__':
app.debug = True
app.run()
代码简析: 我们自己简单写一个string类型的 html,html返回当前url,我们放入到渲染函数render_template_string进行渲染,然后页面会打印出当前url,如果url里含有{{}} 那么便可以进行模板注入。
测试结果如下:
image
而如果我们使用render_template函数,
@app.route('/',methods=['GET', 'POST'])
@app.route('/index',methods=['GET', 'POST'])#我们访问/或者/index都会跳转
def index():
return render_template("index.html",title='Home',user=request.args.get("key"))
index.html
{{title}} - 小猪佩奇
Hello, {{user}}!
那么将不会有模板注入,因为render_template已经传入一个固定好了的模板,没法再去修改,在渲染之后传入数据,只有当第一种代码,我们模板可控的时候,先传入后渲染,这样才会导致ssti模板注入。
CTF 题目
这个tornado是一个python的模板,在web使用的时候给出了四个文件,可以访问,从提示中和url中可以看出,访问需要文件名+文件签名(长度为32位,计算方式为md5(cookie_secret + md5(filename))); flag文件名题目已给出 /fllllllllllag
题目关键为如何获取cookie,在Bp抓包的情况下没有显示cookie,由于是python的一个模板,首先想到的就是模板注入{{}},最终找到的位置是报错网页(随便访问一个文件是更改它的签名就可以进入),里面的参数msg
该处将原有参数替换可以执行模板注入msg={{XXXXX}},需要注意,这里过滤了大多数奇怪的字符,并且跟以往的题目不同的是,这里不需要python的基类再寻找子函数,而是直接获取环境的变量。
该思想来源于题目的提示render,render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,简单的理解例子如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.web import UIModule
from tornado import escape
class custom(UIModule):
def render(self, *args, **kwargs):
return escape.xhtml_escape('
wupeiqi
')
#return escape.xhtml_escape('
wupeiqi
')
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
class LoginHandler(BaseHandler):
def get(self):
'''
当用户访登录的时候我们就得给他写cookie了,但是这里没有写在哪里写了呢?
在哪里呢?之前写的Handler都是继承的RequestHandler,这次继承的是BaseHandler是自己写的Handler
继承自己的类,在类了加扩展initialize! 在这里我们可以在这里做获取用户cookie或者写cookie都可以在这里做
'''
'''
我们知道LoginHandler对象就是self,我们可不可以self.set_cookie()可不可以self.get_cookie()
'''
# self.set_cookie()
# self.get_cookie()
self.render('login.html', **{'status': ''})
def login(request):
#获取用户输入
login_form = AccountForm.LoginForm(request.POST)
if request.method == 'POST':
#判断用户输入是否合法
if login_form.is_valid():#如果用户输入是合法的
username = request.POST.get('username')
password = request.POST.get('password')
if models.UserInfo.objects.get(username=username) and models.UserInfo.objects.get(username=username).password == password:
request.session['auth_user'] = username
return redirect('/index/')
else:
return render(request,'account/login.html',{'model': login_form,'backend_autherror':'用户名或密码错误'})
else:
error_msg = login_form.errors.as_data()
return render(request,'account/login.html',{'model': login_form,'errors':error_msg})
# 如果登录成功,写入session,跳转index
return render(request, 'account/login.html', {'model': login_form})
我们大概可以看出来,render是一个类似模板的东西,可以使用不同的参数来访问网页。那么我们在进行该题目的操作时,其实参数也是传递过来的,那么是什么参数呢。
在tornado模板中,存在一些可以访问的快速对象,例如
{{ escape(handler.settings["cookie"]) }}
这两个{{}}和这个字典对象也许大家就看出来了,没错就是这个handler.settings对象,又黑翼天使23的博客园日志可知,
handler 指向RequestHandler
而RequestHandler.settings又指向self.application.settings
所有handler.settings就指向RequestHandler.application.settings了!
大概就是说,这里面就是我们一下环境变量,我们正是从这里获取的cookie_secret
而后使用在线的或者python的计算一下就可以
import hashlib
def md5value(s):
md5 = hashlib.md5()
md5.update(s.encode())
return md5.hexdigest()
def mdfive2():
filename = '/fllllllllllllag'
cookie = r"M)Z.>}{O]lYIp(oW7$dc132uDaK
#print(md5value(filename))
# print(md5value('*c].)Y!x%+WgjHbvfM@[U'))
# print(''+md5value(filename))
print(md5value(cookie + md5value(filename)))#hints md5(cookie_secret+md5(filename))
mdfive2()
image.png
python模板注入_BUUCTF/护网杯 easy_tornado 模板注入相关推荐
- [护网杯 2018]easy_tornado WriteUp
打开环境发现有三个链接 分别点击 分别得到 flag.txt welcome.txt 以及hints.txt 看到网址后面有 file/filename= 等字样,以及出现的md5加密函数 初步猜测f ...
- python语言的格式框架_django框架模板语言使用方法详解
本文实例讲述了django框架模板语言使用方法.分享给大家供大家参考,具体如下: 模板功能 作用:生成html界面内容,模版致力于界面如何显示,而不是程序逻辑.模板不仅仅是一个html文件,还包括了页 ...
- python之路_django路由配置及模板
URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调 ...
- python在docx指定位置插表格_超简单Python将指定数据插入到docx模板指定位置渲染并保存...
超简单Python将指定数据插入到docx模板渲染并生成 最近有一个需求,制作劳动合同表,要从excel表格中将每个人的数据导入到docx劳动合同中,重复量很大,因此可以使用python高效解决.为了 ...
- 基于Python的Web应用开发实战——3 模板
要想开发出易于维护的程序,关键在于编写形式简洁且结构良好的代码. 当目前为止,你看到的示例都太简单,无法说明这一点,但Flask视图函数的两个完全独立的作用却被融合在了一起,这就产生了一个问题. 视图 ...
- Python网站导航项目-4.前端渲染模板
每每用到别人的导航网站会充斥的各种的广告,以及很多无用的内容,用起来真的很烦人.把内容网址收藏到浏览器中又很不方便,因此基于git的前端代码结合Django Web开发自制一套简易的导航网站项目. 项 ...
- Python+HTML随机点名抽奖源码模板
介绍: 一款不错的Python+HTML随机点名抽奖源码模板,带背景音乐,bootstrap框架自适应手机端,随机数据在others.js里面增加修改. 网盘下载地址: http://kekewl.c ...
- Python Flask Web教程020: flask模板
flask学习记录004 0. 模板注释: 1. 模板可传递变量 2. 模板控制语法:控制块 if ,for 3. 模板过滤器 4. 模板复用 一.模板继承 二.include 三.宏(mcro) 5 ...
- python 会计专用格式_excel会计专用格式模板下载|excel会计专用格式模板680个 最新版 下载 - 巴士下载站...
680个excel会计专用格式模板里面包含了很多种类别的Excel表格模板,专门供给财务使用,主要是帮你自动生成表格,节省了你不少时间,欢迎在WWW.11684.COM 下载! 软件特色 1.涵盖了常 ...
最新文章
- Android实现版本更新提示
- ***CSS魔法堂:选择器及其优先级
- Hough 检测直线
- 自然语言处理笔记2-哈工大 关毅
- Android的服务(Service)(三)Service客户端的绑定与跨进程
- BZOJ-2324 营救皮卡丘 最小费用可行流+拆下界+Floyd预处理
- 11/100. Convert BST to Greater Tree
- 操作失败10秒内未完成启动服务mysql_01-MySQL 命令行-cmd用法-未完成
- python3 正则表达式 嵌套表格_python 正则表达式处理表格
- jQuery之.queue()
- python删除数据库的数据完整代码_轻松掌握Python对数据库的增、删、改、查
- (转自珊珊博客)甜甜蜜蜜走台湾 Day3
- 开发一个app需要多少钱、APP开发需要投入多少资金?
- Twitter在15岁终于迎来蜕变
- 【读点论文】EfficientFormer: Vision Transformers at MobileNet Speed,运用纯transformer架构对比卷积模型在终端上部署的推理速度
- java接口里面可以定义变量么?
- 【基础算法】试除法求约数(Acwing869题)
- broyden matlab,Broyden方法求解非线性方程组的Matlab实现
- 电动机的故障听声音就可以判断
- 你只管粘贴。复制我帮你解决
热门文章
- 支付宝手机网站支付开发记录之结果异步通知
- Jasper Report 6.8 根据后台数据生成动态报表(JRXML文件实现)(二)生成XML文件(支持json,bean,map list数据源)
- bugku Simple_SSTI_1and 2(SSTI模板注入)
- ssh2项目之自己想的购物网站
- 07-白盒测试方法-逻辑覆盖法
- 为什么cleancode
- VTP-------详解
- 【《Multimodal Transformer for Unaligned Multimodal Language Sequences》论文翻译】
- 数据通信基础(1)-数据通信概念、通信系统模型及通信方式
- JavaScript中的标签语句