VYPR
High severityNVD Advisory· Published May 28, 2025· Updated Apr 29, 2026

Apache Commons BeanUtils: PropertyUtilsBean does not suppresses an enum's declaredClass property by default

CVE-2025-48734

Description

Improper Access Control vulnerability in Apache Commons.

A special BeanIntrospector class was added in version 1.9.2. This can be used to stop attackers from using the declared class property of Java enum objects to get access to the classloader. However this protection was not enabled by default. PropertyUtilsBean (and consequently BeanUtilsBean) now disallows declared class level property access by default.

Releases 1.11.0 and 2.0.0-M2 address a potential security issue when accessing enum properties in an uncontrolled way. If an application using Commons BeanUtils passes property paths from an external source directly to the getProperty() method of PropertyUtilsBean, an attacker can access the enum’s class loader via the “declaredClass” property available on all Java “enum” objects. Accessing the enum’s “declaredClass” allows remote attackers to access the ClassLoader and execute arbitrary code. The same issue exists with PropertyUtilsBean.getNestedProperty(). Starting in versions 1.11.0 and 2.0.0-M2 a special BeanIntrospector suppresses the “declaredClass” property. Note that this new BeanIntrospector is enabled by default, but you can disable it to regain the old behavior; see section 2.5 of the user's guide and the unit tests.

This issue affects Apache Commons BeanUtils 1.x before 1.11.0, and 2.x before 2.0.0-M2.Users of the artifact commons-beanutils:commons-beanutils

1.x are recommended to upgrade to version 1.11.0, which fixes the issue.

Users of the artifact org.apache.commons:commons-beanutils2

2.x are recommended to upgrade to version 2.0.0-M2, which fixes the issue.

AI Insight

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

Apache Commons BeanUtils is vulnerable to improper access control allowing attackers to access the enum class loader via the declaredClass property to execute arbitrary code.

Vulnerability

Analysis

CVE-2025-48734 is an improper access control vulnerability in Apache Commons BeanUtils that affects versions 1.x before 1.11.0 and 2.x before 2.0.0-M2. The root cause is that a protective BeanIntrospector class, introduced in version 1.9.2 to suppress the declaredClass property of Java enum objects, was not enabled by default [1]. This allows untrusted property paths to reach the internal declaredClass property of any enum object, which in turn provides access to the enum's ClassLoader [1][2].

Exploitation

To exploit this vulnerability, an attacker must be able to supply property paths to the getProperty() or getNestedProperty() methods of PropertyUtilsBean. If the application passes external input directly to these methods without sanitization, the attacker can craft a path that accesses an enum's declaredClass property [1][3]. This attack requires no authentication beyond the ability to interact with the vulnerable application, making it particularly dangerous when these utilities are used in web-facing components that process user-controlled property names.

Impact

Successful exploitation allows a remote attacker to obtain the enum object's ClassLoader. Using the ClassLoader, the attacker can then load arbitrary classes and execute arbitrary code in the context of the JVM [1]. This effectively results in remote code execution, compromising the confidentiality, integrity, and availability of the affected system.

Mitigation

Users of commons-beanutils:commons-beanutils 1.x should upgrade to version 1.11.0, and users of org.apache.commons:commons-beanutils2 2.x should upgrade to version 2.0.0-M2 [1][4]. Starting with these fixed versions, a new SuppressPropertiesBeanIntrospector is enabled by default, which suppresses the declaredClass property [2]. Administrators can optionally disable this introspector if they require the old behavior, but doing so will reintroduce the vulnerability [1][3].

AI Insight generated on May 20, 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
commons-beanutils:commons-beanutilsMaven
>= 1.0, < 1.11.01.11.0
org.apache.commons:commons-beanutils2Maven
>= 2.0.0-M1, < 2.0.0-M22.0.0-M2

Affected products

992

Patches

1
bd20740da25b

Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS

