1 漏洞原理
因用户输入未过滤或净化不完全,导致Web应用程序接收用户输入,拼接到要执行的系统命令中执行。一旦攻击者可以在目标服务器中执行任意系统命令,就意味着服务器已被非法控制。
2 审计中常用函数
一旦攻击者可以在目标服务器中执行任意系统命令,就意味着服务器已被非法控制。 在Java中可用于执行系统命令的方式有API有:
java.lang.Runtime
java.lang.ProcessBuilder
java.lang.ProcessImpl
2.1 java.lang.Runtime
java.lang.Runtime中提供了getRuntime()内置方法获取类实例。在java中用到最多的就 是java.lang.Runtime#exec() 来命令执行。
例1:
public static void main(String[] args) throws IOException { String command = "calc"; Runtime.getRuntime().exec(command); }
输出结果
例2:
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; public class Demo06 { public static void main(String[] args) { try { String command = "ping ej0v42.dnslog.cn"; Process proc = Runtime.getRuntime().exec(command); //打印执行结果 InputStream in = proc.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF8")); String line = null; while ((line = br.readLine()) != null) { System.out.println(line); } }catch (Exception e){ e.printStackTrace(); } } }
输出结果
Runtime#exec() 调用链
断点后进入
点击步过进入
往上看代码哪里调用了它
接着往下走
接着步过到
剩下一路步过到这里java.security.AccessController.doPrivileged进行执行
根据系统类型区分底层要调用详细参考:
Runtime (Java Platform SE 7 )
2.2 java.lang.ProcessBuilder
ProcessBuilder类是JDK 1.5在java.lang中新添加的一个类,用于创建操作系统进程。 通常使用java.lang.ProcessBuilder#start() 来启动和管理进程。
import java.io.IOException; public class Demo02 { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Demo02 evilClass=new Demo02(); } }
输出结果
2.3 java.lang.ProcessImpl
ProcessImpl类通常是为ProcessBuilder.start()创建新进程服务的,不能直接去调用。 看到
ProcessImpl类构造器私有,所以不能直接对其进行实例化,为了演示可以用反射
例1: 在获取到一个静态方法后,必须用 setAccessible 修改它的作用域,否则不能调用。
public static void impCommandExec2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { String[] cmds = {"calc"}; Class clazz = Class.forName("java.lang.ProcessImpl"); Method method = clazz.getDeclaredMethod("start", new String[]{}.getClass(), Map.class,String.class, ProcessBuilder.Redirect[].class, boolean.class); //获取到要调用的方法:java.lang.ProcessImpl,start,并且按照规范传递相关的参数 method.setAccessible(true);//开启程序利用反射机制对私有的属性或方法进行访问 method.invoke(null,cmds,null,".",null,true);//通过方法的对象调用期执行的方法 }
输出结果
测试案例- 1:反射调用Runtime#exec()时的失败写法
Class clazz = Class.forName("java.lang.Runtime"); clazz.getMethod("exec", String.class).invoke(clazz.newInstance(), "id");
无法执行的原因: Clazz.newInstance() 中要求访问目标类构造函数,由于 java.lang.Runtime 的构造函数 私有,所以执行失败。
Method.invoke()
Clazz.newInstance() 中要求访问目标类构造函数,由于 java.lang.Runtime 的构造函数 私有,所以执行失败。
Method.invoke()
写法一: 通过java.lang.Runtime类内部方法getRuntime()获取当前实例。
Class clazz = Class.forName("java.lang.Runtime"); clazz.getMethod("exec", String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz), "calc.exe");
关于反射调用静态方法: 由于静态方法不属于任何对象,只属于类本身,所以使用invoke时不需要传实例对象。
写法二: 反射获取私有构造函数。用 setAccessible 修改它的作用域。
Class clazz = Class.forName("java.lang.Runtime"); Constructor m = clazz.getDeclaredConstructor(); m.setAccessible(true); clazz.getMethod("exec", String.class).invoke(m.newInstance(), "calc");
总结:注意利用反射机制调用命令API时是否具有访问权限的问题
例2: 使用ProcessBuilder.start()执行"ifconfig -a"命令
InputStream in = new ProcessBuilder("ifconfig -a").start();