03.05 JAVA架構網站安全漏洞滲透測試檢測手法

近期對平臺安全滲透測試中遇到有JAVA+mysql架構的網站,針對此架構我們安全滲透工程師整理了下具體的漏洞檢測方法和防護修復方法,很多像執行框架漏洞獲取到系統權限,以及跨權限寫入木馬後門等等操作,希望大家在滲透測試的道路中發現更多的知識和經驗。

JAVA架構網站安全漏洞滲透測試檢測手法

4.2.1. 格式化字符串

在Python中,有兩種格式化字符串的方式,在Python2的較低版本中,格式化字符串的方式為 "this is a %s" % "test" ,之後增加了format的方式, 語法為 "this is a {}".format('test') 或者 "this is a {test}".format(test='test')

當格式化字符串由用戶輸入時,則可能會造成一些問題,下面是一個最簡單的例子

'class of {0} is {0.__class__}'.format(42)

"class of 42 is "

從上面這個簡單的例子不難知道,當我們可以控制要format的字符串時,則可以使用 init / globals 等屬性讀取一些比較敏感的值,甚至任意執行代碼。

4.2.2. 反序列化

4.2.2.1. pickle

class A(object):

... a = 1

... b = 2

... def __reduce__(self):

... return (subprocess.Popen, (('cmd.exe',),))

cPickle.dumps(A())

"csubprocessnPopennp1n((S'cmd.exe'np2ntp3ntp4nRp5n."

4.2.2.2. 其他

PyYAMLmarshalshelve4.2.3. 沙箱

4.2.3.1. 常用函數

eval / exec / compiledir / typeglobals / locals / varsgetattr / setattr4.2.3.2. 繞過

最簡單的思路是在已有的模塊中import,如果那個模塊中已經 import 可以利用的模塊就可以使用了在父類中尋找可用的模塊,最常見payload是 ().__class__.__bases__[0].__subclasses__() 或者用魔術方法獲取全局作用域 init__.__func__.__globals有些網站沒有過濾 pickle 模塊,可以使用 pickle 實現任意代碼執行,生成 payload 可以使用有的沙箱把相關的模塊代碼都被刪除了,則可以使用libc中的函數,Python 中調用一般可以使用 ctypes 或者 cffi。"A""B" == "AB"4.2.3.3. 防禦

Python官方給出了一些防禦的建議

使用Jython並嘗試使用Java平臺來鎖定程序的權限使用fakeroot來避免使用一些rootjail的技術4.2.4. 框架

4.2.4.1. Django

4.2.4.1.1. 歷史漏洞

CVE-2016-7401 CSRF BypassCVE-2017-7233/7234 Open redirect vulnerabilityCVE-2017-12794 debug page XSS4.2.4.1.2. 配置相關

Nginx 在為 Django 做反向代理時,靜態文件目錄配置錯誤會導致源碼洩露。訪問 /static.. 會 301 重定向到 /static../4.2.4.2. Flask

Flask默認使用客戶端session,使得session可以被偽造

4.2.5. 危險函數 / 模塊列表

4.2.5.1. 命令執行

os.popenos.systemos.spawnos.forkos.execpopen2commandssubprocessexecexecfileevaltimeit.systimeit.timeitplatform.osplatform.sysplatform.popenpty.spawnpty.osbdb.oscgi.sys…4.2.5.2. 危險第三方庫

Templatesubprocess324.2.5.3. 反序列化

marshalPyYAMLpicklecPickleshelvePILJava

4.3.1. 基本概念

JVM是Java平臺的核心,以機器代碼來實現,為程序執行提供了所需的所有基本功能,例如字節碼解析器、JIT編譯器、垃圾收集器等。由於它是機器代碼實現的,其同樣受到二進制文件受到的攻擊。

JCL是JVM自帶的一個標準庫,含有數百個系統類。默認情況下,所有系統類都是可信任的,且擁有所有的特權。

4.3.1.2. JNDI

JNDI(Java Naming and Directory Interface,JAVA命名和目錄接口)是為JAVA應用程序提供命名和目錄訪問服務的API(Application Programing Interface,應用程序編程接口)。

