VYPR
Critical severityNVD Advisory· Published Jul 19, 2018· Updated Sep 17, 2024

CVE-2018-8018

CVE-2018-8018

Description

Apache Ignite deserialization flaw allows arbitrary code execution via specially crafted serialized object sent to GridClientJdkMarshaller endpoint.

AI Insight

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

Apache Ignite deserialization flaw allows arbitrary code execution via specially crafted serialized object sent to GridClientJdkMarshaller endpoint.

Vulnerability

In Apache Ignite versions before 2.4.8 and 2.5.x before 2.5.3, the serialization mechanism lacks a whitelist of allowed classes, enabling arbitrary code execution if third-party vulnerable classes are present in the classpath [2][4]. The flaw resides in the GridClientJdkMarshaller deserialization endpoint [2].

Exploitation

An attacker can send a specially crafted serialized object to the GridClientJdkMarshaller endpoint over the network, without authentication required [2][4]. Successful exploitation requires the presence of exploitable gadget classes on the Ignite classpath, which can be leveraged to execute arbitrary code [4].

Impact

Successful exploitation results in arbitrary code execution in the context of the Ignite server process, leading to full compromise of confidentiality, integrity, and availability [2][3][4].

Mitigation

The vulnerability is fixed in Apache Ignite versions 2.4.8 and 2.5.3 [1][2][4]. Red Hat released an update as part of RHSA-2018:3768 for Red Hat Fuse 7.2 [3]. Users should upgrade to the patched versions. No workaround is documented in the available references.

AI Insight generated on May 22, 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.ignite:ignite-coreMaven
< 2.62.6

Affected products

2

Patches

2
82a7b8209fcf

IGNITE-8565 Client marshalling improvements

