VYPR
Low severityNVD Advisory· Published Jun 28, 2025· Updated Mar 30, 2026

Apache Seata (incubating): Deserialization of untrusted Data in Apache Seata Server

CVE-2025-32897

Description

Deserialization of Untrusted Data vulnerability in Apache Seata (incubating).

This security vulnerability is the same as CVE-2024-47552, but the version range described in the CVE-2024-47552 definition is too narrow. This issue affects Apache Seata (incubating): from 2.0.0 before 2.3.0.

Severity Justification: The Apache Seata security team assesses the severity of this vulnerability as "Low" due to stringent real-world mitigating factors. First, the vulnerability is strictly isolated to the Raft cluster mode, an optional and non-default feature introduced in v2.0.0, while most users rely on the unaffected traditional architecture. Second, Seata is an internal middleware; communication between TC and RM/TM occurs entirely within trusted internal networks. An attacker would require prior, unauthorized access to the Intranet to exploit this, making external exploitation highly improbable. Users are recommended to upgrade to version 2.3.0, which fixes the issue.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Apache Seata (incubating) versions 2.0.0 to 2.2.x are vulnerable to deserialization of untrusted data in Raft cluster mode, fixed in 2.3.0.

Vulnerability

Overview

CVE-2025-32897 is a deserialization of untrusted data vulnerability in Apache Seata (incubating). The flaw is identical to CVE-2024-47552 but corrects the affected version range to include all versions from 2.0.0 before 2.3.0. The vulnerability exists in the Raft cluster mode, an optional feature introduced in v2.0.0, where the RaftSyncMessage deserialization process does not sufficiently restrict which classes can be deserialized [1][2].

Attack

Vector and Prerequisites

Exploitation requires an attacker to have prior unauthorized access to the internal network where Seata components (Transaction Coordinator, Resource Manager, Transaction Manager) communicate. As Seata is designed as internal middleware, communication typically occurs within trusted networks, making remote external exploitation highly improbable. The vulnerability is further isolated to Raft cluster mode, which is not the default deployment architecture [2].

Impact

A successful attacker could craft malicious serialized data that, when deserialized by the RaftSyncMessage decoder, leads to arbitrary code execution or other harmful side effects within the Seata server process. Given the required network access and the optional feature restriction, the Apache Seata security team rates the severity as Low [2].

Mitigation

The fix is included in Apache Seata version 2.3.0. The patch introduces a whitelist of permitted classes for deserialization (RaftSyncMessage, a legacy package name, and byte arrays), rejecting any other class via a runtime exception [3]. Users running Raft cluster mode on versions 2.0.0 through 2.2.x should upgrade immediately. Users not using Raft mode are not affected [2].

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.seata:seata-config-coreMaven
>= 2.0.0, < 2.3.02.3.0

Affected products

2
  • Apache/Seatallm-fuzzy
    Range: >=2.0.0 <2.3.0
  • Apache Software Foundation/Apache Seata (incubating)v5
    Range: 2.0.0

Patches

1
20cd9625d23f