4.3.1.3. OGNL

OGNL(Object-Graph Navigation Language,對象導航語言)是一種功能強大的表達式語言,通過簡單一致的表達式語法,提供了存取對象的任意屬性、調用對象的方法、遍歷整個對象的結構圖、實現字段類型轉化等功能。

Struts2中使用了OGNL,提供了一個ValueStack類。ValueStack分為root和context兩部分。root中是當前的action對象,context中是ActionContext裡面所有的內容。

4.3.1.4. RMI

RMI(Remote Method Invocation,遠程方法調用)能夠讓在客戶端Java虛擬機上的對象像調用本地對象一樣調用服務端java虛擬機中的對象上的方法。

RMI遠程調用步驟:

客戶調用客戶端輔助對象stub上的方法客戶端輔助對象stub打包調用信息(變量,方法名),通過網絡發送給服務端輔助對象skeleton服務端輔助對象skeleton將客戶端輔助對象發送來的信息解包,找出真正被調用的方法以及該方法所在對象調用真正服務對象上的真正方法,並將結果返回給服務端輔助對象skeleton服務端輔助對象將結果打包,發送給客戶端輔助對象stub客戶端輔助對象將返回值解包,返回給調用者客戶獲得返回值4.3.2. 框架

JAVA架構網站安全漏洞滲透測試檢測手法

4.3.2.1. Servlet

4.3.2.1.1. 簡介

Servlet(Server Applet)是Java Servlet的簡稱,稱為小服務程序或服務連接器,是用Java編寫的服務器端程序,主要功能在於交互式地瀏覽和修改數據,生成動態Web內容。

狹義的Servlet是指Java語言實現的一個接口,廣義的Servlet是指任何實現了這個Servlet接口的類,一般情況下,人們將Servlet理解為後者。Servlet運行於支持Java的應用服務器中。從原理上講,Servlet可以響應任何類型的請求,但絕大多數情況下Servlet只用來擴展基於HTTP協議的Web服務器。

4.3.2.1.2. 生命週期為