https://github.com/apache/igniteAndrey GuraJun 7, 2018via ghsa
10 files changed · +494 115
  • modules/clients/src/test/java/org/apache/ignite/internal/client/suite/IgniteClientTestSuite.java+2 0 modified
    @@ -58,6 +58,7 @@
     import org.apache.ignite.internal.processors.rest.RestProcessorMultiStartSelfTest;
     import org.apache.ignite.internal.processors.rest.RestProcessorStartSelfTest;
     import org.apache.ignite.internal.processors.rest.TaskCommandHandlerSelfTest;
    +import org.apache.ignite.internal.processors.rest.TcpRestUnmarshalVulnerabilityTest;
     import org.apache.ignite.internal.processors.rest.protocols.tcp.TcpRestParserSelfTest;
     import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.RedisProtocolConnectSelfTest;
     import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.RedisProtocolServerSelfTest;
    @@ -84,6 +85,7 @@ public static TestSuite suite() {
     
             // Test custom binary protocol with test client.
             suite.addTestSuite(RestBinaryProtocolSelfTest.class);
    +        suite.addTestSuite(TcpRestUnmarshalVulnerabilityTest.class);
     
             // Test jetty rest processor
             suite.addTestSuite(JettyRestProcessorSignedSelfTest.class);
    
  • modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/TcpRestUnmarshalVulnerabilityTest.java+269 0 added
    @@ -0,0 +1,269 @@
    +/*
    + * 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.ignite.internal.processors.rest;
    +
    +import java.io.BufferedInputStream;
    +import java.io.BufferedOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.ObjectInputStream;
    +import java.io.OutputStream;
    +import java.net.InetAddress;
    +import java.net.Socket;
    +import java.nio.ByteBuffer;
    +import java.util.UUID;
    +import java.util.concurrent.atomic.AtomicBoolean;
    +import org.apache.ignite.configuration.ConnectorConfiguration;
    +import org.apache.ignite.configuration.IgniteConfiguration;
    +import org.apache.ignite.internal.client.marshaller.jdk.GridClientJdkMarshaller;
    +import org.apache.ignite.internal.processors.rest.client.message.GridClientHandshakeRequest;
    +import org.apache.ignite.internal.processors.rest.client.message.GridClientMessage;
    +import org.apache.ignite.internal.util.IgniteUtils;
    +import org.apache.ignite.internal.util.lang.GridAbsPredicate;
    +import org.apache.ignite.internal.util.typedef.internal.U;
    +import org.apache.ignite.testframework.GridTestUtils;
    +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
    +
    +import static org.apache.ignite.IgniteSystemProperties.IGNITE_MARSHALLER_BLACKLIST;
    +import static org.apache.ignite.IgniteSystemProperties.IGNITE_MARSHALLER_WHITELIST;
    +import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.IGNITE_HANDSHAKE_FLAG;
    +import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.IGNITE_REQ_FLAG;
    +
    +/**
    + * Tests for whitelist and blacklist ot avoiding deserialization vulnerability.
    + */
    +public class TcpRestUnmarshalVulnerabilityTest extends GridCommonAbstractTest {
    +    /** Marshaller. */
    +    private static final GridClientJdkMarshaller MARSH = new GridClientJdkMarshaller();
    +
    +    /** Shared value. */
    +    private static final AtomicBoolean SHARED = new AtomicBoolean();
    +
    +    /** Port. */
    +    private static int port;
    +
    +    /** Host. */
    +    private static String host;
    +
    +    /** {@inheritDoc} */
    +    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
    +        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
    +
    +        ConnectorConfiguration connCfg = new ConnectorConfiguration();
    +
    +        port = connCfg.getPort();
    +        host = connCfg.getHost();
    +
    +        cfg.setConnectorConfiguration(connCfg);
    +
    +        return cfg;
    +    }
    +
    +    /** {@inheritDoc} */
    +    @Override protected void beforeTest() throws Exception {
    +        super.beforeTest();
    +
    +        SHARED.set(false);
    +
    +        System.clearProperty(IGNITE_MARSHALLER_WHITELIST);
    +        System.clearProperty(IGNITE_MARSHALLER_BLACKLIST);
    +
    +        IgniteUtils.clearClassCache();
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testNoLists() throws Exception {
    +        testExploit(true);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testWhiteListIncluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_included.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_WHITELIST, path);
    +
    +        testExploit(true);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testWhiteListExcluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_excluded.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_WHITELIST, path);
    +
    +        testExploit(false);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testBlackListIncluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_included.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_BLACKLIST, path);
    +
    +        testExploit(false);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testBlackListExcluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_excluded.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_BLACKLIST, path);
    +
    +        testExploit(true);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testBothListIncluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_included.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_WHITELIST, path);
    +        System.setProperty(IGNITE_MARSHALLER_BLACKLIST, path);
    +
    +        testExploit(false);
    +    }
    +
    +    /**
    +     * @param positive Positive.
    +     */
    +    private void testExploit(boolean positive) throws Exception {
    +        try {
    +            startGrid();
    +
    +            attack(marshal(new Exploit()).array());
    +
    +            boolean res = GridTestUtils.waitForCondition(new GridAbsPredicate() {
    +                @Override public boolean apply() {
    +                    return SHARED.get();
    +                }
    +            }, 3000L);
    +
    +            if (positive)
    +                assertTrue(res);
    +            else
    +                assertFalse(res);
    +        }
    +        finally {
    +            stopAllGrids();
    +        }
    +    }
    +
    +    /**
    +     * @param obj Object.
    +     */
    +    private static ByteBuffer marshal(Object obj) throws IOException {
    +        return MARSH.marshal(obj, 0);
    +    }
    +
    +    /**
    +     * @param data Data.
    +     */
    +    private void attack(byte[] data) throws IOException {
    +        InetAddress addr = InetAddress.getByName(host);
    +
    +        try (
    +            Socket sock = new Socket(addr, port);
    +            OutputStream os = new BufferedOutputStream(sock.getOutputStream())
    +        ) {
    +            // Handshake request.
    +            os.write(IGNITE_HANDSHAKE_FLAG);
    +
    +            GridClientHandshakeRequest req = new GridClientHandshakeRequest();
    +            req.marshallerId(GridClientJdkMarshaller.ID);
    +            os.write(req.rawBytes());
    +            os.flush();
    +
    +            // Handshake response
    +            InputStream is = new BufferedInputStream(sock.getInputStream());
    +
    +            is.read(new byte[146]); // Read handshake response.
    +
    +            int len = data.length + 40;
    +
    +            os.write(IGNITE_REQ_FLAG); // Package type.
    +            os.write((byte)(len >> 24)); // Package length.
    +            os.write((byte)(len >> 16));
    +            os.write((byte)(len >> 8));
    +            os.write((byte)(len));
    +            os.write(new byte[40]); // Stream header.
    +            os.write(data); // Exploit.
    +            os.flush();
    +        }
    +    }
    +
    +    /** */
    +    private static class Exploit implements GridClientMessage {
    +        /**
    +         * @param is Input stream.
    +         */
    +        private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
    +            SHARED.set(true);
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public long requestId() {
    +            return 0;
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public void requestId(long reqId) {
    +            // No-op.
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public UUID clientId() {
    +            return null;
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public void clientId(UUID id) {
    +            // No-op.
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public UUID destinationId() {
    +            return null;
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public void destinationId(UUID id) {
    +            // No-op.
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public byte[] sessionToken() {
    +            return new byte[0];
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public void sessionToken(byte[] sesTok) {
    +            // No-op.
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • modules/clients/src/test/java/org/apache/ignite/loadtests/client/ClientMarshallerBenchmarkTest.java+12 4 modified
    @@ -22,11 +22,14 @@
     import java.util.HashMap;
     import java.util.Map;
     import java.util.UUID;
    +import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteException;
     import org.apache.ignite.internal.client.marshaller.GridClientMarshaller;
     import org.apache.ignite.internal.client.marshaller.jdk.GridClientJdkMarshaller;
     import org.apache.ignite.internal.client.marshaller.optimized.GridClientOptimizedMarshaller;
     import org.apache.ignite.internal.processors.rest.client.message.GridClientCacheRequest;
     import org.apache.ignite.internal.util.typedef.X;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
     
     import static org.apache.ignite.internal.processors.rest.client.message.GridClientCacheRequest.GridCacheOperation.CAS;
    @@ -41,10 +44,15 @@ public class ClientMarshallerBenchmarkTest extends GridCommonAbstractTest {
         /**
          */
         public ClientMarshallerBenchmarkTest() {
    -        marshallers = new GridClientMarshaller[] {
    -            new GridClientJdkMarshaller(),
    -            new GridClientOptimizedMarshaller()
    -        };
    +        try {
    +            marshallers = new GridClientMarshaller[] {
    +                new GridClientJdkMarshaller(MarshallerUtils.classNameFilter(this.getClass().getClassLoader())),
    +                new GridClientOptimizedMarshaller()
    +            };
    +        }
    +        catch (IgniteCheckedException e) {
    +            throw new IgniteException(e);
    +        }
         }
     
         /**
    
  • modules/core/src/main/java/org/apache/ignite/internal/client/marshaller/jdk/GridClientJdkMarshaller.java+50 1 modified
    @@ -19,13 +19,16 @@
     
     import java.io.ByteArrayInputStream;
     import java.io.IOException;
    +import java.io.InputStream;
     import java.io.ObjectInput;
     import java.io.ObjectInputStream;
     import java.io.ObjectOutput;
     import java.io.ObjectOutputStream;
    +import java.io.ObjectStreamClass;
     import java.nio.ByteBuffer;
     import org.apache.ignite.internal.client.marshaller.GridClientMarshaller;
     import org.apache.ignite.internal.util.io.GridByteArrayOutputStream;
    +import org.apache.ignite.lang.IgnitePredicate;
     
     /**
      * Simple marshaller that utilize JDK serialization features.
    @@ -34,6 +37,23 @@ public class GridClientJdkMarshaller implements GridClientMarshaller {
         /** ID. */
         public static final byte ID = 2;
     
    +    /** Class name filter. */
    +    private final IgnitePredicate<String> clsFilter;
    +
    +    /**
    +     * Default constructor.
    +     */
    +    public GridClientJdkMarshaller() {
    +        this(null);
    +    }
    +
    +    /**
    +     * @param clsFilter Class filter.
    +     */
    +    public GridClientJdkMarshaller(IgnitePredicate<String> clsFilter) {
    +        this.clsFilter = clsFilter;
    +    }
    +
         /** {@inheritDoc} */
         @Override public ByteBuffer marshal(Object obj, int off) throws IOException {
             GridByteArrayOutputStream bOut = new GridByteArrayOutputStream();
    @@ -60,7 +80,7 @@ public class GridClientJdkMarshaller implements GridClientMarshaller {
         @Override public <T> T unmarshal(byte[] bytes) throws IOException {
             ByteArrayInputStream tmp = new ByteArrayInputStream(bytes);
     
    -        ObjectInput in = new ObjectInputStream(tmp);
    +        ObjectInput in = new ClientJdkInputStream(tmp, clsFilter);
     
             try {
                 return (T)in.readObject();
    @@ -69,4 +89,33 @@ public class GridClientJdkMarshaller implements GridClientMarshaller {
                 throw new IOException("Failed to unmarshal target object: " + e.getMessage(), e);
             }
         }
    +
    +    /**
    +     * Wrapper with class resolving control.
    +     */
    +    private static class ClientJdkInputStream extends ObjectInputStream {
    +        /** Class name filter. */
    +        private final IgnitePredicate<String> clsFilter;
    +
    +
    +        /**
    +         * @param in Input stream.
    +         * @param clsFilter Class filter.
    +         */
    +        public ClientJdkInputStream(InputStream in, IgnitePredicate<String> clsFilter) throws IOException {
    +            super(in);
    +
    +            this.clsFilter = clsFilter;
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
    +            String clsName = desc.getName();
    +
    +            if (clsFilter != null && !clsFilter.apply(clsName))
    +                throw new RuntimeException("Deserialization of class " + clsName + " is disallowed.");
    +
    +            return super.resolveClass(desc);
    +        }
    +    }
     }
    \ No newline at end of file
    
  • modules/core/src/main/java/org/apache/ignite/internal/client/router/impl/GridTcpRouterNioListenerAdapter.java+13 1 modified
    @@ -23,6 +23,8 @@
     import java.util.List;
     import java.util.Map;
     import java.util.UUID;
    +import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteException;
     import org.apache.ignite.IgniteLogger;
     import org.apache.ignite.failure.FailureType;
     import org.apache.ignite.internal.client.GridClientException;
    @@ -42,6 +44,8 @@
     import org.apache.ignite.internal.util.nio.GridNioServerListener;
     import org.apache.ignite.internal.util.nio.GridNioSession;
     import org.apache.ignite.internal.util.typedef.internal.U;
    +import org.apache.ignite.lang.IgnitePredicate;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     import org.apache.ignite.plugin.PluginProvider;
     import org.jetbrains.annotations.Nullable;
     
    @@ -87,7 +91,15 @@ public GridTcpRouterNioListenerAdapter(IgniteLogger log, GridRouterClientImpl cl
     
             marshMap.put(GridClientOptimizedMarshaller.ID, optdMarsh);
             marshMap.put(GridClientZipOptimizedMarshaller.ID, new GridClientZipOptimizedMarshaller(optdMarsh, providers));
    -        marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller());
    +
    +        try {
    +            IgnitePredicate<String> clsFilter = MarshallerUtils.classNameFilter(this.getClass().getClassLoader());
    +
    +            marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller(clsFilter));
    +        }
    +        catch (IgniteCheckedException e) {
    +            throw new IgniteException(e);
    +        }
     
             init();
         }
    
  • modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java+2 106 modified
    @@ -17,14 +17,10 @@
     
     package org.apache.ignite.internal;
     
    -import java.io.BufferedReader;
     import java.io.Externalizable;
     import java.io.File;
    -import java.io.FileInputStream;
    -import java.io.FileNotFoundException;
     import java.io.IOException;
     import java.io.InputStream;
    -import java.io.InputStreamReader;
     import java.io.InvalidObjectException;
     import java.io.ObjectInput;
     import java.io.ObjectOutput;
    @@ -196,6 +192,7 @@
     import org.apache.ignite.lifecycle.LifecycleBean;
     import org.apache.ignite.lifecycle.LifecycleEventType;
     import org.apache.ignite.marshaller.MarshallerExclusions;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     import org.apache.ignite.marshaller.jdk.JdkMarshaller;
     import org.apache.ignite.mxbean.ClusterMetricsMXBean;
     import org.apache.ignite.mxbean.IgniteMXBean;
    @@ -273,8 +270,6 @@
     import static org.apache.ignite.internal.IgniteVersionUtils.VER_STR;
     import static org.apache.ignite.lifecycle.LifecycleEventType.AFTER_NODE_START;
     import static org.apache.ignite.lifecycle.LifecycleEventType.BEFORE_NODE_START;
    -import static org.apache.ignite.marshaller.MarshallerUtils.CLS_NAMES_FILE;
    -import static org.apache.ignite.marshaller.MarshallerUtils.JDK_CLS_NAMES_FILE;
     
     /**
      * Ignite kernal.
    @@ -888,7 +883,7 @@ public void start(
                     schemaExecSvc,
                     customExecSvcs,
                     plugins,
    -                classNameFilter()
    +                MarshallerUtils.classNameFilter(this.getClass().getClassLoader())
                 );
     
                 cfg.getMarshaller().setContext(ctx.marshallerContext());
    @@ -1748,105 +1743,6 @@ private void startProcessor(GridProcessor proc) throws IgniteCheckedException {
             }
         }
     
    -    /**
    -     * Returns class name filter for marshaller.
    -     *
    -     * @return Class name filter for marshaller.
    -     */
    -    private IgnitePredicate<String> classNameFilter() throws IgniteCheckedException {
    -        ClassSet whiteList = classWhiteList();
    -        ClassSet blackList = classBlackList();
    -
    -        return new IgnitePredicate<String>() {
    -            @Override public boolean apply(String s) {
    -                // Allows all primitive arrays and checks arrays' type.
    -                if ((blackList != null || whiteList != null) && s.charAt(0) == '[') {
    -                    if (s.charAt(1) == 'L' && s.length() > 2)
    -                        s = s.substring(2, s.length() - 1);
    -                    else
    -                        return true;
    -                }
    -
    -                return (blackList == null || !blackList.contains(s)) && (whiteList == null || whiteList.contains(s));
    -            }
    -        };
    -    }
    -
    -    /**
    -     * @return White list of classes.
    -     */
    -    private ClassSet classWhiteList() throws IgniteCheckedException {
    -        ClassSet clsSet = null;
    -
    -        String fileName = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_MARSHALLER_WHITELIST);
    -
    -        if (fileName != null) {
    -            clsSet = new ClassSet();
    -
    -            addClassNames(JDK_CLS_NAMES_FILE, clsSet);
    -            addClassNames(CLS_NAMES_FILE, clsSet);
    -            addClassNames(fileName, clsSet);
    -        }
    -
    -        return clsSet;
    -    }
    -
    -    /**
    -     * @return Black list of classes.
    -     */
    -    private ClassSet classBlackList() throws IgniteCheckedException {
    -        ClassSet clsSet = null;
    -
    -        String blackListFileName = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_MARSHALLER_BLACKLIST);
    -
    -        if (blackListFileName != null)
    -            addClassNames(blackListFileName, clsSet = new ClassSet());
    -
    -        return clsSet;
    -    }
    -
    -    /**
    -     * Reads class names from resource referred by given system property name and returns set of classes.
    -     *
    -     * @param fileName File name containing list of classes.
    -     * @param clsSet Class set for update.
    -     * @return Set of classes.
    -     */
    -    private void addClassNames(String fileName, ClassSet clsSet) throws IgniteCheckedException {
    -        InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName);
    -
    -        if (is == null) {
    -            try {
    -                is = new FileInputStream(new File(fileName));
    -            }
    -            catch (FileNotFoundException e) {
    -                throw new IgniteCheckedException("File " + fileName + " not found.");
    -            }
    -        }
    -
    -        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
    -            String line;
    -
    -            for (int i = 1; (line = reader.readLine()) != null; i++) {
    -                String s = line.trim();
    -
    -                if (!s.isEmpty() && s.charAt(0) != '#' && s.charAt(0) != '[') {
    -                    try {
    -                        clsSet.add(s);
    -                    }
    -                    catch (IllegalArgumentException e) {
    -                        throw new IgniteCheckedException("Exception occurred while reading list of classes" +
    -                            "[path=" + fileName + ", row=" + i + ", line=" + s + ']', e);
    -                    }
    -                }
    -            }
    -        }
    -        catch (IOException e) {
    -            throw new IgniteCheckedException("Exception occurred while reading and creating list of classes " +
    -                "[path=" + fileName + ']', e);
    -        }
    -    }
    -
         /**
          * Add helper.
          *
    
  • modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestProtocol.java+12 1 modified
    @@ -27,6 +27,7 @@
     import javax.net.ssl.SSLContext;
     import javax.net.ssl.SSLException;
     import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteException;
     import org.apache.ignite.configuration.ConnectorConfiguration;
     import org.apache.ignite.configuration.IgniteConfiguration;
     import org.apache.ignite.internal.GridKernalContext;
    @@ -46,6 +47,8 @@
     import org.apache.ignite.internal.util.nio.GridNioServerListener;
     import org.apache.ignite.internal.util.nio.ssl.GridNioSslFilter;
     import org.apache.ignite.internal.util.typedef.internal.U;
    +import org.apache.ignite.lang.IgnitePredicate;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     import org.apache.ignite.plugin.PluginProvider;
     import org.apache.ignite.spi.IgnitePortProtocol;
     import org.jetbrains.annotations.Nullable;
    @@ -149,7 +152,15 @@ else if (depFactory != null)
     
             marshMap.put(GridClientOptimizedMarshaller.ID, optMarsh);
             marshMap.put(GridClientZipOptimizedMarshaller.ID, new GridClientZipOptimizedMarshaller(optMarsh, providers));
    -        marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller());
    +
    +        try {
    +            IgnitePredicate<String> clsFilter = MarshallerUtils.classNameFilter(this.getClass().getClassLoader());
    +
    +            marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller(clsFilter));
    +        }
    +        catch (IgniteCheckedException e) {
    +            throw new IgniteException(e);
    +        }
     
             lsnr.marshallers(marshMap);
         }
    
  • modules/core/src/main/java/org/apache/ignite/marshaller/MarshallerUtils.java+117 0 modified
    @@ -17,6 +17,17 @@
     
     package org.apache.ignite.marshaller;
     
    +import java.io.BufferedReader;
    +import java.io.File;
    +import java.io.FileInputStream;
    +import java.io.FileNotFoundException;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.InputStreamReader;
    +import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteSystemProperties;
    +import org.apache.ignite.internal.ClassSet;
    +import org.apache.ignite.lang.IgnitePredicate;
     import org.apache.ignite.lang.IgniteProductVersion;
     import org.apache.ignite.marshaller.jdk.JdkMarshaller;
     import org.jetbrains.annotations.Nullable;
    @@ -83,4 +94,110 @@ public static void jobSenderVersion(IgniteProductVersion ver) {
         public static IgniteProductVersion jobSenderVersion() {
             return JOB_SND_NODE_VER.get();
         }
    +
    +    /**
    +     * Returns class name filter for marshaller.
    +     *
    +     * @return Class name filter for marshaller.
    +     */
    +    public static IgnitePredicate<String> classNameFilter(ClassLoader clsLdr) throws IgniteCheckedException {
    +        ClassSet whiteList = classWhiteList(clsLdr);
    +        ClassSet blackList = classBlackList(clsLdr);
    +
    +        return new IgnitePredicate<String>() {
    +            @Override public boolean apply(String s) {
    +                // Allows all primitive arrays and checks arrays' type.
    +                if ((blackList != null || whiteList != null) && s.charAt(0) == '[') {
    +                    if (s.charAt(1) == 'L' && s.length() > 2)
    +                        s = s.substring(2, s.length() - 1);
    +                    else
    +                        return true;
    +                }
    +
    +                return (blackList == null || !blackList.contains(s)) && (whiteList == null || whiteList.contains(s));
    +            }
    +        };
    +    }
    +
    +    /**
    +     * @param clsLdr Class loader.
    +     * @return White list of classes.
    +     */
    +    private static ClassSet classWhiteList(ClassLoader clsLdr) throws IgniteCheckedException {
    +        ClassSet clsSet = null;
    +
    +        String fileName = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_MARSHALLER_WHITELIST);
    +
    +        if (fileName != null) {
    +            clsSet = new ClassSet();
    +
    +            addClassNames(JDK_CLS_NAMES_FILE, clsSet, clsLdr);
    +            addClassNames(CLS_NAMES_FILE, clsSet, clsLdr);
    +            addClassNames(fileName, clsSet, clsLdr);
    +        }
    +
    +        return clsSet;
    +    }
    +
    +    /**
    +     * @param clsLdr Class loader.
    +     * @return Black list of classes.
    +     */
    +    private static ClassSet classBlackList(ClassLoader clsLdr) throws IgniteCheckedException {
    +        ClassSet clsSet = null;
    +
    +        String blackListFileName = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_MARSHALLER_BLACKLIST);
    +
    +        if (blackListFileName != null)
    +            addClassNames(blackListFileName, clsSet = new ClassSet(), clsLdr);
    +
    +        return clsSet;
    +    }
    +
    +    /**
    +     * Reads class names from resource referred by given system property name and returns set of classes.
    +     *
    +     * @param fileName File name containing list of classes.
    +     * @param clsSet Class set for update.
    +     * @param clsLdr Class loader.
    +     */
    +    private static void addClassNames(
    +        String fileName,
    +        ClassSet clsSet,
    +        ClassLoader clsLdr
    +    ) throws IgniteCheckedException {
    +        InputStream is = clsLdr.getResourceAsStream(fileName);
    +
    +        if (is == null) {
    +            try {
    +                is = new FileInputStream(new File(fileName));
    +            }
    +            catch (FileNotFoundException e) {
    +                throw new IgniteCheckedException("File " + fileName + " not found.");
    +            }
    +        }
    +
    +        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
    +            String line;
    +
    +            for (int i = 1; (line = reader.readLine()) != null; i++) {
    +                String s = line.trim();
    +
    +                if (!s.isEmpty() && s.charAt(0) != '#' && s.charAt(0) != '[') {
    +                    try {
    +                        clsSet.add(s);
    +                    }
    +                    catch (IllegalArgumentException e) {
    +                        throw new IgniteCheckedException("Exception occurred while reading list of classes" +
    +                            "[path=" + fileName + ", row=" + i + ", line=" + s + ']', e);
    +                    }
    +                }
    +            }
    +        }
    +        catch (IOException e) {
    +            throw new IgniteCheckedException("Exception occurred while reading and creating list of classes " +
    +                "[path=" + fileName + ']', e);
    +        }
    +    }
    +
     }
    
  • modules/core/src/test/config/class_list_exploit_included.txt+2 1 modified
    @@ -16,4 +16,5 @@
     #
     
     org.apache.ignite.spi.discovery.tcp.DiscoveryUnmarshalVulnerabilityTest$Exploit
    -org.apache.ignite.stream.socket.SocketStreamerUnmarshalVulnerabilityTest$Exploit
    \ No newline at end of file
    +org.apache.ignite.stream.socket.SocketStreamerUnmarshalVulnerabilityTest$Exploit
    +org.apache.ignite.internal.processors.rest.TcpRestUnmarshalVulnerabilityTest$Exploit
    \ No newline at end of file
    
  • modules/hadoop/src/main/java/org/apache/ignite/internal/processors/hadoop/mapreduce/MapReduceClient.java+15 1 modified
    @@ -17,6 +17,8 @@
     
     package org.apache.ignite.internal.processors.hadoop.mapreduce;
     
    +import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteException;
     import org.apache.ignite.internal.client.GridClient;
     import org.apache.ignite.internal.client.GridClientConfiguration;
     import org.apache.ignite.internal.client.GridClientException;
    @@ -26,6 +28,8 @@
     import java.io.IOException;
     import java.util.Collection;
     import java.util.concurrent.atomic.AtomicInteger;
    +import org.apache.ignite.lang.IgnitePredicate;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     
     import static org.apache.ignite.internal.client.GridClientProtocol.TCP;
     
    @@ -83,7 +87,17 @@ public GridClient client() throws IOException {
     
                         cliCfg.setProtocol(TCP);
                         cliCfg.setServers(addrs);
    -                    cliCfg.setMarshaller(new GridClientJdkMarshaller());
    +
    +                    try {
    +                        IgnitePredicate<String> clsFilter =
    +                            MarshallerUtils.classNameFilter(this.getClass().getClassLoader());
    +
    +                        cliCfg.setMarshaller(new GridClientJdkMarshaller(clsFilter));
    +                    }
    +                    catch (IgniteCheckedException e) {
    +                        throw new IgniteException(e);
    +                    }
    +
                         cliCfg.setMaxConnectionIdleTime(24 * 60 * 60 * 1000L); // 1 day.
                         cliCfg.setDaemon(true);
     
    
82a7b8209fcf

IGNITE-8565 Client marshalling improvements

https://github.com/apache/igniteAndrey GuraJun 7, 2018via ghsa
10 files changed · +494 115
  • modules/clients/src/test/java/org/apache/ignite/internal/client/suite/IgniteClientTestSuite.java+2 0 modified
    @@ -58,6 +58,7 @@
     import org.apache.ignite.internal.processors.rest.RestProcessorMultiStartSelfTest;
     import org.apache.ignite.internal.processors.rest.RestProcessorStartSelfTest;
     import org.apache.ignite.internal.processors.rest.TaskCommandHandlerSelfTest;
    +import org.apache.ignite.internal.processors.rest.TcpRestUnmarshalVulnerabilityTest;
     import org.apache.ignite.internal.processors.rest.protocols.tcp.TcpRestParserSelfTest;
     import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.RedisProtocolConnectSelfTest;
     import org.apache.ignite.internal.processors.rest.protocols.tcp.redis.RedisProtocolServerSelfTest;
    @@ -84,6 +85,7 @@ public static TestSuite suite() {
     
             // Test custom binary protocol with test client.
             suite.addTestSuite(RestBinaryProtocolSelfTest.class);
    +        suite.addTestSuite(TcpRestUnmarshalVulnerabilityTest.class);
     
             // Test jetty rest processor
             suite.addTestSuite(JettyRestProcessorSignedSelfTest.class);
    
  • modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/TcpRestUnmarshalVulnerabilityTest.java+269 0 added
    @@ -0,0 +1,269 @@
    +/*
    + * 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.ignite.internal.processors.rest;
    +
    +import java.io.BufferedInputStream;
    +import java.io.BufferedOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.ObjectInputStream;
    +import java.io.OutputStream;
    +import java.net.InetAddress;
    +import java.net.Socket;
    +import java.nio.ByteBuffer;
    +import java.util.UUID;
    +import java.util.concurrent.atomic.AtomicBoolean;
    +import org.apache.ignite.configuration.ConnectorConfiguration;
    +import org.apache.ignite.configuration.IgniteConfiguration;
    +import org.apache.ignite.internal.client.marshaller.jdk.GridClientJdkMarshaller;
    +import org.apache.ignite.internal.processors.rest.client.message.GridClientHandshakeRequest;
    +import org.apache.ignite.internal.processors.rest.client.message.GridClientMessage;
    +import org.apache.ignite.internal.util.IgniteUtils;
    +import org.apache.ignite.internal.util.lang.GridAbsPredicate;
    +import org.apache.ignite.internal.util.typedef.internal.U;
    +import org.apache.ignite.testframework.GridTestUtils;
    +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
    +
    +import static org.apache.ignite.IgniteSystemProperties.IGNITE_MARSHALLER_BLACKLIST;
    +import static org.apache.ignite.IgniteSystemProperties.IGNITE_MARSHALLER_WHITELIST;
    +import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.IGNITE_HANDSHAKE_FLAG;
    +import static org.apache.ignite.internal.processors.rest.protocols.tcp.GridMemcachedMessage.IGNITE_REQ_FLAG;
    +
    +/**
    + * Tests for whitelist and blacklist ot avoiding deserialization vulnerability.
    + */
    +public class TcpRestUnmarshalVulnerabilityTest extends GridCommonAbstractTest {
    +    /** Marshaller. */
    +    private static final GridClientJdkMarshaller MARSH = new GridClientJdkMarshaller();
    +
    +    /** Shared value. */
    +    private static final AtomicBoolean SHARED = new AtomicBoolean();
    +
    +    /** Port. */
    +    private static int port;
    +
    +    /** Host. */
    +    private static String host;
    +
    +    /** {@inheritDoc} */
    +    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
    +        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
    +
    +        ConnectorConfiguration connCfg = new ConnectorConfiguration();
    +
    +        port = connCfg.getPort();
    +        host = connCfg.getHost();
    +
    +        cfg.setConnectorConfiguration(connCfg);
    +
    +        return cfg;
    +    }
    +
    +    /** {@inheritDoc} */
    +    @Override protected void beforeTest() throws Exception {
    +        super.beforeTest();
    +
    +        SHARED.set(false);
    +
    +        System.clearProperty(IGNITE_MARSHALLER_WHITELIST);
    +        System.clearProperty(IGNITE_MARSHALLER_BLACKLIST);
    +
    +        IgniteUtils.clearClassCache();
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testNoLists() throws Exception {
    +        testExploit(true);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testWhiteListIncluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_included.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_WHITELIST, path);
    +
    +        testExploit(true);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testWhiteListExcluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_excluded.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_WHITELIST, path);
    +
    +        testExploit(false);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testBlackListIncluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_included.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_BLACKLIST, path);
    +
    +        testExploit(false);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testBlackListExcluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_excluded.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_BLACKLIST, path);
    +
    +        testExploit(true);
    +    }
    +
    +    /**
    +     * @throws Exception If failed.
    +     */
    +    public void testBothListIncluded() throws Exception {
    +        String path = U.resolveIgnitePath("modules/core/src/test/config/class_list_exploit_included.txt").getPath();
    +
    +        System.setProperty(IGNITE_MARSHALLER_WHITELIST, path);
    +        System.setProperty(IGNITE_MARSHALLER_BLACKLIST, path);
    +
    +        testExploit(false);
    +    }
    +
    +    /**
    +     * @param positive Positive.
    +     */
    +    private void testExploit(boolean positive) throws Exception {
    +        try {
    +            startGrid();
    +
    +            attack(marshal(new Exploit()).array());
    +
    +            boolean res = GridTestUtils.waitForCondition(new GridAbsPredicate() {
    +                @Override public boolean apply() {
    +                    return SHARED.get();
    +                }
    +            }, 3000L);
    +
    +            if (positive)
    +                assertTrue(res);
    +            else
    +                assertFalse(res);
    +        }
    +        finally {
    +            stopAllGrids();
    +        }
    +    }
    +
    +    /**
    +     * @param obj Object.
    +     */
    +    private static ByteBuffer marshal(Object obj) throws IOException {
    +        return MARSH.marshal(obj, 0);
    +    }
    +
    +    /**
    +     * @param data Data.
    +     */
    +    private void attack(byte[] data) throws IOException {
    +        InetAddress addr = InetAddress.getByName(host);
    +
    +        try (
    +            Socket sock = new Socket(addr, port);
    +            OutputStream os = new BufferedOutputStream(sock.getOutputStream())
    +        ) {
    +            // Handshake request.
    +            os.write(IGNITE_HANDSHAKE_FLAG);
    +
    +            GridClientHandshakeRequest req = new GridClientHandshakeRequest();
    +            req.marshallerId(GridClientJdkMarshaller.ID);
    +            os.write(req.rawBytes());
    +            os.flush();
    +
    +            // Handshake response
    +            InputStream is = new BufferedInputStream(sock.getInputStream());
    +
    +            is.read(new byte[146]); // Read handshake response.
    +
    +            int len = data.length + 40;
    +
    +            os.write(IGNITE_REQ_FLAG); // Package type.
    +            os.write((byte)(len >> 24)); // Package length.
    +            os.write((byte)(len >> 16));
    +            os.write((byte)(len >> 8));
    +            os.write((byte)(len));
    +            os.write(new byte[40]); // Stream header.
    +            os.write(data); // Exploit.
    +            os.flush();
    +        }
    +    }
    +
    +    /** */
    +    private static class Exploit implements GridClientMessage {
    +        /**
    +         * @param is Input stream.
    +         */
    +        private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
    +            SHARED.set(true);
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public long requestId() {
    +            return 0;
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public void requestId(long reqId) {
    +            // No-op.
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public UUID clientId() {
    +            return null;
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public void clientId(UUID id) {
    +            // No-op.
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public UUID destinationId() {
    +            return null;
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public void destinationId(UUID id) {
    +            // No-op.
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public byte[] sessionToken() {
    +            return new byte[0];
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override public void sessionToken(byte[] sesTok) {
    +            // No-op.
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • modules/clients/src/test/java/org/apache/ignite/loadtests/client/ClientMarshallerBenchmarkTest.java+12 4 modified
    @@ -22,11 +22,14 @@
     import java.util.HashMap;
     import java.util.Map;
     import java.util.UUID;
    +import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteException;
     import org.apache.ignite.internal.client.marshaller.GridClientMarshaller;
     import org.apache.ignite.internal.client.marshaller.jdk.GridClientJdkMarshaller;
     import org.apache.ignite.internal.client.marshaller.optimized.GridClientOptimizedMarshaller;
     import org.apache.ignite.internal.processors.rest.client.message.GridClientCacheRequest;
     import org.apache.ignite.internal.util.typedef.X;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
     
     import static org.apache.ignite.internal.processors.rest.client.message.GridClientCacheRequest.GridCacheOperation.CAS;
    @@ -41,10 +44,15 @@ public class ClientMarshallerBenchmarkTest extends GridCommonAbstractTest {
         /**
          */
         public ClientMarshallerBenchmarkTest() {
    -        marshallers = new GridClientMarshaller[] {
    -            new GridClientJdkMarshaller(),
    -            new GridClientOptimizedMarshaller()
    -        };
    +        try {
    +            marshallers = new GridClientMarshaller[] {
    +                new GridClientJdkMarshaller(MarshallerUtils.classNameFilter(this.getClass().getClassLoader())),
    +                new GridClientOptimizedMarshaller()
    +            };
    +        }
    +        catch (IgniteCheckedException e) {
    +            throw new IgniteException(e);
    +        }
         }
     
         /**
    
  • modules/core/src/main/java/org/apache/ignite/internal/client/marshaller/jdk/GridClientJdkMarshaller.java+50 1 modified
    @@ -19,13 +19,16 @@
     
     import java.io.ByteArrayInputStream;
     import java.io.IOException;
    +import java.io.InputStream;
     import java.io.ObjectInput;
     import java.io.ObjectInputStream;
     import java.io.ObjectOutput;
     import java.io.ObjectOutputStream;
    +import java.io.ObjectStreamClass;
     import java.nio.ByteBuffer;
     import org.apache.ignite.internal.client.marshaller.GridClientMarshaller;
     import org.apache.ignite.internal.util.io.GridByteArrayOutputStream;
    +import org.apache.ignite.lang.IgnitePredicate;
     
     /**
      * Simple marshaller that utilize JDK serialization features.
    @@ -34,6 +37,23 @@ public class GridClientJdkMarshaller implements GridClientMarshaller {
         /** ID. */
         public static final byte ID = 2;
     
    +    /** Class name filter. */
    +    private final IgnitePredicate<String> clsFilter;
    +
    +    /**
    +     * Default constructor.
    +     */
    +    public GridClientJdkMarshaller() {
    +        this(null);
    +    }
    +
    +    /**
    +     * @param clsFilter Class filter.
    +     */
    +    public GridClientJdkMarshaller(IgnitePredicate<String> clsFilter) {
    +        this.clsFilter = clsFilter;
    +    }
    +
         /** {@inheritDoc} */
         @Override public ByteBuffer marshal(Object obj, int off) throws IOException {
             GridByteArrayOutputStream bOut = new GridByteArrayOutputStream();
    @@ -60,7 +80,7 @@ public class GridClientJdkMarshaller implements GridClientMarshaller {
         @Override public <T> T unmarshal(byte[] bytes) throws IOException {
             ByteArrayInputStream tmp = new ByteArrayInputStream(bytes);
     
    -        ObjectInput in = new ObjectInputStream(tmp);
    +        ObjectInput in = new ClientJdkInputStream(tmp, clsFilter);
     
             try {
                 return (T)in.readObject();
    @@ -69,4 +89,33 @@ public class GridClientJdkMarshaller implements GridClientMarshaller {
                 throw new IOException("Failed to unmarshal target object: " + e.getMessage(), e);
             }
         }
    +
    +    /**
    +     * Wrapper with class resolving control.
    +     */
    +    private static class ClientJdkInputStream extends ObjectInputStream {
    +        /** Class name filter. */
    +        private final IgnitePredicate<String> clsFilter;
    +
    +
    +        /**
    +         * @param in Input stream.
    +         * @param clsFilter Class filter.
    +         */
    +        public ClientJdkInputStream(InputStream in, IgnitePredicate<String> clsFilter) throws IOException {
    +            super(in);
    +
    +            this.clsFilter = clsFilter;
    +        }
    +
    +        /** {@inheritDoc} */
    +        @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
    +            String clsName = desc.getName();
    +
    +            if (clsFilter != null && !clsFilter.apply(clsName))
    +                throw new RuntimeException("Deserialization of class " + clsName + " is disallowed.");
    +
    +            return super.resolveClass(desc);
    +        }
    +    }
     }
    \ No newline at end of file
    
  • modules/core/src/main/java/org/apache/ignite/internal/client/router/impl/GridTcpRouterNioListenerAdapter.java+13 1 modified
    @@ -23,6 +23,8 @@
     import java.util.List;
     import java.util.Map;
     import java.util.UUID;
    +import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteException;
     import org.apache.ignite.IgniteLogger;
     import org.apache.ignite.failure.FailureType;
     import org.apache.ignite.internal.client.GridClientException;
    @@ -42,6 +44,8 @@
     import org.apache.ignite.internal.util.nio.GridNioServerListener;
     import org.apache.ignite.internal.util.nio.GridNioSession;
     import org.apache.ignite.internal.util.typedef.internal.U;
    +import org.apache.ignite.lang.IgnitePredicate;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     import org.apache.ignite.plugin.PluginProvider;
     import org.jetbrains.annotations.Nullable;
     
    @@ -87,7 +91,15 @@ public GridTcpRouterNioListenerAdapter(IgniteLogger log, GridRouterClientImpl cl
     
             marshMap.put(GridClientOptimizedMarshaller.ID, optdMarsh);
             marshMap.put(GridClientZipOptimizedMarshaller.ID, new GridClientZipOptimizedMarshaller(optdMarsh, providers));
    -        marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller());
    +
    +        try {
    +            IgnitePredicate<String> clsFilter = MarshallerUtils.classNameFilter(this.getClass().getClassLoader());
    +
    +            marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller(clsFilter));
    +        }
    +        catch (IgniteCheckedException e) {
    +            throw new IgniteException(e);
    +        }
     
             init();
         }
    
  • modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java+2 106 modified
    @@ -17,14 +17,10 @@
     
     package org.apache.ignite.internal;
     
    -import java.io.BufferedReader;
     import java.io.Externalizable;
     import java.io.File;
    -import java.io.FileInputStream;
    -import java.io.FileNotFoundException;
     import java.io.IOException;
     import java.io.InputStream;
    -import java.io.InputStreamReader;
     import java.io.InvalidObjectException;
     import java.io.ObjectInput;
     import java.io.ObjectOutput;
    @@ -196,6 +192,7 @@
     import org.apache.ignite.lifecycle.LifecycleBean;
     import org.apache.ignite.lifecycle.LifecycleEventType;
     import org.apache.ignite.marshaller.MarshallerExclusions;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     import org.apache.ignite.marshaller.jdk.JdkMarshaller;
     import org.apache.ignite.mxbean.ClusterMetricsMXBean;
     import org.apache.ignite.mxbean.IgniteMXBean;
    @@ -273,8 +270,6 @@
     import static org.apache.ignite.internal.IgniteVersionUtils.VER_STR;
     import static org.apache.ignite.lifecycle.LifecycleEventType.AFTER_NODE_START;
     import static org.apache.ignite.lifecycle.LifecycleEventType.BEFORE_NODE_START;
    -import static org.apache.ignite.marshaller.MarshallerUtils.CLS_NAMES_FILE;
    -import static org.apache.ignite.marshaller.MarshallerUtils.JDK_CLS_NAMES_FILE;
     
     /**
      * Ignite kernal.
    @@ -888,7 +883,7 @@ public void start(
                     schemaExecSvc,
                     customExecSvcs,
                     plugins,
    -                classNameFilter()
    +                MarshallerUtils.classNameFilter(this.getClass().getClassLoader())
                 );
     
                 cfg.getMarshaller().setContext(ctx.marshallerContext());
    @@ -1748,105 +1743,6 @@ private void startProcessor(GridProcessor proc) throws IgniteCheckedException {
             }
         }
     
    -    /**
    -     * Returns class name filter for marshaller.
    -     *
    -     * @return Class name filter for marshaller.
    -     */
    -    private IgnitePredicate<String> classNameFilter() throws IgniteCheckedException {
    -        ClassSet whiteList = classWhiteList();
    -        ClassSet blackList = classBlackList();
    -
    -        return new IgnitePredicate<String>() {
    -            @Override public boolean apply(String s) {
    -                // Allows all primitive arrays and checks arrays' type.
    -                if ((blackList != null || whiteList != null) && s.charAt(0) == '[') {
    -                    if (s.charAt(1) == 'L' && s.length() > 2)
    -                        s = s.substring(2, s.length() - 1);
    -                    else
    -                        return true;
    -                }
    -
    -                return (blackList == null || !blackList.contains(s)) && (whiteList == null || whiteList.contains(s));
    -            }
    -        };
    -    }
    -
    -    /**
    -     * @return White list of classes.
    -     */
    -    private ClassSet classWhiteList() throws IgniteCheckedException {
    -        ClassSet clsSet = null;
    -
    -        String fileName = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_MARSHALLER_WHITELIST);
    -
    -        if (fileName != null) {
    -            clsSet = new ClassSet();
    -
    -            addClassNames(JDK_CLS_NAMES_FILE, clsSet);
    -            addClassNames(CLS_NAMES_FILE, clsSet);
    -            addClassNames(fileName, clsSet);
    -        }
    -
    -        return clsSet;
    -    }
    -
    -    /**
    -     * @return Black list of classes.
    -     */
    -    private ClassSet classBlackList() throws IgniteCheckedException {
    -        ClassSet clsSet = null;
    -
    -        String blackListFileName = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_MARSHALLER_BLACKLIST);
    -
    -        if (blackListFileName != null)
    -            addClassNames(blackListFileName, clsSet = new ClassSet());
    -
    -        return clsSet;
    -    }
    -
    -    /**
    -     * Reads class names from resource referred by given system property name and returns set of classes.
    -     *
    -     * @param fileName File name containing list of classes.
    -     * @param clsSet Class set for update.
    -     * @return Set of classes.
    -     */
    -    private void addClassNames(String fileName, ClassSet clsSet) throws IgniteCheckedException {
    -        InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName);
    -
    -        if (is == null) {
    -            try {
    -                is = new FileInputStream(new File(fileName));
    -            }
    -            catch (FileNotFoundException e) {
    -                throw new IgniteCheckedException("File " + fileName + " not found.");
    -            }
    -        }
    -
    -        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
    -            String line;
    -
    -            for (int i = 1; (line = reader.readLine()) != null; i++) {
    -                String s = line.trim();
    -
    -                if (!s.isEmpty() && s.charAt(0) != '#' && s.charAt(0) != '[') {
    -                    try {
    -                        clsSet.add(s);
    -                    }
    -                    catch (IllegalArgumentException e) {
    -                        throw new IgniteCheckedException("Exception occurred while reading list of classes" +
    -                            "[path=" + fileName + ", row=" + i + ", line=" + s + ']', e);
    -                    }
    -                }
    -            }
    -        }
    -        catch (IOException e) {
    -            throw new IgniteCheckedException("Exception occurred while reading and creating list of classes " +
    -                "[path=" + fileName + ']', e);
    -        }
    -    }
    -
         /**
          * Add helper.
          *
    
  • modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestProtocol.java+12 1 modified
    @@ -27,6 +27,7 @@
     import javax.net.ssl.SSLContext;
     import javax.net.ssl.SSLException;
     import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteException;
     import org.apache.ignite.configuration.ConnectorConfiguration;
     import org.apache.ignite.configuration.IgniteConfiguration;
     import org.apache.ignite.internal.GridKernalContext;
    @@ -46,6 +47,8 @@
     import org.apache.ignite.internal.util.nio.GridNioServerListener;
     import org.apache.ignite.internal.util.nio.ssl.GridNioSslFilter;
     import org.apache.ignite.internal.util.typedef.internal.U;
    +import org.apache.ignite.lang.IgnitePredicate;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     import org.apache.ignite.plugin.PluginProvider;
     import org.apache.ignite.spi.IgnitePortProtocol;
     import org.jetbrains.annotations.Nullable;
    @@ -149,7 +152,15 @@ else if (depFactory != null)
     
             marshMap.put(GridClientOptimizedMarshaller.ID, optMarsh);
             marshMap.put(GridClientZipOptimizedMarshaller.ID, new GridClientZipOptimizedMarshaller(optMarsh, providers));
    -        marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller());
    +
    +        try {
    +            IgnitePredicate<String> clsFilter = MarshallerUtils.classNameFilter(this.getClass().getClassLoader());
    +
    +            marshMap.put(GridClientJdkMarshaller.ID, new GridClientJdkMarshaller(clsFilter));
    +        }
    +        catch (IgniteCheckedException e) {
    +            throw new IgniteException(e);
    +        }
     
             lsnr.marshallers(marshMap);
         }
    
  • modules/core/src/main/java/org/apache/ignite/marshaller/MarshallerUtils.java+117 0 modified
    @@ -17,6 +17,17 @@
     
     package org.apache.ignite.marshaller;
     
    +import java.io.BufferedReader;
    +import java.io.File;
    +import java.io.FileInputStream;
    +import java.io.FileNotFoundException;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.InputStreamReader;
    +import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteSystemProperties;
    +import org.apache.ignite.internal.ClassSet;
    +import org.apache.ignite.lang.IgnitePredicate;
     import org.apache.ignite.lang.IgniteProductVersion;
     import org.apache.ignite.marshaller.jdk.JdkMarshaller;
     import org.jetbrains.annotations.Nullable;
    @@ -83,4 +94,110 @@ public static void jobSenderVersion(IgniteProductVersion ver) {
         public static IgniteProductVersion jobSenderVersion() {
             return JOB_SND_NODE_VER.get();
         }
    +
    +    /**
    +     * Returns class name filter for marshaller.
    +     *
    +     * @return Class name filter for marshaller.
    +     */
    +    public static IgnitePredicate<String> classNameFilter(ClassLoader clsLdr) throws IgniteCheckedException {
    +        ClassSet whiteList = classWhiteList(clsLdr);
    +        ClassSet blackList = classBlackList(clsLdr);
    +
    +        return new IgnitePredicate<String>() {
    +            @Override public boolean apply(String s) {
    +                // Allows all primitive arrays and checks arrays' type.
    +                if ((blackList != null || whiteList != null) && s.charAt(0) == '[') {
    +                    if (s.charAt(1) == 'L' && s.length() > 2)
    +                        s = s.substring(2, s.length() - 1);
    +                    else
    +                        return true;
    +                }
    +
    +                return (blackList == null || !blackList.contains(s)) && (whiteList == null || whiteList.contains(s));
    +            }
    +        };
    +    }
    +
    +    /**
    +     * @param clsLdr Class loader.
    +     * @return White list of classes.
    +     */
    +    private static ClassSet classWhiteList(ClassLoader clsLdr) throws IgniteCheckedException {
    +        ClassSet clsSet = null;
    +
    +        String fileName = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_MARSHALLER_WHITELIST);
    +
    +        if (fileName != null) {
    +            clsSet = new ClassSet();
    +
    +            addClassNames(JDK_CLS_NAMES_FILE, clsSet, clsLdr);
    +            addClassNames(CLS_NAMES_FILE, clsSet, clsLdr);
    +            addClassNames(fileName, clsSet, clsLdr);
    +        }
    +
    +        return clsSet;
    +    }
    +
    +    /**
    +     * @param clsLdr Class loader.
    +     * @return Black list of classes.
    +     */
    +    private static ClassSet classBlackList(ClassLoader clsLdr) throws IgniteCheckedException {
    +        ClassSet clsSet = null;
    +
    +        String blackListFileName = IgniteSystemProperties.getString(IgniteSystemProperties.IGNITE_MARSHALLER_BLACKLIST);
    +
    +        if (blackListFileName != null)
    +            addClassNames(blackListFileName, clsSet = new ClassSet(), clsLdr);
    +
    +        return clsSet;
    +    }
    +
    +    /**
    +     * Reads class names from resource referred by given system property name and returns set of classes.
    +     *
    +     * @param fileName File name containing list of classes.
    +     * @param clsSet Class set for update.
    +     * @param clsLdr Class loader.
    +     */
    +    private static void addClassNames(
    +        String fileName,
    +        ClassSet clsSet,
    +        ClassLoader clsLdr
    +    ) throws IgniteCheckedException {
    +        InputStream is = clsLdr.getResourceAsStream(fileName);
    +
    +        if (is == null) {
    +            try {
    +                is = new FileInputStream(new File(fileName));
    +            }
    +            catch (FileNotFoundException e) {
    +                throw new IgniteCheckedException("File " + fileName + " not found.");
    +            }
    +        }
    +
    +        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
    +            String line;
    +
    +            for (int i = 1; (line = reader.readLine()) != null; i++) {
    +                String s = line.trim();
    +
    +                if (!s.isEmpty() && s.charAt(0) != '#' && s.charAt(0) != '[') {
    +                    try {
    +                        clsSet.add(s);
    +                    }
    +                    catch (IllegalArgumentException e) {
    +                        throw new IgniteCheckedException("Exception occurred while reading list of classes" +
    +                            "[path=" + fileName + ", row=" + i + ", line=" + s + ']', e);
    +                    }
    +                }
    +            }
    +        }
    +        catch (IOException e) {
    +            throw new IgniteCheckedException("Exception occurred while reading and creating list of classes " +
    +                "[path=" + fileName + ']', e);
    +        }
    +    }
    +
     }
    
  • modules/core/src/test/config/class_list_exploit_included.txt+2 1 modified
    @@ -16,4 +16,5 @@
     #
     
     org.apache.ignite.spi.discovery.tcp.DiscoveryUnmarshalVulnerabilityTest$Exploit
    -org.apache.ignite.stream.socket.SocketStreamerUnmarshalVulnerabilityTest$Exploit
    \ No newline at end of file
    +org.apache.ignite.stream.socket.SocketStreamerUnmarshalVulnerabilityTest$Exploit
    +org.apache.ignite.internal.processors.rest.TcpRestUnmarshalVulnerabilityTest$Exploit
    \ No newline at end of file
    
  • modules/hadoop/src/main/java/org/apache/ignite/internal/processors/hadoop/mapreduce/MapReduceClient.java+15 1 modified
    @@ -17,6 +17,8 @@
     
     package org.apache.ignite.internal.processors.hadoop.mapreduce;
     
    +import org.apache.ignite.IgniteCheckedException;
    +import org.apache.ignite.IgniteException;
     import org.apache.ignite.internal.client.GridClient;
     import org.apache.ignite.internal.client.GridClientConfiguration;
     import org.apache.ignite.internal.client.GridClientException;
    @@ -26,6 +28,8 @@
     import java.io.IOException;
     import java.util.Collection;
     import java.util.concurrent.atomic.AtomicInteger;
    +import org.apache.ignite.lang.IgnitePredicate;
    +import org.apache.ignite.marshaller.MarshallerUtils;
     
     import static org.apache.ignite.internal.client.GridClientProtocol.TCP;
     
    @@ -83,7 +87,17 @@ public GridClient client() throws IOException {
     
                         cliCfg.setProtocol(TCP);
                         cliCfg.setServers(addrs);
    -                    cliCfg.setMarshaller(new GridClientJdkMarshaller());
    +
    +                    try {
    +                        IgnitePredicate<String> clsFilter =
    +                            MarshallerUtils.classNameFilter(this.getClass().getClassLoader());
    +
    +                        cliCfg.setMarshaller(new GridClientJdkMarshaller(clsFilter));
    +                    }
    +                    catch (IgniteCheckedException e) {
    +                        throw new IgniteException(e);
    +                    }
    +
                         cliCfg.setMaxConnectionIdleTime(24 * 60 * 60 * 1000L); // 1 day.
                         cliCfg.setDaemon(true);
     
    

Vulnerability mechanics

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

References

8

News mentions

0

No linked articles in our index yet.