利用 Java Binary Webshell 對抗靜態檢測

umvf4078 8年前發布 | 15K 次閱讀 JSP Java開發 Java Webshell

 

0x01 背景 

Webshell一般是指以服務端動態腳本形式存在的一種網頁后門。在入侵檢測的過程中,檢測Webshell無疑是一大重點。比較常見的檢測手法有:

1. 文件內容檢測(靜態檢測)

2. 文件行為檢測(動態檢測)

3. 網絡異常流量分析

4. …… 

其中,靜態檢測是比較簡單有效的檢測Webshell的手段之一。根據Webshell的文件特征建立異常模型,并使用大量的Webshell樣本對模型進行訓練,通過諸如異常函數、關鍵代碼以及文件內容與普通業務代碼的相似度等等關鍵點來進行分析檢測。 然而,如果Webshell脫離了服務端腳本頁面形式的存在,基于文件特征的靜態檢測又將面臨怎樣的困境?我們不妨一起來看看。

0x02 JavaWeb應用 

在Java Web應用中,Servlet是Java語言實現的一個接口,用于編寫服務端程序[^1]。Servlet程序代碼會預先編譯成.class文件,部署在Java容器中,響應用戶各種協議的請求,大多數情況下基于HTTP協議,包括動態生成網頁內容等等。但是Servlet由Java代碼編寫,不能有效地區分頁面的展示和處理邏輯,導致Servlet代碼非常混亂,而用Java服務器頁面(JSP)的出現,可以讓程序員把展現層和數據層很好的區分管理起來。

JSP作為HttpServlet的擴展,使用HTML的書寫格式,在適當的地方加入Java代碼片段,從而動態生成頁面內容。JSP在首次被訪問時,JSP應用容器(應用服務器中用于管理Java組件的部分)將其轉換為Java Servlet代碼,并編譯成.class字節碼文件并執行。而下次該JSP文件被訪問時,服務器將直接調用Servlet進行處理,除非JSP文件被修改。

比如,在Apache Tomcat中,它提供了一個Jasper編譯器用以將JSP編譯成對應的Servlet。在JSP文件被訪問后,在workDir生成對應的servlet源碼與編譯后的.class字節碼文件。

JSP編譯生成的.class文件默認存放在$CATALINA_BASE/work下,存放路徑也可以通過Server.xml等配置文件中的Host標簽的workDir屬性進行配置 [^2]。


JSP文件再次被訪問時,Tomcat會直接調用已編譯好的字節碼文件。當文件被修改,Tomcat會重新解析JSP文件,生成Servlet代碼并編譯執行。當文件被刪除時,Tomcat返回404 Not Found。

而在在配置文件$CATALINA_BASE/conf/web.xml中,當Jasper運行在開發模式下時,我們可以配置modificationTestInterval參數,控制Tomcat在一定時間之內不檢查JSP文件的修改狀態[^3]。

設想,如果可以關閉Java容器對JSP文件修改狀態的檢查,是否可以將惡意代碼存放在workDir的.class字節碼中,并通過JSP形式持久訪問?

0x03 Resin的一個特性

我們注意到了另一款非常流行且性能優良的企業級應用服務器——Resin。Resin同樣提供了Servlet和JSP運行引擎。 以看到默認情況下,初次訪問JSP后,Resin會在./WEB-INF/work/_jsp目錄下生成Servlet源碼和編譯后的.class字節碼文件。 

與Apache Tomcat不同的是,Resin生成并編譯Servlet之后,可以在JSP文件被刪除的情況下,正常提供訪問。 查看Resin生成的JSP對應的Servlet源碼發現,生成的代碼內包含了檢查JSP文件修改狀態相關方法:_caucho_isModified()。

我們來看看這部分源碼中的關鍵邏輯:

Servlet啟動時,Resin會調用init()方法,結束時會調用destroy()方法[^4]。init()方法中實例化的Depend類用于檢查文件修改, 這里調用的Depend構造函數中,第三個參數標志了在JSP文件被刪除的情況下的處理邏輯。 

public Depend(Path source, long digest, boolean requireSource) 

requireSource為True時,如果JSP文件被刪除則服務器返回404。默認為false,所以當已編譯的JSP文件被刪除時,Resin并不會判定該JSP頁面被修改,依然會執行對應的字節碼。

可以看到,Resin判斷一個JSP文件是否修改的邏輯為

當web.xml中配置autoCompile屬性為false時,Resin會關閉對JSP文件的自動編譯,調用_caucho_setNeverModified()方法,從而不會檢查JSP文件修改狀態。

web.xml

0x04 Binary JSP Webshell 

由于Resin這些特性,我們可以用JSP將Webshell字節碼寫入對應的路徑下,即可得到一個二進制形式存在的JSP Webshell。Resin自動編譯存放的代碼目錄路徑可以自定義配置[^5],默認為`WEB-INF/work`目錄,如:

如:默認配置下,利用JSP寫入二進制字節碼Webshell

利用腳本中Webshell的字節碼內容可以在本地Resin服務器環境中編譯獲得,但是由于編譯和運行的Resin版本不一致會被判定JSP文件已修改,從而被重新編譯,這不是我們想看到的。如0x03小節中所說,Resin中判斷JSP是否修改的邏輯包含在JSP對應的Servlet代碼中,于是我們可以篡改這部分字節碼中的邏輯,使得_caucho_isModified()函數永遠返回false,JVM指令如下:

測試效果如下:利用write_binary_shell.jsp文件,將字節碼webshell寫入對應的目錄下,即可通過訪問對應的JSP文件來訪問Webshell。 由于篡改了相關的判斷邏輯,無論Web是否存在同名JSP文件,Resin依然會優先解析到該字節碼Webshell。

0x05 References 

1. https://zh.wikipedia.org/wiki/Java_Servlet ?

2. https://tomcat.apache.org/tomcat-8.0-doc/config/host.html ?

3. https://tomcat.apache.org/tomcat-8.0-doc/jasper-howto.html ?

4. http://www.caucho.com/resin-3.1/doc/servlet.xtp ?

5. http://www.caucho.com/resin-4.0/admin/config-el-ref.xtp#work-dir ?

 本文由用戶 umvf4078 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!