Severity: moderate 

Affected versions:

- Apache OpenNLP (org.apache.opennlp:opennlp-tools) before 2.5.9
- Apache OpenNLP (org.apache.opennlp:opennlp-tools) 3.0 before 3.0.0-M3

Description:

Arbitrary Class Instantiation via Model Manifest in Apache OpenNLP 
ExtensionLoader





Versions Affected: before 2.5.9, before 3.0.0-M3





Description: 

The ExtensionLoader.instantiateExtension(Class, String) method loads a class by 
its fully-qualified name via Class.forName() and invokes its no-arg 
constructor, with the class name sourced from the manifest.properties entry of 
a model archive. The existing isAssignableFrom check correctly rejects classes 
that are not subtypes of the expected extension interface (BaseToolFactory for 
factory=, ArtifactSerializer for serializer-class-*), but the check runs after 
Class.forName() has already loaded and initialized the named class. 

Class.forName() with default initialization semantics executes the target 
class's static initializer before returning, so an attacker who can supply a 
crafted model archive can cause the static initializer of any class on the 
classpath to run during model loading, regardless of whether that class passes 
the subsequent type check. 

Exploitation requires a class with attacker-useful side effects in its static 
initializer (for example, JNDI lookup, outbound network I/O, or filesystem 
access) to be present on the classpath, so this is not a drop-in remote code 
execution; however, the attack surface grows as third-party model distribution 
becomes more common (community model repositories, Hugging Face-style sharing), 
where users routinely load model files from origins they do not control. A 
secondary, narrower vector affects deployments that ship legitimate 
BaseToolFactory or ArtifactSerializer subclasses with side-effecting no-arg 
constructors: a malicious manifest can name such a class and force its 
constructor to run during model load.





Mitigation: 



  *  2.x users should upgrade to 2.5.9. 
  *  3.x users should upgrade to 3.0.0-M3. 




Note: The fix introduces a package-prefix allowlist that is consulted before 
Class.forName() is invoked, so the static initializer of a disallowed class is 
never executed. Classes under the opennlp. prefix remain permitted by default. 
Deployments that load models referencing factories or serializers outside 
opennlp.* must opt those packages in, either programmatically via 
ExtensionLoader.registerAllowedPackage(String) before the first model load, or 
by setting the OPENNLP_EXT_ALLOWED_PACKAGES system property to a 
comma-separated list of allowed package prefixes. 

Users who cannot upgrade immediately should ensure that all model files are 
sourced from trusted origins and should audit their classpath for classes with 
side-effecting static initializers or constructors, particularly any that 
perform JNDI lookups, network requests, or filesystem operations during class 
initialization.

This issue is being tracked as OPENNLP-1820 

Credit:

Subramanian S (finder)

References:

https://opennlp.apache.org/
https://www.cve.org/CVERecord?id=CVE-2026-42027
https://issues.apache.org/jira/browse/OPENNLP-1820

Reply via email to