前言:最近在学习Java安全方面的内容,记录一下Java中的几个命令执行函数,使用Javasec靶场进行演示
第一个命令执行函数 RuntimeExec 漏洞描述 远程命令执行漏洞,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令
可能会允许攻击者通过改变 $PATH 或程序执行环境的其他方面来执行一个恶意构造的代码。
getRuntime()常用于执行本地命令,使用频率较高。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class RuntimeExec { @RequestMapping("/runtime") public String RuntimeExec (String cmd, Model model) { StringBuilder sb = new StringBuilder (); String line; try { Process proc = Runtime.getRuntime().exec(cmd); InputStream fis = proc.getInputStream(); InputStreamReader isr = new InputStreamReader (fis, "GBK" ); BufferedReader br = new BufferedReader (isr); while ((line = br.readLine()) != null ) { sb.append(line).append(System.lineSeparator()); } } catch (IOException e) { e.printStackTrace(); sb.append(e); } model.addAttribute("results" , sb.toString()); return "basevul/rce/runtime" ; } }
这段代码是一个基于Spring框架的Java程序,其中包含了一个处理HTTP请求的方法。该方法接受一个名为cmd的参数,并将其作为命令传递给操作系统运行。然后,程序获取命令的输出,并将输出内容保存到一个StringBuilder对象中,最后将输出结果作为模型属性返回给视图。
这段代码实现了一个”Runtime Exec”功能,可以执行操作系统命令。
第二个命令执行函数 ScriptEngineManager 该漏洞又叫脚本引擎代码注入
漏洞描述 在Java 8之后ScriptEngineManager的eval函数就没有了
windows:
var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec(“calc”)};
Payload绕过:
var a = mainOutput(); function mainOutput() { var x=java.lang.//Runtime.getRuntime().exec(“calc”);}
恶意代码(java)
1 2 3 4 5 6 7 8 9 10 public void jsEngine (String url) throws Exception { ScriptEngine engine = new ScriptEngineManager ().getEngineByName("JavaScript" ); Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); String payload = String.format("load('%s')" , url); engine.eval(payload, bindings); }
js代码
1 2 3 4 var a = mainOutput ();function mainOutput ( ) { var x = java.lang .Runtime .getRuntime ().exec ("calc" ) };
引用这一个js文件,就可以触发该漏洞
第三个 Groovy执行命令 1 2 3 4 5 6 7 8 import groovy.lang.GroovyShell;@GetMapping("/groovy") public void groovy (String cmd) { GroovyShell shell = new GroovyShell (); shell.evaluate(cmd); }
* windows: “calc”.execute()
* macos: “open -a Calculator”.execute()
就是一个传参,后端有该代码的时候直接传恶意代码就好
第四个 ProcessBuilder 漏洞描述 Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序)。
Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获取相关信息。
创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程,通过流的形式进行读取。
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 public String ProcessBuilderExec (String ip, String safe, Model model) { if (safe != null ) { if (Security.checkOs(ip)) { model.addAttribute("results" , "检测到非法命令注入" ); return "basevul/rce/processbuilder" ; } } String[] cmdList = {"cmd" , "/c" , "ping -n 1 " + ip}; StringBuilder sb = new StringBuilder (); String line; String results; ProcessBuilder pb = new ProcessBuilder (cmdList); pb.redirectErrorStream(true ); Process process = null ; try { process = pb.start(); InputStream fis = process.getInputStream(); InputStreamReader isr = new InputStreamReader (fis, "GBK" ); BufferedReader br = new BufferedReader (isr); while ((line = br.readLine()) != null ) { sb.append(line).append(System.lineSeparator()); } results = sb.toString(); } catch (IOException e) { e.printStackTrace(); results = e.toString(); } model.addAttribute("results" , results); return "basevul/rce/processbuilder" ; }
第五个 ProcessImpl 漏洞描述 对于ProcessImpl类不能直接调用,但可以通过反射来间接调用ProcessImpl来达到执行命令的目的
该类非Public修饰,所以在不同包下只能通过反射的方式去调用执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public String processImplExec (String cmd, Model model) { CharArrayWriter infoStream = null ; try { Class<?> clazz = Class.forName("java.lang.ProcessImpl" ); Method method = clazz.getDeclaredMethod("start" , String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean .class); method.setAccessible(true ); Process e = (Process) method.invoke(null , new String []{cmd}, null , null , null , false ); char [] bs = new char [2048 ]; int readSize = 0 ; infoStream = new CharArrayWriter (); InputStream inputStream = e.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader (inputStream, "GBK" ); while ((readSize = inputStreamReader.read(bs)) > 0 ) { infoStream.write(bs, 0 , readSize); } model.addAttribute("results" , infoStream.toString()); } catch (Exception ex) { ex.printStackTrace(); model.addAttribute("results" , ex.toString()); } return "basevul/rce/processimpl" ; }
在白盒测试中,要挖rce漏洞,可以尝试全局搜索这五个函数并进行代码审计
黑盒测试的时候就只能靠运气了