1 成因
开发者在某种开发需求时,需要引入对系统本地命令的支持来完成某些特定的功能,此时若未对用户的输入做严格的过滤,就可能发生命令注入。
2 造成命令注入的类或方法
-
Runtime类:提供调用系统命令的功能
①Runtime.getRuntime():获得JVM运行时的环境
②Runtime.getRuntime().exec(cmd)执行用户输入的cmd命令
存在命令注入的代码如下:
protected void doGet (HttpServletRequest req, HttpServletRequest resp) throws ServletException, IOException{ String cmd = req.getParameter("cmd"); Process process = Runtime.getRuntime().exec(cmd); InputStream in = process.getInputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] b = new byte[1024];//获取1M的一个缓冲区 int i = -1; while((i=in.read(b)) != -1)//判断是否读完 { byteArrayOutputStream.write(b,0,i); } PrintWriter Out = resp.getWriter(); out.print(new String(byteArrayOutputStream.toByteArray())); }
-
ProcessBuilder:可以创建操作系统进程
//利用指定的操作系统程序和参数构造一个进程生成器。 ProcessBuilder(String… command) //设置此进程生成器的操作系统程序和参数。 command(List<String> command) command(String… command) //设置此进程生成器的工作目录。 directory(File directory) //返回此进程生成器环境的字符串映射视图。 environment方法获得运行进程的环境变量,得到一个Map,可以修改环境变量 environment() //使用此进程生成器的属性启动一个新进程。 start()
-
Groovy:
①
execute()
:可执行shell命令,eg:def command = "git log" def proc = command.execute()//执行git log的命令 proc.waitFor() def status = proc.exitValue()
②
result = sh(script: "shell command", returnStdout: true).trim()
③
GroovyShell()
//直接执行Groovy代码 GroovyShell shell = new GroovyShell();shell.evaluate("\'calc\'.execute()"); //通过加载本地脚本 //1. GroovyShell shell = new GroovyShell(); Script script = shell.parse(new File("src/main/java/ysoserial/vulndemo/GroovyTest.groovy")); script.run(); //2. GroovyShell shell = new GroovyShell(); shell.evaluate(new File("src/main/java/ysoserial/vulndemo/GroovyTest.groovy")); //通过加载远程脚本 GroovyShell shell = new GroovyShell();shell.evaluate(new URI("http://127.0.0.1:8888/GroovyTest.groovy"));
3 连接符进行命令注入
如下代码不存在命令注入漏洞
protected ByteArrayOutputStream ping(String url) throws IOException{
Process process = Runtime.getRuntime().exec("ping " + url);
InputStream in = process.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int i = -1;
while((i = in.read(b)) != -1){
byteArrayOutputStream.write(b,0,i);
}
return byteArrayOutputStream;
}
若注入www.baidu.com&ipconfig
时,java环境会把他当做完整的字符串,不会被当作两条命令执行。
对于Java环境中的命令注入,连接符的使用存在一些局限。
4 正确处理可控参数
public Process exec(String command) throws IOException {
return exec(command, null, null);
}
public Process exec(String command, String[] envp) throws IOException {
return exec(command, envp, null);
}
public Process exec(String cmdarray[]) throws IOException {
return exec(cmdarray, null, null);
}
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
当传入的参数类型为字符串时,会先经过StringTokenizer
的处理,主要是针对空格以及换行符等空白字符进行处理,后续会分割出一个cmdarray数组保存分割后的命令参数,其中cmdarray的第一个元素为所要执行的命令。经过处理后的参数www.baidu.com&ipconfig
成为ping命令的参数,因此此时的连接符&并不生效,从而无法注入系统命令。