Flask Pin码构造详解
字数 2429 2025-08-26 22:11:22

Flask Pin码构造详解

1. 背景介绍

Flask框架在开启debug模式时,会提供一个交互式调试控制台(/console),访问该控制台需要输入PIN码。这个PIN码由多个系统参数生成,了解其生成机制可以帮助我们在CTF比赛或渗透测试中利用这一特性。

2. PIN码生成参数

PIN码主要由六个参数构成,分为两组:

2.1 probably_public_bits (公开参数)

  1. username: 运行当前程序的用户名

    • 获取方式:通过/etc/passwd文件读取
    • 示例:glzjin
  2. modname: 当前对象的模块名

    • 获取方式:取app对象的__module__属性,不存在则取类的__module__属性
    • 默认值:flask.app
  3. getattr(app, "name", app.class.name): 当前对象的名称

    • 获取方式:取app对象的__name__属性,不存在则取类的__name__属性
    • 默认值:Flask
  4. getattr(mod, "file", None): flask包内app.py的绝对路径

    • Python2: app.pyc
    • Python3: app.py
    • 默认路径:/usr/local/lib/python{版本号}/site-packages/flask/app.py

2.2 private_bits (私有参数)

  1. str(uuid.getnode()): 当前网卡的物理地址的整型

    • 获取方式:
      • 通过/sys/class/net/eth0/address读取MAC地址
      • 转换为整型:int(MAC, 16)
    • 示例:92:a0:2e:1e:8d:52160881493234258
  2. get_machine_id(): 机器ID

    • 获取方式因系统而异:

2.2.1 Docker容器

  • 读取/proc/self/cgroup
  • 处理方式:
    • 0.15.5之前:value.strip().partition("/docker/")[2]
    • 0.16.0之后:f.readline().strip().rpartition(b"/")[2]

2.2.2 Linux系统

  1. /etc/machine-id (固定)
  2. /proc/sys/kernel/random/boot_id (每次开机重新生成)

2.2.3 macOS系统

  • 执行命令:ioreg -c IOPlatformExpertDevice -d 2
  • 提取"serial-number"部分:"serial-number" = <{ID}>

2.2.4 Windows系统

  • 读取注册表:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid

3. PIN码生成算法

3.1 生成流程

  1. 将六个参数合并为一个列表
  2. 使用hashlib进行哈希计算
    • 0.15.5之前:MD5
    • 2.0.0之后:SHA1
  3. 将哈希结果转换为9位数字
  4. 按3-4-5或4-5分组格式显示

3.2 代码实现

import hashlib
from itertools import chain

