code-breaking picklecode中对signed_cookies引擎分析
最近做了 ph 牛的 code-breaking,在做 picklecode 这一题时,没有搞懂那个 django 的 signed_cookies 引擎对 session 的操作,就 debug 了一下,做个总结,算是做了个代码审计吧
原文 : https://xz.aliyun.com/t/6265
0x01 获取 session_auth_hash
题目 : https://github.com/phith0n/code-breaking/tree/master/2018/picklecode
django 使用的 SESSION_ENGINE 为
django.contrib.sessions.backends.signed_cookiespycharm 开启 debug 模式,username 为 peri0d,password 为 123456
入口文件在
views.py,第 34 行新建了用户并对密码进行了加密。第 35 行调用auth_login()函数,跳转到auth\__init__.py的login()方法
第 97 行,调用
user类的get_session_auth_hash()方法来获取session_auth_hash的值,跟进get_session_auth_hash()
给
key_salt赋值后调用salted_hmac(key_salt, self.password)生成session_auth_hash,这里的password是经过加密的,跟进salted_hmac()
在第 39 行对
key_salt + secret进行 sha1 加密并以 byte 类型返回给key。这里的value是经过加密后的password。然后调用hmac.new()返回一个 sha1 模式的hmac对象
流程梳理
key_salt = '***'
# SECRET_KEY
secret = '******'
key = hashlib.sha1(key_salt + secret).digest()
sha1_obj = hmac.new(key, msg=password_enc, digestmod=hashlib.sha1)
session_auth_hash = sha1_obj.hexdigest()
0x02 初始化 sessionid
获取
session_auth_hash后,单步调试,进入base.py执行__contains__()函数,参数为_auth_user_id
单步调试,然后执行
_get_session()函数,返回缓存 session,是一个空字典
在第 108 行执行
cycle_key(),使用新密钥保存相同的数据,调用save(),它在请求结束时自动保存一个带有新密钥的 cookie 。

跟进
save(),在第 41 行执行_get_session_key(),生成一个 base64 编码后的字符串作为session key,继续跟进,它又调用了signing.dumps()

然后单步调试进入到
_get_session()方法获取self._session,从缓存中加载 session,此时为一个空字典,即self._session == {}
然后分别给 compress,salt,serializer 赋值,然后调用
signing.dumps(),继续跟进,传入的参数obj = {}, salt = 'django.contrib.sessions.backends.signed_cookies', compress = True
在
signing.dumps()中对序列化之后的数据进行压缩,然后进行 base64 编码,再decode()为一个 Unicode 的base64d,其值为'gAN9cQAu',最后调用TimestampSigner类的sign()方法,继续跟进TimestampSigner类继承自Signer类,先调用它的__init__方法进行初始化,key = 'zs%o-mvuihtk6g4pgd+xpa&1hh9%&ulnf!@9qx8_y5kk+7^cvm', sep = ':', salt = 'django.contrib.sessions.backends.signed_cookies'

然后调用
TimestampSigner类的sign()方法,根据value='gAN9cQAu',sep和timestamp()对value进行重新赋值,其值为'gAN9cQAu:1i5q6e',然后再次在Signer.sign()中重新赋值,得到最后结果'gAN9cQAu:1i5q6e:wjJR2MUONx_wmPA3m8zYqTj5uCQ

回到
save(),继续单步调试,调用了base.py中第 170 行的_set_session_key()方法,将value赋值给session_key和_session_key
回到
save(),完成赋值,回到cycle_key(),再回到auth\__init__.py的login()方法的第 108 行,这时可以在变量列表看到设置的 session 信息了
后面的代码是 django 对用户的持久化处理以及对 CSRF token 的验证等等,值得注意的是在第 126 行到 128 行,进行了 session 设置


流程梳理
_session = {}
# SECRET_KEY
secret = '******'
salt='****' data = serializer().dumps(_session)
compressed = zlib.compress(data)
base64d = b64_encode(data).decode() session_key = TimestampSigner(SECRET_KEY, salt=salt).sign(base64d)
0x03 response 写入 session
然后看它如何在 response 中设置 cookie 的,继续调试,在
contrib\sessions\middleware.py中发现其对 cookie 的操作,从 44 行开始是设置 cookie 的存活时间,在第 58 行看到了save()函数,进行 cookie 的保存,单步调试进入

在 save() 函数中,调用
_get_session_key()函数,剩下的反序列化和前面的相同,只是 session 的值发生了改变,从空字典变为含有 3 个元素的字典,然后就是将 cookie 设置在返回包中,这就完成了 cookie 设置的分析


