CVE-2025-2251 - wildfly-ejb3: Improper Deserialization in JBoss Marshalling Allows Remote Code Execution
This CVE-2025-2251 is rated Moderate and will be fixed in an upcoming JBoss EAP 8.0 Update & JBoss EAP 7.4 Update.
Poisoned data can be serialized and sent to a remote EJB resulting in potential remote code execution (RCE). The server, via JBoss Marshalling, will deserialize the data and execute a JNDI lookup on a naming server controlled by the attacker. Performing a JNDI lookup on an attacker-controlled endpoint raises the possibility, if the JVM running EAP is configured to allow it, of downloading and executing arbitrary code.
The primary mitigation against a JNDI lookup exploit like this is to not override the default JVM settings that prevent loading code from a URL specified by a javax.naming.Reference object. This means leaving the following system properties unset or setting them to false:
com.sun.jndi.ldap.object.trustURLCodebase
com.sun.jndi.rmi.object.trustURLCodebase
Limiting Object Deserialization
EAP and Java SE provide mechanisms to limit attacks that rely upon deserialization of objects, allowing prevention of the deserialization of poisoned data like that which triggers the CVE-2025-2251 JNDI lookup. These mechanisms are based on specifying the class names of types that must not be deserialized (a "block list") or specifying a list of types that may be deserialized and then blocking all other types (an "allow list").
JVM-Wide Limits (JEP 290)
On Java SE 9 and later, Java SE supports configuring process-wide deserialization limits by setting the system property jdk.serialFilter or by setting the security property jdk.serialFilter in $JAVA_HOME/conf/security/java.security. If you are starting your EAP process using our standard launch scripts (e.g. bin/standalone.sh), then you should control this value by setting the JDK_SERIAL_FILTER env var. The common.conf, common.conf.bat and common.conf.ps1 files in the EAP installation bin/ folder have a section that configures JDK_SERIAL_FILTER. Editing that section is the recommended way of configuring this.
See 'Process-Wide Filter' in the JEP 290 specification (Content from openjdk.org is not included.Content from openjdk.org is not included.https://openjdk.org/jeps/290) for details on the value of jdk.serialFilter.
NOTE: The jdk.serialFilter value applies to any Java SE or JBoss Marshalling deserialization performed in the JVM, not just to deserialization done for the remote EJB endpoint.
IMPORTANT: The JEP 290 mechanism is only available on Java SE 9 and later. JBoss EAP 7.4 installations running on Java SE 8 cannot benefit from this mechanism.
Remote EJB Endpoint Limits
EAP's ejb3 subsystem supports applying a class deserialization filter to the JBoss Marshalling unmarshaller used for unmarshalling remote EJB requests. This can be configured by setting the jboss.experimental.ejb.unmarshalling.filter.spec system property. The value of the property should use the same class and package name patterns used for JEP 290 filtering. See 'Process-Wide Filter' in the JEP 290 specification (Content from openjdk.org is not included.Content from openjdk.org is not included.https://openjdk.org/jeps/290) for details.
NOTE: The remote EJB filter does not support JEP 290 style 'limit' patterns, i.e. the maxdepth, maxrefs, maxbytes and maxarray settings supported by JEP 290.
IMPORTANT: Support for filter configuration via the jboss.experimental.ejb.unmarshalling.filter.spec system property may be removed in a future EAP major or minor release.
Allow Lists versus Block Lists
If either of the mechanisms above are used, if possible use an "allow list"; i.e. specify the allowed types that your application needs and deny all others. This of course requires an understanding of what types your application expects.
If you are running an EAP 7.4 or 8.0 release prior to the one where the CVE-2025-2251 exploit is blocked, the following is a suggested blocklist.
In common.conf you would have:
SERIALIZATION_CLASS_FILTER_SPEC="!org.apache.commons.collections.functors.InvokerTransformer;!org.apache.commons.collections.functors.InstantiateTransformer;!org.apache.commons.collections4.functors.InvokerTransformer;!org.apache.commons.collections4.functors.InstantiateTransformer;!org.codehaus.groovy.runtime.ConvertedClosure;!org.codehaus.groovy.runtime.MethodClosure;!org.springframework.beans.factory.ObjectFactory;!org.springframework.beans.factory.config.MethodInvokingFactoryBean;!org.springframework.jndi.support.SimpleJndiBeanFactory;!org.springframework.transaction.jta.JtaTransactionManager;!com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;!org.apache.xalan.xsltc.trax.TemplatesImpl;!bsh.XThis$Handler;!com.mchange.v2.c3p0.PoolBackedDataSource;!clojure.inspector.proxy$javax.swing.table.AbstractTableModel$ff19274a;!org.apache.commons.fileupload.disk.DiskFileItem;!org.apache.wicket.util.upload.DiskFileItem;!org.hibernate.engine.spi.TypedValue;!org.python.core.PyFunction;!org.mozilla.javascript.NativeJavaObject;!org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression;!javax.swing.UIDefaults;!javax.swing.UIDefaults$ProxyLazyValue;!javax.swing.UIDefaults$TextAndMnemonicHashMap;!sun.swing.SwingLazyValue;"!java.rmi.registry.Registry;!java.rmi.server.UnicastRemoteObject;!sun.rmi.server.UnicastRef;!sun.rmi.server.UnicastRef2;!org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap;!org.jboss.as.connector.subsystems.datasources.WildFlyDataSource"
and then you would use that value to configure the process-wide JEP 290 filtering, or the remote-EJB-specific filtering.
For JEP 290 filtering:
# Default JDK_SERIAL_FILTER settings
#
if [ "x$JDK_SERIAL_FILTER" = "x" ]; then
JDK_SERIAL_FILTER="maxbytes=10485760;maxdepth=128;maxarray=100000;maxrefs=300000;$SERIALIATION_CLASS_FILTER_SPEC"
fi
For remote-EJB-specific filtering:
if [ "x$JAVA_OPTS" = "x" ]; then
JAVA_OPTS="$JBOSS_JAVA_SIZING -Djava.net.preferIPv4Stack=true"
JAVA_OPTS="$JAVA_OPTS -Djboss.experimental.ejb.unmarshalling.filter.spec
=$SERIALIATION_CLASS_FILTER_SPEC"
For Windows users, analogous settings can be used in common.conf.bat or common.conf.ps1.