security: fix REC security vulnerability during Raft deserialization (#6702)

7 files changed · +95 10
  • changes/en-us/2.x.md+1 1 modified
    @@ -36,7 +36,7 @@ Add changes here for all PR submitted to the 2.x branch.
     ### refactor:
     
     ### security:
    -
    +- [[#6702](https://github.com/apache/incubator-seata/pull/6702)]  fix REC security vulnerability during Raft deserialization
     
     ### test:
     - [[#6608](https://github.com/apache/incubator-seata/pull/6608)] add unit test for sql-parser-core
    
  • changes/zh-cn/2.x.md+1 0 modified
    @@ -38,6 +38,7 @@
     
     
     ### security:
    +- [[#6702](https://github.com/apache/incubator-seata/pull/6702)]  修复Raft反序列化时存在RCE安全漏洞
     
     ### test:
     - [[#6608](https://github.com/apache/incubator-seata/pull/6608)] 添加sql-parser-core模块测试用例
    
  • common/src/main/java/org/apache/seata/common/exception/ErrorCode.java+11 2 modified
    @@ -24,11 +24,16 @@ public enum ErrorCode {
         /**
          * 0001 ~ 0099  Configuration related errors
          */
    -    ERR_CONFIG(ErrorType.Config, 0001);
    +    ERR_CONFIG(ErrorType.Config, 0001),
    +
         /**
    -     * The error code of the transaction exception.
    +     * 0100 ~ 0199 Security related errors
          */
    +    ERR_DESERIALIZATION_SECURITY(ErrorType.Security, 0156);
     
    +    /**
    +     * The error code of the transaction exception.
    +     */
         private int code;
         private ErrorType type;
     
    @@ -77,6 +82,10 @@ enum ErrorType {
              * Network error type.
              */
             Network,
    +        /**
    +         * Security related error type.
    +         */
    +        Security,
             /**
              * Tm error type.
              */
    
  • common/src/main/resources/error/ErrorCode_en.properties+2 1 modified
    @@ -16,4 +16,5 @@
     #
     ERR_PREFIX=ERR-CODE: [Seata-{code}][{key}]
     ERR_POSTFIX=More: [https://seata.apache.org/docs/next/overview/faq#{code}]
    -ERR_CONFIG=config error, {0}
    \ No newline at end of file
    +ERR_CONFIG=config error, {0}
    +ERR_DESERIALIZATION_SECURITY=deserialization security error, {0}
    \ No newline at end of file
    
  • server/src/main/java/org/apache/seata/server/cluster/raft/sync/RaftSyncMessageSerializer.java+32 6 modified
    @@ -21,7 +21,12 @@
     import java.io.IOException;
     import java.io.ObjectInputStream;
     import java.io.ObjectOutputStream;
    +import java.io.ObjectStreamClass;
    +import java.util.ArrayList;
    +import java.util.List;
     import java.util.Optional;
    +import org.apache.seata.common.exception.ErrorCode;
    +import org.apache.seata.common.exception.SeataRuntimeException;
     import org.apache.seata.common.loader.EnhancedServiceLoader;
     import org.apache.seata.core.compressor.CompressorFactory;
     import org.apache.seata.core.serializer.Serializer;
    @@ -36,6 +41,14 @@ public class RaftSyncMessageSerializer {
     
         private static final Logger LOGGER = LoggerFactory.getLogger(RaftSyncMessageSerializer.class);
     
    +    private static final List<String> PERMITS = new ArrayList<>();
    +
    +    static {
    +        PERMITS.add(RaftSyncMessage.class.getName());
    +        PERMITS.add(io.seata.server.cluster.raft.sync.msg.RaftSyncMessage.class.getName());
    +        PERMITS.add("[B");
    +    }
    +
         public static byte[] encode(RaftSyncMessage raftSyncMessage) throws IOException {
             try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
                     ObjectOutputStream oos = new ObjectOutputStream(bos)) {
    @@ -62,12 +75,22 @@ public static byte[] encode(io.seata.server.cluster.raft.sync.msg.RaftSyncMessag
     
         public static RaftSyncMessage decode(byte[] raftSyncMsgByte) {
             try (ByteArrayInputStream bin = new ByteArrayInputStream(raftSyncMsgByte);
    -                ObjectInputStream ois = new ObjectInputStream(bin)) {
    +            ObjectInputStream ois = new ObjectInputStream(bin) {
    +                @Override
    +                protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
    +                    if (!PERMITS.contains(desc.getName())) {
    +                        throw new SeataRuntimeException(ErrorCode.ERR_DESERIALIZATION_SECURITY,
    +                            "Failed to deserialize object: " + desc.getName() + " is not permitted");
    +                    }
    +
    +                    return super.resolveClass(desc);
    +                }
    +            }) {
                 Object object = ois.readObject();
                 RaftSyncMessage raftSyncMessage;
                 if (object instanceof io.seata.server.cluster.raft.sync.msg.RaftSyncMessage) {
                     io.seata.server.cluster.raft.sync.msg.RaftSyncMessage oldRaftSyncMessage =
    -                        (io.seata.server.cluster.raft.sync.msg.RaftSyncMessage)object;
    +                    (io.seata.server.cluster.raft.sync.msg.RaftSyncMessage)object;
                     raftSyncMessage = new RaftSyncMessage();
                     raftSyncMessage.setCodec(oldRaftSyncMessage.getCodec());
                     raftSyncMessage.setCompressor(oldRaftSyncMessage.getCompressor());
    @@ -77,13 +100,16 @@ public static RaftSyncMessage decode(byte[] raftSyncMsgByte) {
                     raftSyncMessage = (RaftSyncMessage)object;
                 }
                 Serializer serializer = EnhancedServiceLoader.load(Serializer.class,
    -                    SerializerType.getByCode(raftSyncMessage.getCodec()).name());
    +                SerializerType.getByCode(raftSyncMessage.getCodec()).name());
                 Optional.ofNullable(raftSyncMessage.getBody())
    -                    .ifPresent(value -> raftSyncMessage.setBody(serializer.deserialize(CompressorFactory
    -                            .getCompressor(raftSyncMessage.getCompressor()).decompress((byte[])raftSyncMessage.getBody()))));
    +                .ifPresent(value -> raftSyncMessage.setBody(serializer.deserialize(CompressorFactory
    +                    .getCompressor(raftSyncMessage.getCompressor()).decompress((byte[])raftSyncMessage.getBody()))));
                 return raftSyncMessage;
    -        } catch (ClassNotFoundException | IOException e) {
    +        } catch (Exception e) {
                 LOGGER.info("Failed to read raft synchronization log: {}", e.getMessage(), e);
    +            if (e instanceof SeataRuntimeException) {
    +                throw (SeataRuntimeException)e;
    +            }
                 throw new RuntimeException(e);
             }
         }
    
  • server/src/test/java/org/apache/seata/server/raft/RaftSyncMessageTest.java+16 0 modified
    @@ -16,12 +16,15 @@
      */
     package org.apache.seata.server.raft;
     
    +import java.io.ByteArrayOutputStream;
     import java.io.IOException;
    +import java.io.ObjectOutputStream;
     import java.util.ArrayList;
     import java.util.HashMap;
     import java.util.List;
     import java.util.Map;
     
    +import org.apache.seata.common.exception.SeataRuntimeException;
     import org.apache.seata.common.metadata.ClusterRole;
     import org.apache.seata.common.metadata.Node;
     import org.apache.seata.core.exception.TransactionException;
    @@ -62,6 +65,19 @@ public static void setUp(ApplicationContext context){
         public static void destroy(){
             SessionHolder.destroy();
         }
    +
    +    @Test
    +    public void testSecurityMsgSerialize() throws IOException {
    +        TestSecurity testSecurity = new TestSecurity();
    +        byte[] bytes;
    +        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
    +            ObjectOutputStream oos = new ObjectOutputStream(bos)) {
    +            oos.writeObject(testSecurity);
    +            bytes =  bos.toByteArray();
    +        }
    +        Assertions.assertThrows(SeataRuntimeException.class,()->RaftSyncMessageSerializer.decode(bytes));
    +    }
    +
         @Test
         public void testMsgSerialize() throws IOException {
             RaftSyncMessage raftSyncMessage = new RaftSyncMessage();
    
  • server/src/test/java/org/apache/seata/server/raft/TestSecurity.java+32 0 added
    @@ -0,0 +1,32 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF licenses this file to You under the Apache License, Version 2.0
    + * (the "License"); you may not use this file except in compliance with
    + * the License.  You may obtain a copy of the License at
    + *
    + *     http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing, software
    + * distributed under the License is distributed on an "AS IS" BASIS,
    + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    + * See the License for the specific language governing permissions and
    + * limitations under the License.
    + */
    +package org.apache.seata.server.raft;
    +
    +public class TestSecurity implements java.io.Serializable {
    +
    +	private static final long serialVersionUID = 543214259201495900L;
    +
    +	String a = "test";
    +
    +	public String getA() {
    +		return a;
    +	}
    +
    +	public void setA(String a) {
    +		this.a = a;
    +	}
    +}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.