0x04 总结
总结一下,它对 session 处理的核心机制在于
django.core.signing.dumps()函数,其具体代码如下,可以看到,data为 pickle 序列化之后的 byte 对象,我们只要将data改为构造好的 evil pickle code 即能实现任意的代码执行def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False): data = serializer().dumps(obj) is_compressed = False if compress:
compressed = zlib.compress(data)
if len(compressed) < (len(data) - 1):
data = compressed
is_compressed = True
base64d = b64_encode(data).decode()
if is_compressed:
base64d = '.' + base64d
return TimestampSigner(key, salt=salt).sign(base64d)
code-breaking picklecode中对signed_cookies引擎分析的更多相关文章
- jQuery中的Sizzle引擎分析
我分析的jQuery版本是1.8.3.Sizzle代码从3669行开始到5358行,将近2000行的代码,这个引擎的版本还是比较旧,最新的版本已经到v2.2.2了,代码已经超过2000行了.并且还有个 ...
- MySQL中innodb引擎分析(初始化)
MySQL的存储引擎是以插件形式工作的,这应该是MySQL的一大特色了吧! 依据<深入理解MySQL>的内容,5.1版本号时存储引擎的插件化都还不是彻底,确切的说是刚加入的特性.为MySQ ...
- 【原】 Spark中Worker源码分析(二)
继续前一篇的内容.前一篇内容为: Spark中Worker源码分析(一)http://www.cnblogs.com/yourarebest/p/5300202.html 4.receive方法, r ...
- 在QT中使用Irrlicht引擎的方法与步骤
Ø 相关库,插件安装部分 本篇文档介绍在Qt5.2.0下面使用lrrlicht引擎在Qt窗口中输出(开发环境:vs2012) 1. 首先安装好Qt5.2.0,下载地址: http://downlo ...
- Express ( MiddleWare/中间件 路由 在 Express 中使用模板引擎 常用API
A fast, un-opinionated, minimalist web framework for Node.js applications. In general, prefer simply ...
- MySql中innodb存储引擎事务日志详解
分析下MySql中innodb存储引擎是如何通过日志来实现事务的? Mysql会最大程度的使用缓存机制来提高数据库的访问效率,但是万一数据库发生断电,因为缓存的数据没有写入磁盘,导致缓存在内存中的数据 ...
- equals和==方法比较(二)--Long中equals源码分析
接上篇,分析equals方法在Long包装类中的重写,其他类及我们自定义的类,同样可以根据需要重新equals方法. equals方法定义 equals方法是Object类中的方法,java中所有的对 ...
- AI框架中图层IR的分析
摘要:本文重点分析一下AI框架对IR有什么特殊的需求.业界有什么样的方案以及MindSpore的一些思考. 本文分享自华为云社区<MindSpore技术专栏 | AI框架中图层IR的分析> ...
- 实时营销引擎在vivo营销自动化中的实践 | 引擎篇04
作者:vivo 互联网服务器团队 本文是<vivo营销自动化技术解密>的第5篇文章,重点分析介绍在营销自动化业务中实时营销场景的背景价值.实时营销引擎架构以及项目开发过程中如何利用动态队列 ...
随机推荐
- BFS与DFS常考算法整理
BFS与DFS常考算法整理 Preface BFS(Breath-First Search,广度优先搜索)与DFS(Depth-First Search,深度优先搜索)是两种针对树与图数据结构的遍历或 ...
- prometheus远程写参数优化
一.概述 prometheus可以通过远程存储来解决自身存储的瓶颈,所以其提供了远程存储接口,并可以通过过配置文件进行配置(prometheus.yml).一般情况下我们使用其默认的配置参数,但是为了 ...
- 知识图谱里的知识存储:neo4j的介绍和使用
一般情况下,我们使用数据库查找事物间的联系的时候,只需要短程关系的查询(两层以内的关联).当需要进行更长程的,更广范围的关系查询时,就需要图数据库的功能. 而随着社交.电商.金融.零售.物联网等行 ...
- PyTorch Hub发布!一行代码调用最潮模型,图灵奖得主强推
为了调用各种经典机器学习模型,今后你不必重复造轮子了. 刚刚,Facebook宣布推出PyTorch Hub,一个包含计算机视觉.自然语言处理领域的诸多经典模型的聚合中心,让你调用起来更方便. 有多方 ...
- Vue2.0 -- 钩子函数/ 过度属性/常用指令/以及Vue-resoure发送请求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- postman设置全局变量及参数化
笔者第一次记录使用过程,仅供参考 测试过程中接口的前缀都是一样的,所以我们可以将这个前缀作为全局变量来使用 首先,打开postman点击这里的小齿轮设置 在这里就可以进行变量的一个添加,添加好之后记住 ...
- localStorage中一个数组嵌套一个数组的怪相
localStorage中一个数组嵌套一个数组的怪相 需求:向本地存储中循环添加对象 思路 : 先完成点击事件中添加本地存储功能,当刷新时使用一个数组记录已经存储下来的数据,并在点击事件中将新生成 ...
- java中的PO VO DAO BO POJO
一.PO:persistant object 持久对象,可以看成是与数据库中的表相映射的ava对象. 最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合PO中应该不包含任何对数据 ...
- SQL Server 创建链接服务器的脚本,自定义链路服务器的简短名称
USE [master]GO /****** Object: LinkedServer [SQL01] Script Date: 2020/4/9 11:51:17 ******/EXEC m ...
- c语言 0与非0
------------恢复内容开始------------ 结论: 其它的编程语言中有布尔数据类型,并用百来表示逻辑真和逻辑假,C语言没有这个内置类度型,在C语言中真和假是用整型值来表示知的,0就表 ...