GitHub Enterprise漏洞链:从SSRF到RCE的四个漏洞利用
大家好,我是Orange。距离我上次写博客已经有一段时间了。
在过去的几个月里,我花了很多时间准备Black Hat USA 2017和DEF CON 25的演讲。成为Black Hat和DEFCON的演讲者一直是我的人生目标之一。这也是我在如此正式的会议上第一次用英语演讲,真是一次难忘的经历:P
感谢评审委员会的认可。
这篇文章是我演讲中的一个简单案例研究。这里的技术并不新鲜,但我会向你展示这些老技巧有多么强大!如果你感兴趣,可以在这里查看幻灯片:
《SSRF的新时代 - 利用流行编程语言中的URL解析器!》
幻灯片涵盖了更强大的SSRF新方法以及本文未包含的其他技术。
在本文中,我将向你展示一个漂亮的漏洞利用链,它将4个漏洞串联起来,最终在GitHub Enterprise上实现了远程代码执行(RCE)。这个漏洞还获得了GitHub第三届漏洞赏金周年促销活动的"最佳报告"奖励!
前言
在我上一篇博客文章中,我提到了新目标 - GitHub Enterprise,并演示了如何反混淆Ruby代码并在其上找到SQL注入。之后,我看到几位漏洞赏金猎人开始关注GitHub Enterprise,并发现了许多惊人的漏洞,比如:
- ilektrojohn的《通往你代码库的道路铺满了伪造的断言》
- iblue的《GitHub Enterprise远程代码执行》
看到这些文章后,我有点沮丧,责怪自己为什么没有注意到这些。
因此,我下定决心要找到一个别人没有发现的关键漏洞。当然,是用我自己的方式!
漏洞
在检查GitHub Enterprise的架构之前,我的直觉告诉我,GitHub Enterprise内部有许多服务。如果我能与它们互动,我相信我能发现一些有趣的东西。
所以,我专注于寻找服务器端请求伪造(SSRF)漏洞。
第一个漏洞 - 无害的SSRF
在使用GitHub Enterprise时,我注意到一个有趣的功能叫做WebHook。它可以在特定的GIT命令发生时定义一个自定义的HTTP回调。
你可以从以下URL创建一个HTTP回调:
|
|
并通过提交文件来触发它。GitHub Enterprise会通过HTTP请求通知你。有效载荷和请求如下:
有效载荷URL:
|
|
回调请求:
|
|
GitHub Enterprise使用Ruby Gem faraday来获取外部资源,并通过Gem faraday-restrict-ip-addresses防止用户请求内部服务。
这个Gem似乎只是一个黑名单,可以通过RFC 3986中定义的罕见IP地址格式轻松绕过。在Linux中,0代表localhost。
PoC:
|
|
好了,我们现在有一个SSRF了。然而,我们仍然不能做任何事情。为什么?
这个SSRF有几个限制:
- 仅支持POST方法
- 仅允许HTTP和HTTPS方案
- 不支持302重定向
- 在faraday中无法进行CR-LF注入
- 无法控制POST数据和HTTP头
唯一可以控制的是路径部分。
但是,值得一提的是,这个SSRF可以导致拒绝服务(DoS)。
有一个Elasticsearch服务绑定在端口9200上。在shutdown命令中,Elasticsearch不关心POST数据是什么。因此,你可以玩它的REST-ful API来取乐:P
拒绝服务PoC:
|
|
第二个漏洞 - 内部Graphite中的SSRF
我们现在有一个SSRF,但有很多限制。我能做什么? 我的下一个想法是 - 有没有我们可以利用的内网服务?
这是一项大工程。内部有几个HTTP服务,每个服务基于不同的语言实现,如C/C++、Go、Python和Ruby…
经过几天的挖掘,我发现有一个叫做Graphite的服务运行在端口8000上。Graphite是一个高度可扩展的实时图形系统,GitHub使用这个系统向用户显示一些统计数据。
Graphite是用Python编写的,也是一个开源项目,你可以在这里下载源代码!
通过阅读源代码,我很快在这里发现了另一个SSRF。第二个SSRF很简单。
在文件webapps/graphite/composer/views.py中:
|
|
你可以看到Graphite接收用户输入的url并直接获取它!所以,我们可以使用第一个SSRF来触发第二个SSRF,并将它们组合成一个SSRF执行链。
SSRF执行链有效载荷:
|
|
第二个SSRF的请求:
|
|
好了,我们成功地将基于POST的SSRF变成了基于GET的SSRF。但仍然不能做任何事情。
让我们进入下一阶段!
第三个漏洞 - Python中的CR-LF注入
如你所见,Graphite使用Python的httplib.HTTPConnection来获取资源。经过一些尝试和错误,我注意到httplib.HTTPConnection中存在CR-LF注入。因此,我们可以在HTTP协议中嵌入恶意负载。
CR-LF注入PoC:
|
|
|
|
这是一小步,但对整个漏洞利用链来说是一个巨大的飞跃。现在,我可以在这个SSRF执行链中走私其他协议。例如,如果我们想玩Redis,可以尝试以下有效载荷:
|
|
注意:SLAVEOF是一个非常棒的命令,可以让你产生出站流量。当面对一些盲SSRF时,这是一个有用的技巧!
看起来很棒!然而,协议走私也有一些限制:
- 需要握手的协议如SSH、MySQL和SSL会失败
- 由于Python2的限制,我们在第二个SSRF中使用的有效载荷只允许0x00到0x8F的字节
顺便说一下,在HTTP方案中走私协议的方法不止一种。在我的幻灯片中,我还展示了如何利用Linux Glibc的特性通过SSL SNI走私协议,以及一个绕过Python CVE-2016-5699的案例研究!
如果你感兴趣,可以查看一下 :)
第四个漏洞 - 不安全的反序列化
现在,我们有了在HTTP协议中走私其他协议的能力,但下一个问题是,我选择走私什么协议?
我花了很多时间研究如果我能控制Redis或Memcached,可以触发什么漏洞。
在审查源代码时,我很好奇为什么GitHub可以在Memcached中存储Ruby对象。经过一些挖掘,我发现GitHub Enterprise使用Ruby Gem memcached来处理缓存,并且缓存是用Marshal包装的。
这对我来说是个好消息。每个人都知道Marshal是危险的。
(如果你不知道,我推荐你阅读@frohoff和@gebl在AppSec California 2015上的幻灯片《Marshalling Pickles》)
所以,我们的目标很明确。
我们使用SSRF执行链在Memcached中存储恶意的Ruby对象。下次GitHub获取缓存时,Ruby Gem memcached会自动反序列化数据。结果是… BOOM!远程代码执行!XD
Rails控制台中的不安全Marshal:
|
|
好了,让我们总结一下我们的步骤!
- 第一个SSRF - 绕过Webhook中的现有保护
- 第二个SSRF - Graphite服务中的SSRF
- 将第一个SSRF和第二个SSRF串联成SSRF执行链
- SSRF执行链中的CR-LF注入
- 作为Memcached协议走私并插入恶意的Marshal对象
- 触发RCE
漏洞利用概览
最终的漏洞利用可以在Gist上找到,视频可以在Youtube上观看。
修复
GitHub已经做了许多改进来防止相关问题再次发生!
- 增强了Gem faraday-restrict-ip-addresses
- 应用了自定义的Django中间件,确保攻击者无法访问http://127.0.0.1:8000/render/之外的路径
- 增强了iptables规则,阻止带有User-Agent: GitHub-Hookshot模式的访问
|
|
时间线
- 2017/01/23 23:22 通过HackerOne向GitHub报告漏洞,分配报告编号200542
- 2017/01/23 23:37 GitHub将状态更改为Triaged。
- 2017/01/24 04:43 GitHub回应问题已验证并正在修复。
- 2017/01/31 14:01 发布GitHub Enterprise 2.8.7。
- 2017/02/01 01:02 GitHub回应此问题已修复!
- 2017/02/01 01:02 GitHub奖励7,500美元赏金!
- 2017/03/15 02:38 GitHub奖励5,000美元最佳报告奖金。