客戶端請求該 Servlet加載 Servlet 類到內存實例化並調用init()方法初始化該Servlet service()(根據請求方法不同調用 doGet() / doPost() / … / destroy()4.3.2.1.3. 接口

init()

在 Servlet 的生命期中,僅執行一次 init() 方法,在服務器裝入 Servlet 時執行。

service()

service() 方法是 Servlet 的核心。每當一個客戶請求一個HttpServlet對象,該對象的 service() 方法就要被調用,而且傳遞給這個方法一個”請求”(ServletRequest)對象和一個”響應”(ServletResponse)對象作為參數。

4.3.2.2. Struts 2

4.3.2.2.1. 簡介

Struts2是一個基於MVC設計模式的Web應用框架,它本質上相當於一個servlet,在MVC設計模式中,Struts2作為控制器(Controller)來建立模型與視圖的數據交互。

4.3.2.2.2. 請求流程

客戶端發送請求的tomcat服務器請求經過一系列過濾器FilterDispatcher調用ActionMapper來決定這個請求是否要調用某個ActionActionMppaer決定調用某個ActionFilterDispatcher把請求給ActionProxyActionProxy通過Configuration Manager查看structs.xml,找到對應的Action類ActionProxy創建一個ActionInvocation對象ActionInvocation對象回調Action的execute方法Action執行完畢後,ActionInvocation根據返回的字符串,找到相應的result,通過HttpServletResponse返回給服務器4.3.2.2.3. 相關CVE

CVE-2016-3081 (S2-032)CVE-2016-3687 (S2-033)CVE-2016-4438 (S2-037)CVE-2017-5638CVE-2017-7672CVE-2017-9787CVE-2017-9793CVE-2017-9804CVE-2017-9805CVE-2017-12611CVE-2017-15707CVE-2018-1327CVE-2018-117764.3.2.3. Spring MVC

4.3.2.3.1. 請求流程

用戶發送請求給服務器服務器收到請求,使用DispatchServlet處理Dispatch使用HandleMapping檢查url是否有對應的Controller,如果有,執行如果Controller返回字符串,ViewResolver將字符串轉換成相應的視圖對象DispatchServlet將視圖對象中的數據,輸出給服務器 服務器將數據輸出給客戶端4.3.3. 容器

常見的Java服務器有Tomcat、Weblogic、JBoss、GlassFish、Jetty、Resin、IBM Websphere等,這裡對部分框架做一個簡單的說明。

4.3.3.1. Tomcat

Tomcat是一個輕量級應用服務器,在中小型系統和併發訪問用戶不是很多的場合下被普遍使用,用於開發和調試JSP程序。

在收到請求後,Tomcat的處理流程如下:

客戶端訪問Web服務器,發送HTTP請求Web服務器接收到請求後,傳遞給Servlet容器Servlet容器加載Servlet,產生Servlet實例後,向其傳遞表示請求和響應的對象Servlet實例使用請求對象得到客戶端的請求信息,然後進行相應的處理Servlet實例將處理結果通過響應對象發送回客戶端,容器負責確保響應正確送出,同時將控制返回給Web服務器Tomcat服務器是由一系列可配置的組件構成的,其中核心組件是Catalina Servlet容器,它是所有其他Tomcat組件的頂層容器。

4.3.3.1.1. 相關CVE

CVE-2019-0232CVE-2017-12615CVE-2013-2067CVE-2012-4534CVE-2012-4431CVE-2012-3546CVE-2012-3544CVE-2012-2733CVE-2011-3375CVE-2011-3190CVE-2008-29384.3.3.2. Weblogic

4.3.3.2.1. 簡介

WebLogic是美國Oracle公司出品的一個Application Server,是一個基於Java EE架構的中間件,WebLogic是用於開發、集成、部署和管理大型分佈式Web應用、網絡應用和數據庫應用的Java應用服務器。其將Java的動態功能和Java Enterprise標準的安全性引入大型網絡應用的開發、集成、部署和管理之中。

WebLogic對業內多種標準的全面支持,包括EJB、JSP、Servlet、JMS、JDBC等。

4.3.3.2.2. 相關CVE

CVE-2019-2658CVE-2019-2650CVE-2019-2649CVE-2019-2648CVE-2019-2647CVE-2019-2646CVE-2019-2645CVE-2019-2618CVE-2019-2615CVE-2019-2568CVE-2018-3252CVE-2018-3248CVE-2018-3245CVE-2018-3201CVE-2018-3197CVE-2018-3191CVE-2018-1258CVE-2017-10271CVE-2017-3248CVE-2016-3510CVE-2015-48524.3.3.3. JBoss

4.3.3.3.1. 簡介

JBoss是一個基於J2EE的管理EJB的容器和服務器,但JBoss核心服務不包括支持servlet/JSP的WEB容器,一般與Tomcat或Jetty綁定使用。

4.3.3.3.2. 相關CVE

CVE-2017-121494.3.4. 沙箱

4.3.4.1. 簡介

Java實現了一套沙箱環境,使遠程的非可信代碼只能在受限的環境下執行。

4.3.4.2. 相關CVE

CVE-2012-0507CVE-2012-4681CVE-2017-3272CVE-2017-32894.3.5. 反序列化

4.3.5.1. 簡介

序列化就是把對象轉換成字節流,便於保存在內存、文件、數據庫中;反序列化即逆過程,由字節流還原成對象。Java中的 ObjectOutputStream 類的 writeObject() 方法可以實現序列化,類 ObjectInputStream類的readObject() 方法用於反序列化。

如果要實現類的反序列化,則是對其實現 Serializable 接口。

4.3.5.2. 序列數據結構

0xaced 魔術頭4.3.5.3. 序列化流程

ObjectOutputStream實例初始化時,將魔術頭和版本號寫入bout (BlockDataOutputStream類型) 中調用ObjectOutputStream.writeObject()開始寫對象數據○ObjectStreamClass.lookup()封裝待序列化的類描述 (返回ObjectStreamClass類型) ,獲取包括類名、自定義serialVersionUID、可序列化字段 (返回ObjectStreamField類型) 和構造方法,以及writeObject、readObject方法等○writeOrdinaryObject()寫入對象數據

■寫入對象類型標識

■writeClassDesc()進入分支writeNonProxyDesc()寫入類描述數據寫入類描述符標識寫入類名寫入SUID (當SUID為空時,會進行計算並賦值)計算並寫入序列化屬性標誌位寫入字段信息數據寫入Block Data結束標識寫入父類描述數據

■writeSerialData()寫入對象的序列化數據若類自定義了writeObject(),則調用該方法寫對象,否則調用defaultWriteFields()寫入對象的字段數據 (若是非原始類型,則遞歸處理子對象)4.3.5.4. 反序列化流程

ObjectInputStream實例初始化時,讀取魔術頭和版本號進行校驗調用ObjectInputStream.readObject()開始讀對象數據○讀取對象類型標識○readOrdinaryObject()讀取數據對象

■readClassDesc()讀取類描述數據讀取類描述符標識,進入分支readNonProxyDesc()讀取類名讀取SUID讀取並分解序列化屬性標誌位讀取字段信息數據resolveClass()根據類名獲取待反序列化的類的Class對象,如果獲取失敗,則拋出ClassNotFoundExceptionskipCustomData()循環讀取字節直到Block Data結束標識為止 讀取父類描述數據initNonProxy()中判斷對象與本地對象的SUID和類名 (不含包名) 是否相同,若不同,則拋出InvalidClassExceptionObjectStreamClass.newInstance()獲取並調用離對象最近的非

■Serializable的父類的無參構造方法 (若不存在,則返回null) 創建對象實例

■readSerialData()讀取對象的序列化數據若類自定義了readObject(),則調用該方法讀對象,否則調用defaultReadFields()讀取並填充對象的字段數據4.3.5.5. 相關函數

ObjectInputStream.readObjectObjectInputStream.readUnsharedXMLDecoder.readObjectYaml.loadXStream.fromXMLObjectMapper.readValueJSON.parseObject4.3.5.6. 主流JSON庫

4.3.5.6.1. GSON

Gson默認只能反序列化基本類型,如果是複雜類型,需要程序員實現反序列化機制,相對比較安全。

4.3.5.6.2. Jackson

除非指明@jsonAutoDetect,Jackson不會反序列化非public屬性。在防禦時,可以不使用enableDefaultTyping方法。

相關CVE有

CVE-2017-7525CVE-2017-150954.3.5.6.3. Fastjson

相關CVE有

CVE-2017-183494.3.5.7. 存在危險的基礎庫

commons-fileupload 1.3.1

commons-io 2.4

commons-collections 3.1

commons-logging 1.2

commons-beanutils 1.9.2

org.slf4j:slf4j-api 1.7.21

com.mchange:mchange-commons-java 0.2.11

org.apache.commons:commons-collections 4.0

com.mchange:c3p0 0.9.5.2

org.beanshell:bsh 2.0

b5org.codehaus.groovy:groovy 2.3.9

org.springframework:spring-aop 4.1.4.

RELEASE4.3.5.8.

網站漏洞修復和防護

JAVA架構網站安全漏洞滲透測試檢測手法

4.3.5.8.1. Hook resolveClass

在使用 readObject() 反序列化時會調用 resolveClass 方法讀取反序列化的類名,可以通過hook該方法來校驗反序列化的類,一個Demo如下

以上的Demo就只允許序列化 SerialObject ,通過這種方式,就可以設置允許序列化的白名單

4.3.5.8.2. ValidatingObjectInputStream

Apache Commons IO Serialization包中的 ValidatingObjectInputStream 類提供了 accept 方法,可以通過該方法來實現反序列化類白/黑名單控制,一個demo如下

4.3.5.8.3. ObjectInputFilter

Java 9提供了支持序列化數據過濾的新特性,可以繼承 java.io.ObjectInputFilter 類重寫 checkInput方法來實現自定義的過濾器,並使用 ObjectInputStream 對象的 setObjectInputFilter 設置過濾器來實現反序列化類白/黑名單控制,對JAVA漏洞滲透測試有想進一步瞭解的可以諮詢專業的網站安全公司。


分享到:


相關文章: