CVE-2025-48734 is an improper access control vulnerability in Apache Commons BeanUtils. The flaw concerns applications that let an external source control property expressions passed to PropertyUtilsBean.getProperty() או PropertyUtilsBean.getNestedProperty().
Under vulnerable conditions, a property path can move from an application object into a Java enum, from the enum into its declaringClass property, and from the resulting כיתה object toward its class loader. Apache warns that this access can ultimately enable arbitrary code execution.
That description deserves careful interpretation. CVE-2025-48734 is not an unconditional remote code execution vulnerability that activates merely because a vulnerable BeanUtils JAR exists on the classpath. An application must expose a suitable property-resolution path, attacker-controlled input must reach it, an enum must be reachable through the traversed object graph, and the surrounding runtime must provide enough additional functionality to turn reflective or class-loader access into a meaningful security impact.
Apache fixed the default behavior in Commons BeanUtils 1.11.0 and Commons BeanUtils 2.0.0-M2. The patched implementation adds a dedicated introspector that suppresses the declaringClass property by default. (NVD)
CVE-2025-48734 at a Glance
| שדה | פרטים |
|---|---|
| מזהה CVE | CVE-2025-48734 |
| רכיב | Apache Commons BeanUtils |
| סוג הפגיעות | Improper Access Control |
| CWE | CWE-284 |
| Primary affected APIs | PropertyUtilsBean.getProperty() ו getNestedProperty() |
| Dangerous property | declaringClass on Java enum objects |
| Affected 1.x artifact | commons-beanutils:commons-beanutils |
| Affected 1.x versions | 1.0 through versions before 1.11.0 |
| Fixed 1.x version | 1.11.0 |
| Affected 2.x artifact | org.apache.commons:commons-beanutils2 |
| Affected 2.x versions | 2.0.0-M1 through versions before 2.0.0-M2 |
| Fixed 2.x version | 2.0.0-M2 |
| Published | May 28, 2025 |
| Practical condition | Externally controlled property paths must reach a vulnerable BeanUtils property-resolution call |
The CVE record contains a CISA-ADP CVSS v3.1 vector of AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H, which produces a score of 8.8. GitHub therefore classifies the advisory as High severity. The NVD page, however, currently states that NVD itself has not supplied an independent CVSS score, so reports should distinguish the contributed CISA-ADP assessment from an NVD assessment. (NVD)
What Is Apache Commons BeanUtils?
Apache Commons BeanUtils is a Java library that wraps reflection and JavaBeans introspection APIs. It gives applications a convenient way to read, write, describe, copy, and convert object properties without manually invoking every getter or setter.
A typical application might use BeanUtils to access a simple property such as:
String username = (String) propertyUtils.getProperty(user, "username");
The library also supports nested property expressions:
Object country = propertyUtils.getNestedProperty(
order,
"customer.address.country"
);
Apache documents several supported expression formats:
| Property format | דוגמה | משמעות |
|---|---|---|
| Simple | שם משתמש | Read one JavaBean property |
| Nested | customer.address.city | Traverse several getter methods |
| Indexed | items[0] | Read an array or list element |
| Mapped | attributes(role) | Read a mapped value |
| Combined | orders[0].customer.attributes(tier) | Combine nested, indexed, and mapped access |
The nested format is central to CVE-2025-48734. BeanUtils resolves the first property, receives an object, introspects that object, resolves the next property, and continues until it reaches the end of the expression. This is useful for application development, but it effectively creates a small object-graph query language. When the expression itself is attacker-controlled, the caller is no longer selecting only the properties intended by the application developer. (Apache Commons)
Apache lists BeanUtils use cases that include scripting against Java object models, template processing, tag libraries, configuration consumption, and frameworks that manipulate collections or JavaBeans. These are precisely the kinds of environments where dynamic property names can cross trust boundaries if they are derived from request parameters, filtering expressions, sorting fields, reporting templates, workflow configuration, or other externally supplied values. (Apache Commons)
The Root Cause of CVE-2025-48734
Every Java enum inherits the following method from java.lang.Enum:
public final Class<E> getDeclaringClass()
The method returns the כיתה object corresponding to the enum constant’s enum type. Under JavaBeans naming conventions, a zero-argument getter named getDeclaringClass() can be exposed as a readable property named declaringClass.
Oracle’s Java API documentation confirms that Enum.getDeclaringClass() returns the כיתה object for the enum constant’s enum type. (Oracle Documentation)
Consider this simplified domain model:
public enum AccountStatus {
ACTIVE,
SUSPENDED
}
public class Account {
private AccountStatus status = AccountStatus.ACTIVE;
public AccountStatus getStatus() {
return status;
}
}
A normal application might intentionally allow:
status
In a vulnerable BeanUtils configuration, the expression resolver may also accept:
status.declaringClass
That expression first retrieves the AccountStatus enum constant. BeanUtils then introspects the enum object and resolves its declaringClass property by invoking getDeclaringClass().
The returned value is no longer ordinary application data. It is a java.lang.Class אובייקט.
From there, a longer property expression may continue:
status.declaringClass.classLoader
Apache’s own regression test demonstrates this exact traversal. When the new protection is deliberately removed, the test reaches both the enum’s declaring class and the associated ClassLoader. In the patched default configuration, the same property expression raises NoSuchMethodException. (GitHub)
The access-control failure therefore exists at the boundary between two ideas:
- BeanUtils treats getter-derived properties as navigable application data.
- Some getter-derived properties expose runtime metadata and privileged platform objects rather than business data.
The vulnerable versions did not suppress the enum-derived declaringClass property by default.
Is the Property Named declaredClass או declaringClass?
Some vulnerability descriptions and comments use the phrase “declared class property” or even the identifier declaredClass. The actual Java enum method is getDeclaringClass(), and the property expression used by BeanUtils is:
declaringClass
The Apache patch confirms this implementation detail. It introduces:
Collections.singleton("declaringClass")
inside the new SUPPRESS_DECLARING_CLASS introspector. Apache’s regression tests likewise use paths such as:
testEnum.declaringClass.classLoader
Security teams should therefore search for both terms in advisories and scanner output, but code review and runtime detection should focus primarily on the actual property token declaringClass. (GitHub)
The CVE-2025-48734 Attack Path
A realistic attack chain can be represented as follows:
External request or configuration
|
v
Attacker-controlled property expression
|
v
PropertyUtilsBean.getProperty()
or getNestedProperty()
|
v
Application bean property
|
v
Reachable Java enum constant
|
v
enum.getDeclaringClass()
|
v
java.lang.Class object
|
v
ClassLoader or other reflective metadata
|
v
Environment-dependent secondary behavior
|
v
Information disclosure, security-boundary bypass,
or potentially arbitrary code execution
The important term is environment-dependent. The Apache advisory states that accessing the enum’s declaring class can expose the class loader and allow remote attackers to execute arbitrary code. That establishes the maximum security impact recognized by the vendor. It does not establish that every use of a vulnerable BeanUtils version has a complete, reliable, one-request RCE chain. (NVD)
A complete exploitation scenario ordinarily requires the following conditions.
Attacker-Controlled Property Names
The attacker must influence the string passed to a BeanUtils property API. Examples might include a request parameter used as a dynamic field selector, a sorting or filtering expression, an administrative reporting template, or a remotely supplied configuration object.
The following pattern is dangerous:
@GetMapping("/inspect")
public Object inspect(
@RequestParam String property,
Account account) throws Exception {
return new PropertyUtilsBean().getProperty(account, property);
}
The request is not limited to known business fields. It effectively asks BeanUtils to interpret an arbitrary expression selected by the requester.
By contrast, the following design is materially safer:
private static final Map<String, Function<Account, Object>> ALLOWED_FIELDS =
Map.of(
"id", Account::getId,
"status", Account::getStatus,
"displayName", Account::getDisplayName
);
public Object inspect(Account account, String requestedField) {
Function<Account, Object> accessor = ALLOWED_FIELDS.get(requestedField);
if (accessor == null) {
throw new IllegalArgumentException("Unsupported field");
}
return accessor.apply(account);
}
Here, external input selects an application-defined identifier rather than a reflection expression.
A Reachable Enum in the Object Graph
The property path needs a reachable enum value. This condition is often easy to satisfy because enums are commonly used for:
- Account status
- User role
- Transaction state
- Order state
- Permission level
- Workflow phase
- Deployment environment
- Authentication state
- Document classification
An application that exposes an object graph containing one of these values may provide the necessary transition from business data to java.lang.Class.
Continued Traversal Beyond the Enum
Reading the enum value itself is not enough. The vulnerable expression must be allowed to continue through declaringClass and potentially into properties of the resulting כיתה אובייקט.
BeanUtils explicitly supports nested expressions where each returned object becomes the next introspection target. (Apache Commons)
A Useful Runtime or Framework Context
Class-loader exposure is dangerous because class loaders occupy a privileged position in Java’s runtime architecture. They are responsible for locating and loading classes and resources. They may also expose framework-specific behavior, resource lookup paths, module relationships, container internals, or other objects that were never intended to be selected through an untrusted property expression.
Nevertheless, the final impact depends on what methods and properties remain accessible, what classes are present, which framework wraps the result, how returned objects are serialized or rendered, and whether another expression engine or reflective mechanism participates in the request.
This is why vulnerability assessment must go beyond the dependency version.
Why ClassLoader Access Is Serious but Not Automatically RCE
Security advisories often compress a multistep security problem into a maximum-impact statement. In this case, “access the ClassLoader and execute arbitrary code” is the vendor-described outcome. Defenders should respect that impact while still determining whether their own application exposes the necessary chain.
Class-loader access can be security-sensitive for several reasons:
- It crosses from business objects into JVM runtime infrastructure.
- It may expose classpath or resource information.
- It can reveal implementation details that help construct another exploit.
- Framework-specific class loaders may support operations beyond ordinary data access.
- A second expression, templating, scripting, deserialization, or plugin mechanism may turn metadata access into active code loading.
- The affected code may execute with the full privileges of the Java service account.
However, simply receiving a reference to a ClassLoader does not, by itself, prove that an attacker can submit arbitrary bytecode and execute it. A mature assessment should therefore report the result in stages:
| Verified stage | Appropriate conclusion |
|---|---|
| Vulnerable dependency only | Potential exposure; reachability unknown |
| External input reaches BeanUtils property API | Attacker-controlled expression confirmed |
Enum declaringClass is reachable | Access-control bypass confirmed |
| Class loader is returned or traversable | Runtime metadata exposure confirmed |
| Sensitive resource or privileged behavior is reachable | High-impact exploit path confirmed |
| Controlled code execution is demonstrated in an authorized environment | RCE confirmed |
This evidence-based classification prevents two common errors: dismissing a dangerous dependency because no public exploit was copied into production, or declaring full RCE based only on an SCA alert.
גרסאות מושפעות ותוקנות
Apache identifies the following package ranges as affected. (NVD)
Commons BeanUtils 1.x
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.11.0</version>
</dependency>
Affected:
commons-beanutils:commons-beanutils
1.0 <= version < 1.11.0
Minimum fixed version:
1.11.0
Commons BeanUtils 2.x
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-beanutils2</artifactId>
<version>2.0.0-M2</version>
</dependency>
Affected:
org.apache.commons:commons-beanutils2
2.0.0-M1 <= version < 2.0.0-M2
Minimum fixed version:
2.0.0-M2
Apache released both 1.11.0 and 2.0.0-M2 on May 25, 2025. The release notes list the addition of SUPPRESS_DECLARING_CLASS in both fixed lines. (Apache Commons)
When possible, use the newest compatible release rather than treating the minimum fixed version as a permanently pinned target. Apache’s current project landing page lists the 2.x line separately and warns that BeanUtils 2 changes packages from org.apache.commons.beanutils אל org.apache.commons.beanutils2, meaning a 1.x-to-2.x migration is not simply a drop-in version replacement. (Apache Commons)
What Changed in the Patch?
The central patch is small, but its security effect is significant.
Before the fix, the default introspector reset sequence included:
introspectors.add(DefaultBeanIntrospector.INSTANCE);
introspectors.add(
SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS
);
The patch adds:
introspectors.add(
SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS
);
The new constant is defined using the property name:
public static final SuppressPropertiesBeanIntrospector
SUPPRESS_DECLARING_CLASS =
new SuppressPropertiesBeanIntrospector(
Collections.singleton("declaringClass")
);
א SuppressPropertiesBeanIntrospector removes configured properties from the introspection context. In practical terms, even though JavaBeans introspection discovers getDeclaringClass(), the suppressing introspector removes the resulting declaringClass descriptor before the application can navigate through it.
Apache enables both the earlier כיתה suppression and the new declaringClass suppression by default. (GitHub)
The fix is therefore a secure-default change rather than a removal of all dynamic property functionality.
Relationship to Earlier BeanUtils Class-Property Hardening
CVE-2025-48734 is easier to understand when viewed as another instance of a recurring introspection-boundary problem.
All Java objects inherit getClass(). Under JavaBeans conventions, this method can appear as a property named כיתה. Uncontrolled access to כיתה may lead from an application object to its כיתה object and then toward its class loader.
Apache previously introduced SuppressPropertiesBeanIntrospector in BeanUtils 1.9.2 and later changed default behavior so the general כיתה property would be suppressed. Apache’s project documentation describes the earlier risk and shows that the protection could be removed to restore legacy behavior. (Apache Commons)
CVE-2025-48734 addresses an adjacent route:
Ordinary Java object:
object.class.classLoader
Java enum:
enumValue.declaringClass.classLoader
Blocking כיתה did not necessarily block declaringClass. The enum method provided a second route to a כיתה אובייקט.
The broader lesson is that dangerous reflection paths should not be defended by blocking one historically known property name at a time. Applications should restrict the complete set of externally selectable properties to business fields that are intentionally exposed.
How to Determine Whether Your Application Is Exposed

