本地命令执行是一种非常高风险的漏洞,在任何时候都应当非常谨慎的使用,在业务中如果使用到了本地系统命令那么应当禁止接收用户传入参数。在很多时候攻击者会利用某些漏洞(如:Struts2、反序列化等)来攻击我们的业务系统,最终利用Java本地命令执行达到控制Web服务器的目的。这种情况下用户执行的系统命令对我们来说就不再受控制了,我们除了可以配置SecurityManager规则限制命令执行以外,使用RASP来防御本地命令执行就显得更加的便捷可靠。

3.1 RASP防御Java本地命令执行

在Java底层执行系统命令的API是java.lang.UNIXProcess/ProcessImpl#forkAndExec方法,forkAndExec是一个native方法,如果想要Hook该方法需要使用Agent机制中的Can-Set-Native-Method-Prefix,为forkAndExec设置一个别名,如:__RASP__forkAndExec,然后重写__RASP__forkAndExec方法逻辑,即可实现对原forkAndExec方法Hook。
示例 - Java本地命令执行API:
4. 3. 本地命令执行防御 - 图1
使用RASP的Hook机制捕获当前正在执行的系统命令,不过不同的API获取执行的命令参数的方式不太一样。
示例 - Hook java.lang.ProcessImpl执行系统命令:

  1. /**
  2. * Hook Windows系统ProcessImpl类构造方法
  3. */
  4. @RASPMethodHook(
  5. className = "java.lang.ProcessImpl", methodName = CONSTRUCTOR_INIT,
  6. methodArgsDesc = ".*", methodDescRegexp = true
  7. )
  8. public static class ProcessImplHook extends RASPMethodAdvice {
  9. @Override
  10. public RASPHookResult<?> onMethodEnter() {
  11. try {
  12. String[] commands = null;
  13. // JDK9+的API参数不一样!
  14. if (getArg(0) instanceof String[]) {
  15. commands = getArg(0);
  16. } else if (getArg(0) instanceof byte[]) {
  17. commands = new String[]{new String((byte[]) getArg(0))};
  18. }
  19. // 检测执行的命令合法性
  20. return LocalCommandHookHandler.processCommand(commands, getThisObject(), this);
  21. } catch (Exception e) {
  22. RASPLogger.log(AGENT_NAME + "处理ProcessImpl异常:" + e, e);
  23. }
  24. return new RASPHookResult<?>(RETURN);
  25. }
  26. }

3.1.1 请求参数关联分析

获取到本地命令执行的参数后需要与Http请求的参数进行关联分析,检测当前执行的系统命令是否与请求参数相关,如果确认当前执行的系统命令来源于Http请求参数,那么RASP会立即阻止命令执行并阻断Http请求。

3.1.2 限制执行本地系统命令

因为本地命令执行的危害性极大,所以在默认情况下可以直接禁止本地命令执行,如果业务的确有必要开启那么可以对相应的业务URL做白名单。限制的方式可分为两种类型:

  1. 完全限制本地命令执行,禁止在Java中执行任何命令;
  2. 允许程序内部的本地命令执行,只在有Http请求的时候才禁止执行命令;

这两种类型的禁止方案为可选方案,可在RASP的云端实时配置。
示例 - 禁止在HTTP请求时执行本地系统命令:
4. 3. 本地命令执行防御 - 图2