https://github.com/apache/commons-beanutilsGary GregoryMay 25, 2025via ghsa
6 files changed · +178 2
  • src/changes/changes.xml+16 0 modified
    @@ -54,6 +54,7 @@
           <action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.set(String, Object) now throws IllegalArgumentException instead of RuntimeException to wrap cases of SQLException.</action>
           <action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.set(String, String, Object) now throws IllegalArgumentException instead of RuntimeException to wrap cases of SQLException.</action>
           <!-- ADD -->
    +      <action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS.</action>
           <!-- UPDATE -->
           <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 78 to 84 #348.</action>
           <action type="update" dev="ggregory" due-to="Gary Gregory">Bump commons-logging:commons-logging from 1.3.4 to 1.3.5.</action>
    @@ -258,6 +259,21 @@
             Do not implement Serializable.
           </action>
         </release>
    +    <release version="1.11.0" date="YYYY-MM-DD" description="This is a maintenance release and requires Java 8.">
    +      <!-- FIX -->
    +      <action type="fix" dev="ggregory" due-to="Gary Gregory">BeanComparator.compare(T, T) now throws IllegalArgumentException instead of RuntimeException to wrap all cases of ReflectiveOperationException.</action>
    +      <action type="fix" dev="ggregory" due-to="Gary Gregory">MappedMethodReference.get() now throws IllegalStateException instead of RuntimeException to wrap cases of NoSuchMethodException.</action>
    +      <action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.get(String) now throws IllegalArgumentException instead of RuntimeException to wrap cases of SQLException.</action>
    +      <action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.hasNext() now throws IllegalStateException instead of RuntimeException to wrap cases of SQLException.</action>
    +      <action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.next() now throws IllegalStateException instead of RuntimeException to wrap cases of SQLException.</action>
    +      <action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.set(String, Object) now throws IllegalArgumentException instead of RuntimeException to wrap cases of SQLException.</action>
    +      <action type="fix" dev="ggregory" due-to="Gary Gregory">ResultSetIterator.set(String, String, Object) now throws IllegalArgumentException instead of RuntimeException to wrap cases of SQLException.</action>
    +      <!-- ADD -->
    +      <action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS.</action>
    +      <!-- UPDATE -->
    +      <action dev="ggregory" type="update" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 81 to 84.</action>
    +      <action dev="ggregory" type="update" due-to="Gary Gregory">Bump commons-logging:commons-logging from 1.3.4 to 1.3.5.</action>
    +    </release>
         <release version="1.10.1" date="2025-01-31" description="This is a maintenance release and requires Java 8.">
           <!-- FIX -->
           <action type="fix" issue="BEANUTILS-541" dev="ggregory" due-to="Sergey Chernov">FluentPropertyBeanIntrospector concurrency issue (backport to 1.X) #325.</action>
    
  • src/main/java/org/apache/commons/beanutils2/package-info.java+7 1 modified
    @@ -424,7 +424,7 @@
      *
      * <p>A good use case for suppressing properties is the special {@code class}
      * property which is per default available for all beans; it is generated from the
    - * {@code getClass()</code> method inherited from <code>Object} which follows the
    + * {@code getClass()} method inherited from {@code Object} which follows the
      * naming conventions for property get methods. Exposing this property in an
      * uncontrolled way can lead to a security vulnerability as it allows access to
      * the class loader. More information can be found at
    @@ -437,6 +437,12 @@
      * {@code SUPPRESS_CLASS} constant of
      * {@code SuppressPropertiesBeanIntrospector}.</p>
      *
    + * <p>Another problematic property is the {@code enum} "declaredClass" property,
    + * through which you can also access that class' class loader. The {@code SuppressPropertiesBeanIntrospector}
    + * provides {@code SUPPRESS_DECLARING_CLASS} to workaround this issue.</p>
    + *
    + * <p>Both {@code SUPPRESS_CLASS} and {@code SUPPRESS_DECLARING_CLASS} are enabled by default.</p>
    + *
      * <a id="dynamic"></a>
      * <h2>3. Dynamic Beans (DynaBeans)</h2>
      *
    
  • src/main/java/org/apache/commons/beanutils2/PropertyUtilsBean.java+1 0 modified
    @@ -1201,6 +1201,7 @@ public final void resetBeanIntrospectors() {
             introspectors.clear();
             introspectors.add(DefaultBeanIntrospector.INSTANCE);
             introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
    +        introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
         }
     
         /**
    
  • src/main/java/org/apache/commons/beanutils2/SuppressPropertiesBeanIntrospector.java+13 1 modified
    @@ -35,14 +35,26 @@
      * @since 1.9.2
      */
     public class SuppressPropertiesBeanIntrospector implements BeanIntrospector {
    +
         /**
          * A specialized instance which is configured to suppress the special {@code class} properties of Java beans. Unintended access to the property
          * {@code class} (which is common to all Java objects) can be a security risk because it also allows access to the class loader. Adding this instance as
          * {@code BeanIntrospector} to an instance of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no longer be accessed.
          */
         public static final SuppressPropertiesBeanIntrospector SUPPRESS_CLASS = new SuppressPropertiesBeanIntrospector(Collections.singleton("class"));
     
    -    /** A set with the names of the properties to be suppressed. */
    +    /**
    +     * A specialized instance which is configured to suppress the special {@code class} properties of Java beans. Unintended access to the call for
    +     * {@code declaringClass} (which is common to all Java {@code enum}) can be a security risk because it also allows access to the class loader. Adding this
    +     * instance as {@code BeanIntrospector} to an instance of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no longer be
    +     * accessed.
    +     *
    +     * @since 2.0.0-M2
    +     */
    +    public static final SuppressPropertiesBeanIntrospector SUPPRESS_DECLARING_CLASS = new SuppressPropertiesBeanIntrospector(
    +            Collections.singleton("declaringClass"));
    +
    +/** A set with the names of the properties to be suppressed. */
         private final Set<String> propertyNames;
     
         /**
    
  • src/main/java/org/apache/commons/beanutils2/TestEnum.java+33 0 added
    @@ -0,0 +1,33 @@
    +/*
    + * 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
    + *
    + *      https://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.commons.beanutils2;
    +
    +/**
    + * An {@code enum} test fixture.
    + */
    +public enum TestEnum {
    +
    +    /** Test fixture. */
    +    A,
    +
    +    /** Test fixture. */
    +    B,
    +
    +    /** Test fixture. */
    +    C
    +}
    
  • src/test/java/org/apache/commons/beanutils2/bugs/EnumDeclaringClassTest.java+108 0 added
    @@ -0,0 +1,108 @@
    +/*
    + * 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
    + *
    + *      https://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.commons.beanutils2.bugs;
    +
    +import static org.junit.jupiter.api.Assertions.assertEquals;
    +import static org.junit.jupiter.api.Assertions.assertInstanceOf;
    +import static org.junit.jupiter.api.Assertions.assertNotNull;
    +import static org.junit.jupiter.api.Assertions.assertThrows;
    +
    +import org.apache.commons.beanutils2.BeanUtilsBean;
    +import org.apache.commons.beanutils2.PropertyUtilsBean;
    +import org.apache.commons.beanutils2.SuppressPropertiesBeanIntrospector;
    +import org.apache.commons.beanutils2.TestEnum;
    +import org.junit.jupiter.api.Test;
    +
    +public class EnumDeclaringClassTest {
    +
    +    public static class Fixture {
    +
    +        String name = "default";
    +        TestEnum testEnum = TestEnum.A;
    +
    +        public String getName() {
    +            return name;
    +        }
    +
    +        public TestEnum getTestEnum() {
    +            return testEnum;
    +        }
    +
    +        public void setName(final String name) {
    +            this.name = name;
    +        }
    +
    +        public void setTestEnum(final TestEnum day) {
    +            this.testEnum = day;
    +        }
    +    }
    +
    +    /**
    +     * Allow opt-out to make your app less secure but allow access to "declaringClass".
    +     */
    +    @Test
    +    public void testAllowAccessToClassPropertyFromBeanUtilsBean() throws ReflectiveOperationException {
    +        final BeanUtilsBean bub = new BeanUtilsBean();
    +        final PropertyUtilsBean propertyUtilsBean = bub.getPropertyUtils();
    +        propertyUtilsBean.removeBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
    +        final Fixture fixture = new Fixture();
    +        final String string = bub.getProperty(fixture, "testEnum.declaringClass");
    +        assertEquals(TestEnum.class.getName(), string);
    +        final Class<TestEnum> teClass = assertInstanceOf(Class.class, propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass"));
    +        final ClassLoader classLoader = teClass.getClassLoader();
    +        assertNotNull(classLoader);
    +        assertNotNull(bub.getProperty(fixture, "testEnum.declaringClass.classLoader"));
    +        assertInstanceOf(ClassLoader.class, propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"));
    +    }
    +
    +    /**
    +     * Allow opt-out to make your app less secure but allow access to "declaringClass".
    +     */
    +    @Test
    +    public void testAllowAccessToClassPropertyFromPropertyUtilsBean() throws ReflectiveOperationException {
    +        final PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
    +        propertyUtilsBean.removeBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS);
    +        final Fixture fixture = new Fixture();
    +        final Object cls = propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass");
    +        final Class<TestEnum> teClass = assertInstanceOf(Class.class, cls);
    +        final ClassLoader classLoader = teClass.getClassLoader();
    +        assertNotNull(classLoader);
    +        assertInstanceOf(ClassLoader.class, propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"));
    +    }
    +
    +    /**
    +     * By default opt-in to security that does not allow access to "declaringClass".
    +     */
    +    @Test
    +    public void testSuppressClassPropertyByDefaultFromBeanUtilsBean() throws ReflectiveOperationException {
    +        final Fixture fixture = new Fixture();
    +        final BeanUtilsBean bub = new BeanUtilsBean();
    +        assertThrows(NoSuchMethodException.class, () -> bub.getProperty(fixture, "testEnum.declaringClass.classLoader"));
    +        assertThrows(NoSuchMethodException.class, () -> bub.getPropertyUtils().getNestedProperty(fixture, "testEnum.declaringClass.classLoader"));
    +    }
    +
    +    /**
    +     * By default opt-in to security that does not allow access to "declaringClass".
    +     */
    +    @Test
    +    public void testSuppressClassPropertyByDefaultFromPropertyUtilsBean() throws ReflectiveOperationException {
    +        final Fixture fixture = new Fixture();
    +        final PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
    +        assertThrows(NoSuchMethodException.class, () -> propertyUtilsBean.getNestedProperty(fixture, "testEnum.declaringClass.classLoader"));
    +    }
    +}
    

Vulnerability mechanics

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

References

6

News mentions

0

No linked articles in our index yet.