A reliable CVE-2025-48734 assessment should combine dependency inventory, code search, data-flow analysis, object-graph analysis, runtime configuration review, and controlled validation.
Step 1: Identify Direct and Transitive Dependencies
For Maven:
mvn dependency:tree \
-Dincludes=commons-beanutils:commons-beanutils
Check the 2.x coordinates separately:
mvn dependency:tree \
-Dincludes=org.apache.commons:commons-beanutils2
The Maven Dependency Plugin’s dependency:tree goal displays the project dependency tree and can also produce structured formats such as JSON, DOT, GraphML, and TGF. (Apache Maven)
To capture a machine-readable tree:
mvn dependency:tree \
-DoutputType=json \
-DoutputFile=dependency-tree.json
For Gradle:
./gradlew dependencies --configuration runtimeClasspath
Then inspect a specific dependency:
./gradlew dependencyInsight \
--dependency commons-beanutils \
--configuration runtimeClasspath
Do not inspect only the top-level build file. BeanUtils frequently arrives transitively through older frameworks, developer tools, reporting components, validation libraries, application servers, integration platforms, or internal shared libraries.
Step 2: Inspect the Final Runtime Artifact
A correct source dependency tree does not always match the deployed artifact. Shading, repackaging, container layering, manually copied JARs, application-server modules, or build-cache behavior can preserve an older version.
Inspect an executable JAR:
jar tf application.jar | grep -i beanutils
For a Spring Boot archive:
unzip -l application.jar | grep -i beanutils
For a container image:
docker run --rm --entrypoint sh application-image \
-c 'find / -iname "*beanutils*.jar" 2>/dev/null'
Record the checksum and implementation version of every discovered JAR. Do not assume that a filename accurately represents the embedded bytecode.
Step 3: Search for Relevant BeanUtils APIs
Search Java source:
rg -n \
'PropertyUtilsBean|BeanUtilsBean|PropertyUtils\.getProperty|PropertyUtils\.getNestedProperty|BeanUtils\.getProperty|BeanUtils\.getNestedProperty' \
src/
Also search for static imports and wrapper classes:
rg -n \
'getProperty\s*\(|getNestedProperty\s*\(' \
src/
The second search produces more noise, but it helps locate internal abstractions that hide the BeanUtils call.
Relevant APIs include:
PropertyUtilsBean.getProperty(...)
PropertyUtilsBean.getNestedProperty(...)
BeanUtilsBean.getProperty(...)
BeanUtilsBean.getNestedProperty(...)
PropertyUtils.getProperty(...)
PropertyUtils.getNestedProperty(...)
BeanUtils.getProperty(...)
BeanUtils.getNestedProperty(...)
The official CVE description specifically identifies PropertyUtilsBean.getProperty() ו getNestedProperty(). Because BeanUtilsBean relies on PropertyUtilsBean, applications using the higher-level BeanUtils interface may also inherit the behavior. (NVD)
Step 4: Trace the Property-Name Data Flow
For every call site, identify where the property expression comes from.
High-risk sources include:
request.getParameter("field")
request.getParameter("sort")
request.getHeader("X-Property")
jsonNode.get("property").asText()
message.getPropertyPath()
configuration.getDynamicField()
templateContext.getExpression()
The most important question is not:
Does the application use BeanUtils?
It is:
Can data controlled by an untrusted party influence the property expression interpreted by BeanUtils?
Trace normalization, validation, authorization, and transformations along the path. A field may appear restricted at the controller layer but later be concatenated into a nested expression:
String property = "account." + requestedField;
return propertyUtils.getProperty(wrapper, property);
An allowlist that validates only the first token may also be bypassed if dots, brackets, parentheses, or encoded separators survive later decoding.
Step 5: Identify Reachable Enums
Inspect the bean type supplied as the first argument and map the properties reachable from it.
לדוגמה:
ApiResponse
└── user
└── account
└── status: AccountStatus enum
The potentially interesting property path would be:
user.account.status.declaringClass
Automated reachability analysis can examine:
- Getter return types
- Record components
- Map values
- Collection elements
- Nested DTOs
- Framework-generated proxies
- DynaBeans
- Objects returned by custom property resolvers
An enum does not need to be a direct property of the initial object. It only needs to be reachable through a valid BeanUtils expression.
Step 6: Review Introspector Customization
Search for configuration that removes or resets security introspectors:
rg -n \
'removeBeanIntrospector|resetBeanIntrospectors|addBeanIntrospector|SUPPRESS_CLASS|SUPPRESS_DECLARING_CLASS' \
src/
The patched library enables SUPPRESS_DECLARING_CLASS by default, but Apache explicitly preserves the ability to remove it and regain the old behavior. A patched version can therefore be made vulnerable again through unsafe customization. (NVD)
Pay particular attention to code resembling:
propertyUtilsBean.removeBeanIntrospector(
SuppressPropertiesBeanIntrospector.SUPPRESS_DECLARING_CLASS
);
Apache’s own test describes this opt-out as making the application less secure. (GitHub)
Safe Local Validation for CVE-2025-48734
Validation should be performed only in an isolated test environment that you own or are explicitly authorized to assess. There is no need to attempt arbitrary code execution to determine whether the vulnerable property path is present.
A safe test can stop after verifying access to the class loader.
Minimal Test Fixture
public enum AccessLevel {
USER,
ADMIN
}
public class TestAccount {
private final AccessLevel accessLevel = AccessLevel.USER;
public AccessLevel getAccessLevel() {
return accessLevel;
}
}
Vulnerable-Behavior Test
Run this only against an intentionally vulnerable test dependency:
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.junit.jupiter.api.Test;
class BeanUtilsExposureTest {
@Test
void declaringClassPathShouldNotReachClassLoader() throws Exception {
PropertyUtilsBean propertyUtils = new PropertyUtilsBean();
TestAccount account = new TestAccount();
Object result = propertyUtils.getNestedProperty(
account,
"accessLevel.declaringClass.classLoader"
);
assertNotNull(result);
assertTrue(result instanceof ClassLoader);
}
}
On a vulnerable configuration, the expression may return a ClassLoader.
This test deliberately avoids:
- Loading attacker-controlled classes
- Writing files
- Starting processes
- Accessing external URLs
- Modifying the classpath
- Invoking framework-specific class-loading behavior
The result proves that the access-control boundary is missing, not that every possible downstream impact has been weaponized.
Patched-Behavior Regression Test
After upgrading, preserve the test as a negative security regression:
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.junit.jupiter.api.Test;
class BeanUtilsPatchedTest {
@Test
void declaringClassMustBeSuppressedByDefault() {
PropertyUtilsBean propertyUtils = new PropertyUtilsBean();
TestAccount account = new TestAccount();
assertThrows(
NoSuchMethodException.class,
() -> propertyUtils.getNestedProperty(
account,
"accessLevel.declaringClass.classLoader"
)
);
}
}
Apache’s own patch tests use the same security expectation: default access through testEnum.declaringClass.classLoader must produce NoSuchMethodException. (GitHub)
Validate the Real Application Wrapper
בדיקות new PropertyUtilsBean() alone is insufficient when the production application uses:
- A singleton BeanUtils instance
- A custom
BeanUtilsBean - A custom property resolver
- Additional introspectors
- Framework-provided wrappers
- Cached property descriptors
- A shaded or modified BeanUtils fork
The strongest regression test calls the same internal service or helper used in production.
לדוגמה:
@Test
void publicFieldSelectorMustRejectRuntimeProperties() {
assertThrows(
InvalidFieldException.class,
() -> fieldSelectionService.readField(
testAccount,
"accessLevel.declaringClass.classLoader"
)
);
}
This verifies the application boundary rather than only the third-party library default.
Primary Mitigation: Upgrade the Dependency
The preferred remediation is straightforward:
- שדרוג
commons-beanutils:commons-beanutilsto 1.11.0 or later. - שדרוג
org.apache.commons:commons-beanutils2to 2.0.0-M2 or later. - Rebuild and redeploy every affected runtime artifact.
- Confirm that no older JAR remains in the container, application server, plugin directory, or shared classpath.
- Verify that application code has not removed
SUPPRESS_DECLARING_CLASS.
Apache explicitly recommends these fixed versions for the affected package lines. (NVD)
Maven Dependency Management
For organizations that receive BeanUtils transitively, dependency management can enforce the fixed 1.x version:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.11.0</version>
</dependency>
</dependencies>
</dependencyManagement>
Then verify the resolved version:
mvn dependency:tree \
-Dincludes=commons-beanutils:commons-beanutils
Do not assume that adding a fixed direct dependency always wins. Maven mediation, parent dependency management, shading, and application-server modules can still produce unexpected results.
Gradle Constraint
dependencies {
constraints {
implementation("commons-beanutils:commons-beanutils:1.11.0") {
because("Fixes CVE-2025-48734")
}
}
}
Then confirm:
./gradlew dependencyInsight \
--dependency commons-beanutils \
--configuration runtimeClasspath
Temporary Mitigation When an Immediate Upgrade Is Impossible
A temporary workaround should not replace the vendor fix, but applications based on a BeanUtils line that already supports SuppressPropertiesBeanIntrospector can explicitly suppress the property.
For BeanUtils 1.x:
import java.util.Collections;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector;
public final class SafePropertyUtilsFactory {
private SafePropertyUtilsFactory() {
}
public static PropertyUtilsBean create() {
PropertyUtilsBean propertyUtils = new PropertyUtilsBean();
propertyUtils.addBeanIntrospector(
new SuppressPropertiesBeanIntrospector(
Collections.singleton("declaringClass")
)
);
return propertyUtils;
}
}
Create and configure the instance before it processes untrusted objects. Avoid sharing an older instance whose property descriptors may already have been cached under an unsafe configuration.
Where appropriate, suppress both reflective bridge properties:
propertyUtils.addBeanIntrospector(
new SuppressPropertiesBeanIntrospector(
Arrays.asList("class", "declaringClass")
)
);
This workaround has limitations. It depends on correct initialization, correct instance usage, correct introspector order, and the absence of application code that later resets the configuration. An upgrade remains the more reliable control.
Do Not Rely Only on a Blocklist
Rejecting the literal string declaringClass is useful as an emergency control, but it should not be the primary security boundary.
A property-language blocklist can fail because:
- Another dangerous reflective property may exist.
- Custom resolvers may normalize tokens differently.
- The application may concatenate trusted and untrusted fragments.
- Encoded input may be decoded after validation.
- Indexed and mapped expressions may create unexpected paths.
- A future library or JDK change may expose another bridge property.
- The same property engine may be used outside the guarded endpoint.
Prefer an allowlist of permitted business properties:
private static final Set<String> PUBLIC_ACCOUNT_FIELDS =
Set.of(
"id",
"displayName",
"status",
"createdAt"
);
public Object readPublicField(
Account account,
String requestedField) throws Exception {
if (!PUBLIC_ACCOUNT_FIELDS.contains(requestedField)) {
throw new IllegalArgumentException("Unsupported field");
}
return propertyUtils.getSimpleProperty(
account,
requestedField
);
}
Notice the use of getSimpleProperty() after rejecting nested syntax. This reduces the available expression language rather than merely filtering a few known-dangerous words.
Detecting Exploitation Attempts
CVE-2025-48734 attempts may appear in HTTP parameters, JSON properties, message payloads, template variables, search filters, sort keys, GraphQL-like selectors, or administrative configuration.
Potentially suspicious tokens include:
declaringClass
classLoader
.declaringClass.
.declaringClass.classLoader
class.classLoader
A detection rule might alert on these tokens when they appear in fields expected to contain property names:
field
property
propertyPath
sort
sortBy
groupBy
selector
column
attribute
expression
A conceptual log query could be:
(property_field contains "declaringClass")
OR
(property_field contains "classLoader")
Context matters. The string may appear legitimately in developer tooling, source-code search, documentation, or diagnostic traffic. Higher-confidence detection combines the token with:
- A request to an endpoint supporting dynamic field selection
- A server-side BeanUtils exception
- א
NoSuchMethodExceptionreferencingdeclaringClass - Repeated exploration of nested property paths
- Attempts to access
כיתה,classLoader, resources, modules, or protection domains - Unexpected serialization of
כיתהאוClassLoaderobjects
Patched systems may generate NoSuchMethodException when an attacker probes the blocked property. Apache’s regression tests confirm that this is the intended outcome. (GitHub)
Useful Application Logging
Applications that intentionally support property selection should record:
timestamp
authenticated principal
source IP or workload identity
endpoint or message channel
requested property expression
root object type
allowlist decision
exception type
request correlation ID
Avoid logging the complete business object or sensitive values returned by the property lookup.
Incident Response Questions
When suspicious expressions are observed, determine:
- Was the runtime using an affected BeanUtils version?
- Which code path interpreted the property?
- Did the expression reach an enum?
- Was
declaringClasssuccessfully resolved? - Did the result reach
classLoader? - Was the resulting object serialized, rendered, logged, or passed into another engine?
- Were sensitive resources, classes, or runtime metadata exposed?
- Did the service account perform unusual file, network, process, or class-loading activity?
- Does the application contain plugins, scripts, template engines, or expression languages that could complete a secondary chain?
- Are there older copies of BeanUtils elsewhere in the environment?
Preserve request logs, application logs, container metadata, loaded-module information, and the exact deployed artifact before rebuilding.
Java Dependency Exposure and Transitive Risk
CVE-2025-48734 is also a software supply-chain management problem. A development team may never have added BeanUtils directly, yet still deploy it through another component.
A transitive dependency becomes especially difficult to manage when:
- The introducing framework is no longer maintained.
- A vendor product embeds the JAR internally.
- A fat JAR contains several copies.
- The component is shaded under another package name.
- An application server supplies a shared version.
- A plugin loads its own dependency graph.
- Different services inherit different versions from separate parent POMs.
- Security scanners identify the package, but source owners cannot locate the call site.
Dependency presence and exploitability should be treated as separate dimensions.
| Dependency status | Reachability status | עדיפות |
|---|---|---|
| Affected | Confirmed attacker-controlled path | Critical remediation priority |
| Affected | BeanUtils API reachable, input trust unknown | High-priority investigation |
| Affected | No relevant API usage found | Upgrade through normal emergency process |
| Fixed | Security introspector removed by custom code | High-priority code remediation |
| Fixed | Default configuration retained | Regression test and monitor |
| Unknown | Shaded or vendor-packaged component | Obtain vendor confirmation or inspect bytecode |
Even when code-level analysis suggests that the vulnerable method is unreachable, upgrading remains valuable. Reachability analysis can miss reflection, framework configuration, runtime-generated code, plugins, or dormant administrative features.
Avoiding Common Assessment Mistakes
Mistake 1: Treating Every Vulnerable JAR as Confirmed RCE
A scanner can establish version exposure. It usually cannot establish that untrusted property expressions reach a vulnerable API or that a complete RCE chain exists.
Report the package finding accurately:
The application includes an affected Apache Commons BeanUtils version. Code-level reachability and external input control require validation.
Then gather evidence.
Mistake 2: Dismissing the CVE Because the Application Does Not Call getNestedProperty() Directly
Higher-level helpers may delegate to PropertyUtilsBean. Static utility methods, framework wrappers, internal shared libraries, and BeanUtilsBean deserve review. The official advisory notes that PropertyUtilsBean, and consequently BeanUtilsBean, are affected. (NVD)
Mistake 3: Checking Only the כיתה Property
CVE-2025-48734 specifically concerns the enum route through declaringClass. An application may correctly block:
כיתה
while remaining vulnerable to:
status.declaringClass
Mistake 4: Upgrading but Disabling the New Protection
The fixed library allows applications to remove SUPPRESS_DECLARING_CLASS. Apache retained this behavior for compatibility, but its own test describes the opt-out as less secure. (GitHub)
Mistake 5: Testing Against Production
A proof that retrieves a class loader is sufficient to demonstrate the access-control failure. There is no need to attempt class injection, command execution, or destructive follow-on behavior in production.
Mistake 6: Updating the POM Without Verifying the Deployment
Always inspect the resolved dependency tree and final artifact. Old JARs may remain inside a container layer, application-server module, plugin directory, or manually maintained deployment package.
Recommended Remediation Priority
Priority 1: Internet-Reachable Dynamic Property APIs
Patch immediately when an internet-accessible endpoint passes requester-controlled field names or nested expressions to BeanUtils.
דוגמאות לכך כוללות:
- Dynamic object inspection endpoints
- Reporting APIs
- Search and filtering systems
- Administrative data browsers
- Generic export endpoints
- Rule engines
- Template preview systems
- API gateways that transform object fields
Priority 2: Authenticated or Internal Dynamic Property APIs
The contributed CVSS vector assumes low privileges rather than no privileges. Internal and authenticated access should not be dismissed. Compromised user accounts, tenant users, support operators, service identities, and lateral movement can make such paths exploitable. (NVD)
Priority 3: Dependency Present but Reachability Unconfirmed
Upgrade on an accelerated maintenance schedule while completing code analysis. The absence of an obvious public endpoint does not prove that background jobs, message consumers, plugin systems, or dormant administrative features are safe.
Priority 4: Fixed Version with No Unsafe Customization
Keep a regression test, maintain dependency monitoring, and confirm that future refactoring does not reintroduce arbitrary nested property evaluation.
Secure Design Patterns Beyond the Patch
The BeanUtils update fixes the known declaringClass route by default, but secure application architecture should reduce dependence on open-ended reflection.
Use Explicit Field Mappings
private static final Map<String, Function<Order, Object>> EXPORT_FIELDS =
Map.of(
"orderId", Order::getId,
"status", Order::getStatus,
"createdAt", Order::getCreatedAt
);
This design makes the exposure contract visible during code review.
Separate Public DTOs From Internal Domain Objects
Do not let generic reflection operate on framework objects, persistence entities, security principals, class-loader-aware components, or rich internal domain models.
Create a restricted DTO:
public record PublicAccountView(
String id,
String displayName,
String status
) {
}
Even if a dynamic selector is necessary, operate on a narrow object that contains no privileged runtime relationships.
Reject Nested Syntax When It Is Not Required
private static final Pattern SIMPLE_PROPERTY =
Pattern.compile("[A-Za-z][A-Za-z0-9_]{0,63}");
if (!SIMPLE_PROPERTY.matcher(field).matches()) {
throw new IllegalArgumentException("Invalid field");
}
This blocks dots, brackets, and mapped-property syntax. It is not a substitute for an allowlist, but it reduces unnecessary parser capability.
Enforce Authorization Per Field
A field may be technically safe from reflection abuse while still exposing sensitive data.
if ("internalRiskScore".equals(field)
&& !principal.hasRole("SECURITY_ANALYST")) {
throw new AccessDeniedException("Field not permitted");
}
Property selection is an authorization decision, not merely a string-parsing operation.
Add Negative Security Tests
Maintain tests for:
class
class.classLoader
declaringClass
status.declaringClass
status.declaringClass.classLoader
Also include unexpected nested, indexed, and mapped expressions.
Validating CVE-2025-48734 With an Authorized Pentest Workflow
A useful CVE verification workflow should produce more than a scanner alert. It should identify the deployed dependency, locate the property-resolution endpoint, establish whether the input is attacker-controlled, prove whether an enum is reachable, and stop at the minimum evidence required to demonstrate the security boundary.
For an authorized Java target, the evidence chain should include:
Dependency version
->
Relevant API call
->
External input source
->
Object type and reachable enum
->
Safe declaringClass probe
->
Observed blocked or exposed result
->
Remediation verification
Penligent’s CVE verification workflow can be used to coordinate this type of evidence-driven assessment: the tester provides the known vulnerability context and authorized target, the system selects appropriate tools, and the result is retained as a reproducible risk and validation chain. For CVE-2025-48734, the test policy should explicitly prohibit destructive class loading or command execution and focus on dependency resolution, reachability, safe property traversal, and post-upgrade regression.
The objective is not to maximize exploit spectacle. It is to answer the operational question:
Can an untrusted user cross from an intended business property into Java runtime metadata in this specific application?
Frequently Asked Questions
Is CVE-2025-48734 a remote code execution vulnerability?
Apache states that uncontrolled access to an enum’s declaring class can expose the class loader and allow remote attackers to execute arbitrary code. However, the vulnerable dependency alone does not prove that every application has an end-to-end RCE path. External control of the property expression, a reachable enum, successful nested traversal, and an environment-specific follow-on capability are required. (NVD)
Does every application using Apache Commons BeanUtils need an emergency patch?
Every affected deployment should be upgraded. Remediation urgency is highest when untrusted input controls property paths. Applications with no reachable dynamic property API may have lower immediate exploitability, but they still carry an affected dependency and can become exposed through future code changes or unexamined framework behavior.
Which versions fix CVE-2025-48734?
The minimum fixed versions are:
commons-beanutils:commons-beanutils 1.11.0
org.apache.commons:commons-beanutils2 2.0.0-M2
Use those versions or later compatible releases. (NVD)
Why are there two different Maven coordinates?
BeanUtils 1.x uses:
commons-beanutils:commons-beanutils
The 2.x line uses:
org.apache.commons:commons-beanutils2
Apache also changed the Java package namespace from org.apache.commons.beanutils אל org.apache.commons.beanutils2, so migrating between major lines may require source changes. (Apache Commons)
Is BeanUtils 1.9.4 safe?
No. Version 1.9.4 contains earlier protection for the general כיתה property but is still inside the affected range for CVE-2025-48734. The enum declaringClass route is fixed in 1.11.0. (GitHub)
Can a WAF rule mitigate the vulnerability?
A WAF rule that blocks declaringClass ו classLoader may reduce immediate exposure on known endpoints, but it is incomplete. Property expressions may enter through non-HTTP channels, internal APIs, message queues, encoded input, administrative interfaces, or server-side configuration. Upgrade the library and restrict property expressions in application code.
Does the patch remove nested property access?
No. The fix adds a suppressing introspector for declaringClass. Normal nested application properties remain available. (GitHub)
Can an application disable the fix?
Yes. The patched API permits removal of SUPPRESS_DECLARING_CLASS to restore legacy access. This should be treated as an unsafe compatibility override and documented as a security exception. (NVD)
What is the safest way to verify the patch?
Create a local object containing an enum and call the same application wrapper used in production with:
enumProperty.declaringClass.classLoader
A secure default configuration should reject the path, typically with NoSuchMethodException. Do not continue into class loading or code execution. Apache uses this blocked-result pattern in its regression test. (GitHub)
Final Assessment
CVE-2025-48734 is a high-severity access-control problem at the boundary between Java application data and JVM runtime metadata.
The flaw exists because vulnerable Apache Commons BeanUtils versions allow a dynamically evaluated property expression to traverse from an enum value into its declaringClass property. That transition produces a כיתה object and may expose the associated class loader. Apache considers the resulting path capable of supporting arbitrary code execution, although real-world exploitability depends on how the application sources property expressions and what additional runtime behavior is reachable. (NVD)
The correct response has four parts.
First, upgrade commons-beanutils:commons-beanutils to 1.11.0 or later, or org.apache.commons:commons-beanutils2 to 2.0.0-M2 or later.
Second, verify the dependency inside the actual deployed artifact rather than only the source build file.
Third, find every path where untrusted input can influence BeanUtils property expressions, especially calls to getProperty() ו getNestedProperty().
Fourth, replace open-ended reflection with explicit business-field allowlists wherever possible.
The library patch closes the known enum bridge by default. The architectural fix is to ensure that an external user never receives unrestricted authority to navigate a Java object graph in the first place.

