
Proof-of-concept demonstrating a deserialization filter bypass in Apache MINA leading to remote code execution, with detailed root cause analysis, exploit PoCs, and remediation guidance.
CVSS 3.1: 9.8 CRITICAL AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE: CWE-502 Deserialization of Untrusted Data
Reporter: Venkatraman Kumar, Securin
Advisory: Apache Mailing List
Apache MINA versions 2.1.0 through 2.1.11 and 2.2.0 through 2.2.6 contain a deserialization filter bypass in AbstractIoBuffer.resolveClass(). The acceptMatchers allowlist — intended to restrict which Java classes can be deserialized — is completely skipped when ObjectStreamClass.forClass() returns null.
An attacker with network access to a MINA endpoint using ObjectSerializationCodecFactory can craft a protocol payload that bypasses the class filter, enabling via standard Java deserialization gadget chains (e.g., Commons Collections).
This is an incomplete fix for CVE-2026-41635. The original patch was applied to the 2.0.x branch but was never backported to 2.1.x or 2.2.x due to a merge oversight.
| Branch | Vulnerable | Fixed |
|---|---|---|
| 2.1.x | 2.1.0 – 2.1.11 | 2.1.12 |
| 2.2.x | 2.2.0 – 2.2.6 | 2.2.7 |
The vulnerability is in AbstractIoBuffer.resolveClass(), which handles class resolution during Java object deserialization.
MINA uses a custom serialization protocol with two class descriptor types:
In the vulnerable code, the acceptMatchers filter is only checked in the type-1 branch (when forClass() returns non-null). The type-0 branch calls Class.forName() directly, bypassing the filter entirely:
// AbstractIoBuffer.java — VULNERABLE (2.2.6)
protected Class<?> resolveClass(ObjectStreamClass desc) {
Class<?> clazz = desc.forClass();
if (clazz == null) {
// BUG: No acceptMatchers check — filter completely bypassed
return Class.forName(name, false, classLoader);
} else {
// Filter only applied here
for (ClassNameMatcher matcher : acceptMatchers) { ... }
}
}
The fix in 2.2.7 moves the filter check before the branch:
// AbstractIoBuffer.java — FIXED (2.2.7)
protected Class<?> resolveClass(ObjectStreamClass desc) {
String className = desc.getName();
// Filter applied FIRST, regardless of forClass() result
if (!acceptMatchers.stream().anyMatch(m -> m.matches(className))) {
throw new ClassNotFoundException("Class not in accept list " + className);
}
Class<?> clazz = desc.forClass();
// ... safe resolution follows
}
Attacker Vulnerable MINA Server
| |
| 1. Craft MINA payload with type-0 |
| descriptors for gadget chain classes |
| |
| 2. Send to endpoint using |
| ObjectSerializationCodecFactory -------->|
| |
| 3. readClassDescriptor() reads type-0|
| → delegates to super (std Java) |
| |
| 4. resolveClass() sees forClass()==null
| → Class.forName() WITHOUT filter |
| |
| 5. Gadget chain fully deserialized |
| → readObject() triggers chain |
| → Runtime.exec() fires |
| |
| RCE ACHIEVED |
IoBuffer.getObject() or ObjectSerializationCodecFactoryaccept() configured (applications without a filter were already exploitable via CVE-2026-41635)The attacker controls the serialized byte stream. By using type-0 class descriptors (instead of type-1) for Serializable gadget chain classes, every class in the deserialization graph bypasses the acceptMatchers filter, regardless of the application's allowlist configuration.
Three PoCs demonstrate escalating impact:
| PoC | What it proves |
|---|---|
FilterBypassPoC.java | Filter bypass for primitives, non-Serializable classes, arrays |
CraftedBypassPoC.java | Attacker-crafted type-0 payloads bypass filter for ANY Serializable class |
RcePoC.java | Full RCE via CC6 gadget chain through the filter bypass |
Classes not in the accept list are deserialized without restriction:

An attacker crafts MINA protocol payloads with type-0 descriptors to load any class past a String-only accept list:

CC6-variant gadget chain (HashSet → TiedMapEntry → LazyMap → ChainedTransformer → Runtime.exec()) achieves command execution through the filter bypass:

The same tests are blocked on the fixed version:

The gadget chain is rejected by the filter:

The fastest way to test — no JDK or Maven required:
# Clone this repo
git clone https://github.com/dinosn/CVE-2026-42779.git
cd CVE-2026-42779
# Build and run all PoCs
docker build -t cve-2026-42779 .
docker run --rm cve-2026-42779
# Run individual PoCs
docker run --rm cve-2026-42779 bypass # Filter bypass only
docker run --rm cve-2026-42779 crafted # Crafted payload bypass
docker run --rm cve-2026-42779 rce # Full RCE
# Drop into a shell to explore
docker run --rm -it cve-2026-42779 shell
The image bundles the vulnerable MINA 2.2.6 JAR, Commons Collections 3.2.2, and all three pre-compiled PoCs. Everything runs self-contained inside the container.
If you prefer to build from source:
# Clone and build the vulnerable version
git clone https://github.com/apache/mina.git /tmp/apache-mina
cd /tmp/apache-mina
git checkout 2.2.6
mvn install -pl mina-core -DskipTests -q
# Download commons-collections (for RCE PoC)
curl -sL "https://repo1.maven.org/maven2/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar" \
-o commons-collections-3.2.2.jar
# Compile the PoCs
javac -cp mina-core/target/mina-core-2.2.6.jar FilterBypassPoC.java
javac -cp mina-core/target/mina-core-2.2.6.jar CraftedBypassPoC.java
javac -cp mina-core/target/mina-core-2.2.6.jar:commons-collections-3.2.2.jar RcePoC.java
# Run filter bypass PoC
java -cp .:mina-core/target/mina-core-2.2.6.jar FilterBypassPoC
# Run crafted payload PoC
java -cp .:mina-core/target/mina-core-2.2.6.jar CraftedBypassPoC
# Run full RCE PoC
java -Dorg.apache.commons.collections.enableUnsafeSerialization=true \
--add-opens java.base/java.util=ALL-UNNAMED \
--add-opens java.base/java.lang.reflect=ALL-UNNAMED \
-cp .:mina-core/target/mina-core-2.2.6.jar:commons-collections-3.2.2.jar \
RcePoC
Or use the included Makefile:
make run-all # Build and run all three PoCs
make run-rce # Just the RCE PoC
Requirements: JDK 11+ and Maven (for source build) or Docker (for container)
Upgrade to Apache MINA 2.1.12 or 2.2.7.
If upgrading is not immediately possible:
IoBuffer.getObject() or ObjectSerializationCodecFactory with untrusted input| Date | Event |
|---|---|
| 2026-05-01 | Advisory published by Apache MINA PMC |
| 2026-05-01 | Fixed versions 2.1.12 and 2.2.7 released |
| 2026-05-02 | This PoC developed and tested |
This proof of concept is provided for defensive security research, education, and authorized penetration testing only. Use responsibly and only against systems you have permission to test.