def getMd5Pin(probably_public_bits, private_bits):
    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 = ('%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 getSha1Pin(probably_public_bits, private_bits):
    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")
    num = f"{int(h.hexdigest(), 16):09d}"[: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

4. 历史版本变化

版本/日期 重要变更
2019-5-15之前 仅支持Linux/Mac/Windows系统,不考虑Docker
2019-5-15 添加对Docker容器的machine-id获取支持
2019-7-17 (0.15.5) 修改Docker容器的machine-id正则表达式
2020-1-5 调整容器ID获取顺序
2021-1-18 (2.0.0) 将MD5加密方式改为SHA1

5. 实战应用示例

以CISCN2019华东南赛区web4题为例:

  1. 收集参数:

    • username: glzjin (从/etc/passwd获取)
    • modname: flask.app (默认)
    • appname: Flask (默认)
    • path: /usr/local/lib/python2.7/site-packages/flask/app.pyc
    • MAC地址: 92:a0:2e:1e:8d:52160881493234258
    • machine-id: 0e5d30fa-26d7-42e1-a736-fc8a2e419c51 (从/proc/sys/kernel/random/boot_id获取)
  2. 生成PIN码:

    probably_public_bits = [
        'glzjin',
        'flask.app',
        'Flask',
        '/usr/local/lib/python2.7/site-packages/flask/app.pyc'
    ]
    private_bits = [
        '160881493234258',
        '0e5d30fa-26d7-42e1-a736-fc8a2e419c51'
    ]
    print(getMd5Pin(probably_public_bits, private_bits))
    
  3. 访问调试控制台:

    • 访问/console
    • 输入生成的PIN码

6. 总结要点

  1. 参数获取顺序:

    • 0.15.5之前: /etc/machine-id/proc/sys/kernel/random/boot_idioreg → 注册表
    • 0.16.0之后: /etc/machine-id/proc/sys/kernel/random/boot_id/proc/self/cgroupioreg → 注册表
  2. 加密方式:

    • Python2: 绝大部分为MD5
    • Python3: 少部分为MD5,大部分为SHA1
  3. 关键文件路径:

    • /etc/passwd - 用户名
    • /sys/class/net/eth0/address - MAC地址
    • /proc/self/cgroup - Docker容器ID
    • /etc/machine-id - Linux机器ID
    • /proc/sys/kernel/random/boot_id - Linux启动ID
  4. 调试技巧:

    • 通过报错页面获取flask路径
    • 使用脚本自动化生成PIN码
    • 注意Python版本差异(pyc vs py)
Flask Pin码构造详解 1. 背景介绍 Flask框架在开启debug模式时,会提供一个交互式调试控制台(/console),访问该控制台需要输入PIN码。这个PIN码由多个系统参数生成,了解其生成机制可以帮助我们在CTF比赛或渗透测试中利用这一特性。 2. PIN码生成参数 PIN码主要由六个参数构成,分为两组: 2.1 probably_ public_ bits (公开参数) username : 运行当前程序的用户名 获取方式:通过 /etc/passwd 文件读取 示例: glzjin modname : 当前对象的模块名 获取方式:取app对象的 __module__ 属性,不存在则取类的 __module__ 属性 默认值: flask.app getattr(app, " name ", app. class . name ) : 当前对象的名称 获取方式:取app对象的 __name__ 属性,不存在则取类的 __name__ 属性 默认值: Flask getattr(mod, " file ", None) : flask包内app.py的绝对路径 Python2: app.pyc Python3: app.py 默认路径: /usr/local/lib/python{版本号}/site-packages/flask/app.py 2.2 private_ bits (私有参数) str(uuid.getnode()) : 当前网卡的物理地址的整型 获取方式: 通过 /sys/class/net/eth0/address 读取MAC地址 转换为整型: int(MAC, 16) 示例: 92:a0:2e:1e:8d:52 → 160881493234258 get_ machine_ id() : 机器ID 获取方式因系统而异: 2.2.1 Docker容器 读取 /proc/self/cgroup 处理方式: 0.15.5之前: value.strip().partition("/docker/")[2] 0.16.0之后: f.readline().strip().rpartition(b"/")[2] 2.2.2 Linux系统 /etc/machine-id (固定) /proc/sys/kernel/random/boot_id (每次开机重新生成) 2.2.3 macOS系统 执行命令: ioreg -c IOPlatformExpertDevice -d 2 提取"serial-number"部分: "serial-number" = <{ID}> 2.2.4 Windows系统 读取注册表: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid 3. PIN码生成算法 3.1 生成流程 将六个参数合并为一个列表 使用hashlib进行哈希计算 0.15.5之前:MD5 2.0.0之后:SHA1 将哈希结果转换为9位数字 按3-4-5或4-5分组格式显示 3.2 代码实现 4. 历史版本变化 | 版本/日期 | 重要变更 | |----------|----------| | 2019-5-15之前 | 仅支持Linux/Mac/Windows系统,不考虑Docker | | 2019-5-15 | 添加对Docker容器的machine-id获取支持 | | 2019-7-17 (0.15.5) | 修改Docker容器的machine-id正则表达式 | | 2020-1-5 | 调整容器ID获取顺序 | | 2021-1-18 (2.0.0) | 将MD5加密方式改为SHA1 | 5. 实战应用示例 以CISCN2019华东南赛区web4题为例: 收集参数 : username: glzjin (从 /etc/passwd 获取) modname: flask.app (默认) appname: Flask (默认) path: /usr/local/lib/python2.7/site-packages/flask/app.pyc MAC地址: 92:a0:2e:1e:8d:52 → 160881493234258 machine-id: 0e5d30fa-26d7-42e1-a736-fc8a2e419c51 (从 /proc/sys/kernel/random/boot_id 获取) 生成PIN码 : 访问调试控制台 : 访问 /console 输入生成的PIN码 6. 总结要点 参数获取顺序 : 0.15.5之前: /etc/machine-id → /proc/sys/kernel/random/boot_id → ioreg → 注册表 0.16.0之后: /etc/machine-id → /proc/sys/kernel/random/boot_id → /proc/self/cgroup → ioreg → 注册表 加密方式 : Python2: 绝大部分为MD5 Python3: 少部分为MD5,大部分为SHA1 关键文件路径 : /etc/passwd - 用户名 /sys/class/net/eth0/address - MAC地址 /proc/self/cgroup - Docker容器ID /etc/machine-id - Linux机器ID /proc/sys/kernel/random/boot_id - Linux启动ID 调试技巧 : 通过报错页面获取flask路径 使用脚本自动化生成PIN码 注意Python版本差异(pyc vs py)