Apache Tomcat是在Apache Software Foundation (ASF)支持下开发的开源Java Servlet容器,实现了多个Java EE规范,包括Java Servlet, JavaServer Pages (JSP), Java Expression Language (EL), WebSocket,提供一个Java代码运行的纯Java HTTP web服务器环境。
4月15日,Nightwatch Cybersecurity公布了Apache Tomcat的Common Gateway Interface (CGI) Servlet的远程代码执行漏洞。攻击者利用该高危漏洞可以滥用Tomcat CGI Servlet输入有效性错误导致的操作系统命令注入来执行任意命令。
CGI是用来管理web服务器与应用交互的协议。CGI Servlet是用来生成来自查询字符串的命令行参数的,默认是禁用的。但是运行在Windows机器上的启用了CGI Servlet参数enableCmdLineArguments的Tomcat服务器由于Java Runtime Environment (JRE)传递命令行参数到Windows时的bug可能会导致远程代码执行。
在Apache Tomcat中,文件web.xml用来为加载到Tomcat实例的所有web应用定义默认值。CGI Servlet是默认提供的servlets。Servlet支持符合CGI说明的外部应用的执行。CGI Servlet映射到URL模式“/cgi-bin/*”,表明所有执行的CGI应用必须在web应用中。
通过调用CreateProcess()函数来启动Windows操作系统中的新进程,该函数将以下命令作为字符串:
int CreateProcess( …, lpComandLine, … )
在Windows中,参数并不是以字符串数组的形式传递的,二是以单个命令行字符串的形式传递的。这要求程序用GetCommandLine() API来提取命令行参数,来分析命令行,然后用CommandLineArgvW() helper函数来分析参数字符串。
如下图所示:
Cmdline = “program.exe hello world”
图1. Windows中的命令行字符串
Argv[0]->program.exe Argv[1]->hello Argv[2]->world
漏洞是因为从JRE到Windows的命令行参数的不适当传递引发的。
对Java应用来说,ProcessBuilder()是在CreateProcess()函数之前被调用的。然后参数会传递给ProcessImpl()的静态方法开始,这是一个与平台无法的类。在ProcessImpl()的Windows实现中,开始方法调用ProcessImpl()的所有构造器来为CreateProcess调用创建命令行。
图2. Java apps的命令行字符串
ProcessImpl() 构建了Cmdline,并传递给CreateProcess() Windows函数,然后CreateProcess()在cmd.exe shell环境中执行.bat和.cmd文件。如果文件要运行的文件含有.bat或.cmd扩展,要运行的镜像就会变成cmd.exe。CreateProcess()会在Stage 1重启,并将batch文件的名传递给cmd.exe作为第一个参数。
hello.bat …中的结果会变成‘C:/Windows/system32/cmd.exe /c “hello.bat …”‘。因为CommandLineToArgvW 的引用规则与cmd的引用规则不同,也就是说需要应用其他的引用规则来避免cmd.exe翻译时的命令行注入。
因为Java (ProcessImpl())对cmd.exe传递的参数的调用没有额外的引用,cmd.exe处理的参数会被执行,如果参数没有以适当的方式传递给cmd.exe就会引发问题。
cmd的作用就是一个文本预处理器,给定一个命令行,cmd负责进行一系列的文本转化,然后将这些转化后的命令行交给CreateProcess()。
转化的过程会将环境变量名替换为对应的值。转化一般是由&, ||, &&操作符来触发的,将命令行分割为多个部分。所有的cmd转化都是由以下元字符来触发的:(, ), %, !, ^, “, <, >, &, and |。
Cmd在处理元字符“时,会复制“到新的命令行,然后复制其他命令行字符到新的命令行,并不会考虑这些字符串是否元字符。
如果用cmd的元字符“来保护参数,使用引号可以产生意外的行为。将不可信的数据作为命令行参数椽笔,不匹配发送时就可能会产生安全漏洞,比如:
hello.bat “dir /”&whoami” 0: [hello.bat] 1: [&dir]
其中cmd会将&元字符作为命令分隔符,因为&是在引号区域外的。因此,whoami可以被任意代码所替代。在运行上面的hello.bat时会得到下面的输出结果:
图3. 运行“hello.bat”的输出结果
利用该问题可以用Apache Tomcat来执行任意代码,结果如下:
图4. Apache Tomcat中的命令执行
为了成功执行命令注入,需要添加一些参数,并在web.xml文件中启用CGI Servlet。
图5. web.xml
Apache Software Foundation在Apache Tomcat CGI Servlet中引入了新的参数cmdLineArgumentsDecoded来解决CVE-2019-0232漏洞。只有当enableCmdLineArguments被设置为true时才使用cmdLineArgumentsDecoded参数。它定义了一个解码的命令行参数必须匹配的正则模型“[[a-zA-Z0-9/Q-_.///:/E]+]”,如果不匹配请求就会被拒绝。
图6. Apache Tomcat补丁
建议
Apache Software Foundation建议运行Apache Tomcat的用户升级软件到最新版本:
Apache Tomcat 9->Apache Tomcat 9.0.18 or later
Apache Tomcat 8->Apache Tomcat 8.5.40 or later
Apache Tomcat 7->Apache Tomcat 7.0.93 or later
而且用户应当将CGI Servlet初始化参数enableCmdLineArguments设置为False来预防潜在的漏洞滥用。