軟件(jiàn)開(kāi)發Java反射庫中的(de)安全漏洞在30個(gè)月(yuè)後終于修複了(✘$₽¥le),2013年(nián)7月(yuè),安全組織Sec₽↕urity Explorations發現(xiàn)了(leγ≠)Java 7u25中的(de)一(yī)個(gè)安全漏洞,通(t¶<↑ōng)過這(zhè)個(gè)漏洞攻擊者可(kě)以完全擺脫 ¶¶Java沙箱。Oracle在更新的(de)7u40中包÷★ ₩含了(le)一(yī)個(gè)補丁,但(dàn)是(shì)據Securit↓♦<y Explorations 在今年(nián)早些(xiē)時(shí£↕∞←)候聲稱,這(zhè)個(gè)補丁僅僅在理(lǐ $£")念上(shàng)對(duì)其進行(xíng)了(le)✘>修正,對(duì)代碼稍加修改之後,依然可(kě)以利用(→yòng)這(zhè)個(gè)漏洞。另外(wài),随後的(dγ☆÷✘e)研究表明(míng)這(zhè)個(gè)漏洞甚至比較初報(<↕¶∑bào)道(dào)的(de)更加嚴重。在這(zhè)個(gè)問(Ωφλwèn)題公開(kāi)之後,Oracle發布了(l♦$e)一(yī)個(gè)補丁,作(zuò)為(wèi)8u7φ<✘♠7的(de)一(yī)部分(fēn)。
這(zhè)個(gè)漏洞可(kě)以在新的(d∞•₹e)反射庫中找到(dào),該庫從(cóng)Java<<± 7以後均可(kě)以使用(yòng),更具體(♣≈αtǐ)來(lái)講,是(shì)在使用(yòng)新的(dλ$e)MethodHandle類動态訪問(w" èn)和(hé)調用(yòng)方法的(de$)時(shí)候。它與不(bù)同Class✘★∑♠Loader加載類的(de)方式有(yǒu)關。要(yào)理(lǐ)↓§♥解這(zhè)個(gè)問(wèn)題,需要("σπyào)一(yī)些(xiē)基本的(de)知(zhī₽>)識,這(zhè)些(xiē)知(zhī)識涉及到(dào)Java C₹↕ ∑lassLoader的(de)工(gōng)作(zuò)方式, 因為(wè®±i)類加載是(shì)在Java中大(dà>)家(jiā)了(le)解較少(shǎo)的≥↓(de)領域之一(yī),所以在闡述這(zhè)個(gè)問(wèn">)題本身(shēn)之前,我們會(huì)首先概述一(y→♠ī)下(xià)這(zhè)個(gè)理 ✘&(lǐ)念。
Java ClassLoaderJava能($≤ néng)夠在運行(xíng)時(shí)從(cóng)各種來(l"¶ái)源動态加載代碼。這(zhè)種功能(néβ✘"ng)是(shì)通(tōng)過一(yī)系列名為α↔σ×(wèi)ClassLoader的(de)特殊類來(lái)實現(δδ♥≈xiàn)的(de)。标準的(de)Java實現(xiàn)會(✔ >huì)提供多(duō)個(gè)ClassLo₩✘≥ader來(lái)加載類,它們能(néng)夠從(cóng)文(wén)件(☆Ω↓jiàn)系統、URL或壓縮文(wén)件(jiàn)等位置加載類✘≈↕,不(bù)過Java也(yě)為(wèi)開(kāi)發人( ↕∏rén)員(yuán)提供了(le)創建自♦¥δ(zì)定義ClassLoader的(de)能(néng)力,β'₹以應對(duì)個(gè)性化(huà)的(d®€≈e)需求。與ClassLoader交互的(deφ©γ♥)常見(jiàn)方式是(shì)調用(yòng)其loadClass(✘ΩString)方法,這(zhè)個(gè)方法會( >huì)接受類的(de)名稱,如(rú)∑×§果能(néng)夠找到(dào)的(de)話(huà),就(jiù)會(h∞ε₩uì)返回相(xiàng)關的(de)Clλass對(duì)象,如(rú)果找不(bù)到(dào)λ•的(de)話(huà),就(jiù)會(huì)抛出C₩π₹←lassNotFoundException異常。在Java應用(yòngδ♥)中的(de)每個(gè)類都(dōu)是(shì)通(t$≤<ōng)過某個(gè)ClassLoader按照(zh£↔ ào)這(zhè)種方式加載的(de)。
通(tōng)過設置父ClassLoad<×<er,這(zhè)些(xiē)不(bù)同的(de)Clas≥≤sLoader能(néng)夠互相(xiàng)連接起來(lái),形成'★一(yī)個(gè)層級的(de)結構。如(rú)果沒有 ♣(yǒu)設置父ClassLoader的(de)話(huà),那®₽(nà)麽父ClassLoader默認将會©λ↕δ(huì)設置為(wèi)加載該ClassLoader的(de)那(nà)個β®♥♠(gè)類加載器(qì)(ClassLoader本身(shēn)也λ♠∑$(yě)是(shì)類,因此也(yě)需要(yào)通(tōng)過某個(g÷↓è)ClassLoader來(lái)進行(xíng)加載)。如(rú)£©果提供了(le)父ClassLoader的(de)話( ₩huà),那(nà)麽ClassLoade€↕r的(de)默認行(xíng)為(wèi)就(jiù)是(s¥ hì)将加載所請(qǐng)求類的(de)任務委托給它的(de)父加載器←≥©(qì),隻有(yǒu)父加載器(qì)(或祖父加載器(qì))無法加載這(z♣γhè)個(gè)類的(de)時(shí)候,這(zhè)個(gè)Clas€₩sLoader本身(shēn)才會(huì)♣±"Ω試圖加載所請(qǐng)求的(de)類。但(dàn)是(₹εγshì),自(zì)定義加載器(qì)的(de)創建σ→者并非強制(zhì)性要(yào)求遵循這(zhè)種默認行(xíng)為(w↓∏èi),他(tā)們完全可(kě)以選擇實現(xià≠→☆n)不(bù)同的(de)行(xíng)為(wèi)。當Jav™₩¥≤a應用(yòng)啓動的(de)時(shí)候,如(rú)下(xià♦δ<±)的(de)ClassLoader将會(huì)按照(zhào)順序'≠發揮作(zuò)用(yòng):Bootstrap ClassLoade"σr:JVM本身(shēn)的(de)一(yī)部分(fēn),因此在每個★¥γ(gè)JVM中,它的(de)實現(xiàn)都(dōu)是(sγ'φ↑hì)特有(yǒu)的(de)。這(zhè)個(g✔ è)ClassLoader沒有(yǒu)父ClassLoader,它 Ω用(yòng)于加載java.lang包下$λ•(xià)的(de)核心類。
Extension ClassLoade©≈ r:負責加載擴展庫中的(de)類,在每個(gè)Java™ε♣φ安裝環境下(xià)可(kě)能(néng✘♥¶)會(huì)有(yǒu)所差别。Extension ClassLoader₹¶α 将會(huì)加載java.ext.dirs變量所指定路(lù)徑下(xiπ♦ εà)的(de)所有(yǒu)內(nèi)容。
Application ClassLoader:負責加≠•≠載應用(yòng)程序的(de)主類以及所有(yǒu)位于應↓∏用(yòng)類路(lù)徑下(xià)的(de)類。
Custom ClassLoader:應用(★© yòng)程序中使用(yòng)的(de)所有(yǒu)其他(tā)的(de↑♥'↔)ClassLoader。它是(shì)可(kě)選的₩&∞(de),根據應用(yòng)的(de)情況不(bù)同,它可(kě)能(¥↕ néng)并不(bù)存在。
在運行(xíng)時(shí),使用(yò£¥ng)自(zì)定義的(de)ClassLoader動态加載類為(wèi)₹λ很(hěn)多(duō)的(de)應用(yòng)創造了(l$•↔e)可(kě)能(néng)性,否則的(de)話(huà),有(yǒu↑φ)些(xiē)功能(néng)可(kě)能(néng)是(shì)無法實 ↕現(xiàn)的(de),不(bù)過,遺©¶憾的(de)是(shì),它也(yě)造成了(le)很(hěn)多(duō)÷✘的(de)安全問(wèn)題,尤其是(shì)在類仿造÷δε✔(class impersonation)方面。理(lǐ)論上≈←(shàng),開(kāi)發人(rén)員(yu>"án)可(kě)以創建一(yī)個(gè)自(zεγ'ì)定義的(de)ClassLoader,讓它來(lái)加✔&€Ω載原始類java.lang.Object的(de)β一(yī)個(gè)模拟實現(xiàn),并在應€ ÷♠用(yòng)程序中使用(yòng)這(zhè<£)個(gè)自(zì)定義的(de)對(duì)象。這(zhè)可(kδΩě)能(néng)會(huì)在兩個(gè)方面引發安全問(wè§&¶n)題:這(zhè)個(gè)自(zì)定義的(de)對(duì)象能(n¶✘₹εéng)夠訪問(wèn)java.lan♥§★g包下(xià)所有(yǒu)包範圍內(nèi)可(kě)見£φ(jiàn)的(de)類內(nèi)容;其次,這(zhè)個(gè)自(zì)∏™>™定義的(de)Object會(huì)被JVM作(zuò)為(wèi€ ©)标準的(de)Object對(duì)☆β 象,因此會(huì)将其作(zuò)為(wèi)由Java實現(xiàn)的(★>✔de)可(kě)信任的(de)類。為(wèi)了(le)保護Java以應對(d₽± uì)這(zhè)些(xiē)安全問(wèΩ<n)題,Java類要(yào)通(tōng)過三個(gè)屬性來(lái)進行∞λ×(xíng)識别:類名、包以及ClassLoad>π↕er的(de)引用(yòng)。
如(rú)果兩個(gè)不(bù)同的(de)類具有(y∞♣ǒu)相(xiàng)同的(de)類名和(hé)包名,但( •dàn)是(shì)由不(bù)同的(de)ClassLoader所÷♣♦加載的(de)話(huà),Java會(huì)認為(wè↕φi)它們是(shì)不(bù)相(xiàn≤ g)等的(de),在它們兩者之間(jiān)進行(xíng)賦值的←÷≤(de)話(huà),将會(huì)導緻ClassCastExcepti∏ ↑£on異常。這(zhè)樣的(de)話(huà),就(jiù)能(néng)保÷♣→Ω護環境免受類仿造的(de)攻擊。部分(fēn)修複以及由此導緻的(de)漏洞'☆Security Explorations較早報(×α ¥bào)告了(le)這(zhè)個(gè)漏洞,并将其歸類為(wèi)CVE-π&δ2013-5838,這(zhè)個(gè)漏洞可(k&♦ě)以描述為(wèi),當通(tōng)過Method Handle₽λ$調用(yòng)方法時(shí),對(duì)于被←©←調用(yòng)方法的(de)那(nà)個(gè)類,它的(de)C ↕★lassLoader并沒有(yǒu)進行δ•(xíng)檢查,這(zhè)意味著(zhe)攻擊者可(kě)以按照(π∏zhào)上(shàng)文(wén)所述的(de)方法進行(x∑σ¥íng)類的(de)仿造。
展現(xiàn)原始漏洞的(de)代碼樣例,→®•目标類的(de)ClassLoader并沒有(yǒu)進行(↔↔ £xíng)檢查;來(lái)源:Securi→γ±ty Explorations。Oracleγ§在2013年(nián)9月(yuè)提供了(le)一(yī βΩσ)個(gè)修正,作(zuò)為(wèi)Ja↕π ♠va 7u40的(de)一(yī)部分(λ←'fēn),包含了(le)類可(kě)見(jiàn)性的(≈₩©≤de)檢查,它會(huì)對(duì)比預期類型和(hé)傳入↕>♦類型的(de)ClassLoader,對(duì)比方式如(rú)下(xiα à):如(rú)果兩個(gè)ClassLoader相(α©₩xiàng)同的(de)話(huà),那(nà)麽按照¥€"(zhào)定義這(zhè)兩個(gè)類型是(shì)完全兼容的(de);
如(rú)果其中一(yī)個(gè)ClassLoader是(shì)±®>σ另一(yī)個(gè)ClassLoaderβ£ 的(de)父加載器(qì),那(nà)麽它認為(wèi)這(zhè)€↔'兩個(gè)類是(shì)通(tōng)過正常 φ∞©的(de)ClassLoader層級結構加載的(de),因此将其視(shì)為∑λα(wèi)相(xiàng)等是(shì)安全的(♦₹•¥de)。
在第二項檢查中,Security Explorations發現 ∏♦(xiàn)exploit稍加修改就(jiù)λ♦✔ 可(kě)能(néng)繼續有(yǒu)效。首先,用(yòng)于仿δβ造類的(de)自(zì)定義ClassLoader将∑§目标ClassLoader設置為(wèi)它的(ε♥☆de)父類加載器(qì),這(zhè)可(kě)以通(tōn®☆g)過API以參數(shù)的(de)方式進行(xíng)設置:U♦>₩$RLClassLoader lookup_CL = URLClasΩ∑sLoader.newInstance(urlArray, membe©δr_CL);
通(tōng)過該機(jī)制(zhì)将自(z≈♠≠£ì)定義的(de)ClassLoader作(zuò)為(wèi)等級>™↓✘結構的(de)一(yī)部分(fēn),來(lái)源:Secu>γrity Explorations。然後™✘§,鑒于ClassLoader的(de)默認行(xín'↑÷↔g)為(wèi)是(shì)将加載類的(de)任務委托給它的(de)父™↕加載器(qì),攻擊者就(jiù)需要(yào)确保父ClassL←↕ε•oader無法加載到(dào)這(zhè)個(gè)類,♥ 這(zhè)樣他(tā)們自(zì)定義↓λ的(de)ClassLoader就(jiù)能(néng)≤"♥發揮作(zuò)用(yòng)了(le)。借助Jaσ •va以網絡方法加載類的(de)過程,這(zhè)種攻擊模式得Ωφ★(de)到(dào)了(le)印證:如(r☆&ú)果這(zhè)個(gè)類通(tōng)過URL位置的(de)方式來(láδαi)進行(xíng)定義的(de)話(huà),父Cl✔£σassLoader将會(huì)試圖連接相(xiàng)關的(de>$)服務器(qì)并獲取這(zhè)個(gè)類的(★¶de)代碼,此時(shí),預先構建好(hǎo)的(de)HTTP服φ>務器(qì)可(kě)以返回404 NOT FOUND錯(cuò)誤,讓₽¶父ClassLoader加載這(zhè)個(gè)類₹☆•出現(xiàn)失敗,因此就(jiù)會(↕∏huì)将控制(zhì)權轉移給自(zì)定義的(de)ClassL¶©©★oader。
通(tōng)過自(zì)定義的(de)HTTP服務器(≠ qì),強制(zhì)父ClassLoader加載類失敗之後的♦★λ(de)代碼流,來(lái)源:Securit£§ y Explorations。當這(zhè)個(gè)缺陷2016年ε×£(nián)3月(yuè)重新爆出時(shí),當時(shíλ↑)較新的(de)可(kě)用(yòng)版本是(shì)8u74,這"♦↑(zhè)個(gè)版本被證明(míng '₹)是(shì)有(yǒu)漏洞的(de),Oracle在8u77中δ★為(wèi)其提供了(le)修正。但(dàn)是(sh✘ì),在8u77的(de)發布說(shuō) ∏∑♣明(míng)中,這(zhè)個(gè)漏洞依然描述為§£¶(wèi)“會(huì)影(α∞Ωyǐng)響桌面設備上(shàng),Web浏覽器(qì)中所運行(xíng↓γ ↕)的(de)JavaSE[并且]不(bù)✘≥會(huì)影(yǐng)響到(dào)Java部署環境,比如(✔"£↑rú)典型的(de)服務器(qì)或獨立部署的(de)桌面應用(yòn✔≈φ≠g)”,但(dàn)是(sh≈λì)事(shì)實證明(míng),它還(hái)是(shì)會(huì)>₩影(yǐng)響服務器(qì)配置以及Google App Engine的(d✘↑×e)Java環境。修正:2016年(niá∞✔n)4月(yuè)29日(rì)本文(wé≥↕n)錯(cuò)誤地(dì)認為(wèi)這(zhè)個γπ(gè)漏洞在8u77、8u91和(hé)8u92版本中<↔依然存在,實際上(shàng),在8u77中它已經得(÷✘₹de)到(dào)了(le)修正。在8u77的(σ☆<de)發布說(shuō)明(míng)中将其描述為(wèi®★ )對(duì)CVE-2016-0636的(de)修正,具體(t₹ ǐ)描述是(shì)這(zhè)樣的(de)“未指明(mín¥$π→g)的(de)漏洞[...]借助Hotspot子(zǐ)組≥∑€↑件(jiàn)中未知(zhī)的(de)感染內φ≥ ♥(nèi)容”并且沒有(yǒu)包$₽ 含對(duì)本文(wén)中所提及的(de)CVE-201φ↑3-5838的(de)明(míng)确引用(yòng)。但(dàn)是(↕≤shì),Security Explor ✔ations指出 CVE-2016-06↔36是(shì)針對(duì)Issue 69的(de)一(yī)個(gè☆ ←)CVE編号,而Issue 69是(shì)他(tā)↑們自(zì)己代指較初CVE-2013-5838的(d↑©e)一(yī)種方式。(在本文(wén)英文(wén)原文(₹∑§πwén)的(de)評論區(qū),討(tǎo)論了(le)該問(Ω☆≠✘wèn)題解決的(de)相(xiàng)關過程,感興趣的(de)讀(dú β♥)者可(kě)以訪問(wèn)原文(wén)查看(kàn)。$♠——譯者注)