Lucene search
+L

Chronicle Wire 2026.8 Class Instantiation

🗓️ 27 Aug 2026 00:00:00Reported by Ron EType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 5 Views

Arbitrary class instantiation in Chronicle Wire 2026.8 via YAML type tags in untyped deserialization.

Code
Chronicle Wire permits YAML type tags supplied within serialized input to
    influence Java class selection and object instantiation during untyped
    deserialization.
    
    When applications deserialize attacker-controlled or otherwise untrusted
    YAML through APIs such as readObject() or object(Object.class), an
    externally controlled YAML type tag can identify a Java class that
    Chronicle Wire resolves through its configured ClassLookup.
    
    When the default permissive class lookup is used and the supplied class can
    be resolved, the resulting class can propagate through Chronicle Wire's
    generic object deserialization path and ultimately reach
    ObjectUtils.newInstance(clazz). Chronicle Wire can then invoke the selected
    class's deserialization lifecycle, including readMarshallable() where
    applicable.
    
    The security-sensitive behavior is therefore not limited to ordinary data
    binding. Externally supplied serialized data can influence *which Java
    class is instantiated during deserialization*.
    
    The accompanying proof of concept confirms that:
    
       - An externally controlled YAML type tag selects the Java class
       instantiated by readObject().
       - The selected class's constructor is executed.
       - A selected class's readMarshallable() implementation is automatically
       invoked during deserialization.
       - An existing third-party class already present on the runtime classpath
       can be instantiated using a YAML type tag.
       - Configuring a restrictive ClassLookup prevents the demonstrated
       arbitrary class selection.
    
    The practical security impact depends on the classes available on the
    target application's classpath and whether untrusted YAML reaches an
    affected untyped deserialization API. The PoC establishes the arbitrary
    class-selection and instantiation primitive but does not claim universal
    arbitrary code execution.
    Vulnerability Details
    
    Chronicle Wire supports YAML type tags capable of identifying Java classes
    during deserialization.
    
    For example:
    
    !fully.qualified.ClassName
    
    The parser does not treat this value solely as descriptive metadata. The
    supplied class name is resolved using the wire's configured ClassLookup.
    
    The default wire configuration initializes the lookup using the global
    alias pool:
    
    protected ClassLookup classLookup =
        ClassAliasPool.CLASS_ALIASES;
    
    When a YAML TAG token is encountered, the supplied type is resolved:
    
    Class<?> typePrefix() {
        ...
        return classLookup().forName(stringBuilder);
    }
    
    The class represented by the serialized YAML can therefore influence the
    Java type selected during deserialization.
    
    In affected object-reading paths, Chronicle Wire can subsequently
    instantiate the resolved class:
    
    Class<?> clazz = typePrefix();
    
    if (clazz != object.getClass())
        object = ObjectUtils.newInstance(clazz);
    
    The externally selected type can also propagate into the generic object
    deserialization path:
    
    Object o = typePrefixOrObject(clazz);
    
    ...
    
    t = Wires.object2(..., (Class) o);
    
    Within Wires.object2(), the type supplied by serialized input can replace
    the caller's original type under several conditions:
    
    if (clazz == null
            || clazz.isAssignableFrom(clazz2)
            || ReadResolvable.class.isAssignableFrom(clazz2)
            || !ObjectUtils.isConcreteClass(clazz))
    {
        clazz = clazz2;
    }
    
    Chronicle Wire can then instantiate the selected class:
    
    if (o == null)
        o = ObjectUtils.newInstance(clazz);
    
    and continue the object's deserialization lifecycle:
    
    Wires.readMarshallable(
        clazz,
        o,
        in.wireIn(),
        true);
    
    As a result, when permissive class resolution is available, externally
    controlled YAML can influence both the class instantiated by Chronicle Wire
    and the class-specific deserialization logic subsequently executed.
    Root Cause
    
    The root cause is the use of serialized YAML type information to select
    Java classes during generic or untyped object deserialization without a
    mandatory deny-by-default class allow-list.
    
    Chronicle Wire resolves externally supplied YAML type tags through its
    configured ClassLookup. When the default permissive lookup permits the
    requested type, the resulting class can propagate into generic object
    deserialization and reach ObjectUtils.newInstance().
    
    The security boundary becomes particularly important when an application
    performs operations such as:
    
    TextWire.from(untrustedYaml).readObject();
    
    or equivalent untyped deserialization.
    
    In this situation, the application is not exclusively determining the Java
    class being constructed. The serialized YAML participates in that decision.
    
    A restrictive ClassLookup can prevent arbitrary class resolution, but such
    a restriction is not inherent to the demonstrated default deserialization
    path.
    Proof of Concept Results
    
    The security test suite successfully reproduced multiple independent paths
    in which serialized type information caused Chronicle Wire to instantiate
    classes selected through the supplied Wire/YAML data.
    
    The tests completed successfully with no failures or errors:
    
    [INFO] Running net.openhft.chronicle.wire.SecurityAdditionalPoCTest
    
    [INFO] Tests run: 13, Failures: 0, Errors: 0, Skipped: 0
    
    [INFO] BUILD SUCCESS
    
    Direct Tagged Class Instantiation
    
    The PoC confirmed that Chronicle Wire resolves a supplied YAML type tag and
    instantiates the corresponding Java class during deserialization.
    
    Observed result:
    
    WireObjectInput.readObject instantiated tagged class:
    net.openhft.chronicle.wire.SecurityAdditionalPoCTest$AdditionalTypedPathProbe
    
    This confirms that the class encoded in serialized input is not merely
    parsed as metadata. The resolved type reaches object construction and
    results in an instance of the tagged class.
    Map Value Type Instantiation
    
    The same externally controlled type-selection behavior was reproduced while
    deserializing a typed value contained within a map.
    
    Observed result:
    
    Map value read instantiated tagged class:
    net.openhft.chronicle.wire.SecurityAdditionalPoCTest$AdditionalTypedPathProbe
    
    This demonstrates that the behavior is not limited to a single top-level
    readObject() operation. Typed serialized values encountered within other
    object-reading paths can also cause tagged classes to be instantiated.
    File-Based Typed Deserialization
    
    The PoC additionally confirmed arbitrary class instantiation when typed
    serialized data is loaded from a caller-controlled file.
    
    The test file contained:
    
    !net.openhft.chronicle.wire.SecurityAdditionalPoCTest$FilePathProbe {
        marker: from-file
    }
    
    The test output confirmed the exact payload written to the file:
    
    WireType.fromFile payload written BEGIN
    !net.openhft.chronicle.wire.SecurityAdditionalPoCTest$FilePathProbe {
    marker: from-file }
    WireType.fromFile payload written END
    
    Chronicle Wire subsequently instantiated the class identified by the
    serialized type tag:
    
    WireType.fromFile instantiated tagged class:
    net.openhft.chronicle.wire.SecurityAdditionalPoCTest$FilePathProbe
    
    This provides an additional concrete deserialization path where serialized
    type information determines the Java class instantiated by Chronicle Wire.
    Stream-Based File Deserialization
    
    The same behavior was confirmed through the file-stream deserialization
    path.
    
    Observed result:
    
    WireType.streamFromFile instantiated tagged class:
    net.openhft.chronicle.wire.SecurityAdditionalPoCTest$AdditionalTypedPathProbe
    
    This demonstrates that externally supplied type information can reach class
    instantiation through more than one Chronicle Wire input API.
    Constructor and readMarshallable() Execution
    
    The strongest lifecycle evidence was produced through a MethodReader typed
    argument.
    
    The serialized argument selected the following class:
    
    net.openhft.chronicle.wire.SecurityAdditionalPoCTest$MethodReaderProbe
    
    The test recorded both constructor and deserialization callback invocation:
    
    MethodReader typed argument instantiated class:
    net.openhft.chronicle.wire.SecurityAdditionalPoCTest$MethodReaderProbe
    
    MethodReader typed argument constructor calls: 1
    MethodReader typed argument readMarshallable calls: 1
    
    This confirms that externally selected type information can result in more
    than creation of an inert Java object.
    
    For the selected class, Chronicle Wire caused:
    
       -
    
       the class to be resolved;
       -
    
       an instance to be constructed;
       -
    
       the constructor to execute; and
       -
    
       the class-specific readMarshallable() callback to execute.
    
    The observed invocation counts were:
    
    Constructor calls:        1
    readMarshallable calls:   1
    
    This provides direct evidence that class-specific executable lifecycle
    behavior is reached as a consequence of serialized type selection.
    Consolidated PoC Evidence
    
    The test results demonstrate arbitrary class instantiation through several
    Chronicle Wire deserialization paths:
    
       -
    
       WireObjectInput.readObject() instantiated an externally tagged class.
       -
    
       Map value deserialization instantiated an externally tagged class.
       -
    
       WireType.fromFile() instantiated the class identified by a YAML type tag
       contained in a caller-controlled file.
       -
    
       WireType.streamFromFile() instantiated an externally tagged class.
       -
    
       MethodReader typed argument deserialization instantiated an externally
       selected class.
       -
    
       Constructor execution was directly observed.
       -
    
       readMarshallable() execution was directly observed.
       -
    
       All 13 security tests completed without failure or error.
    
    The most significant lifecycle result was:
    
    MethodReader typed argument instantiated class:
    net.openhft.chronicle.wire.SecurityAdditionalPoCTest$MethodReaderProbe
    
    MethodReader typed argument constructor calls: 1
    MethodReader typed argument readMarshallable calls: 1
    
    Combined with the direct readObject(), map-value, fromFile(), and
    streamFromFile() results, the PoC demonstrates that externally supplied
    type information can influence Java class selection and cause the selected
    class to be instantiated across multiple Chronicle Wire deserialization
    surfaces.
    
    The PoC does not rely solely on inspecting the source code or confirming
    that a class name was successfully resolved. It observes actual object
    construction and class-specific deserialization callback execution at
    runtime.
    PoC Conclusion
    
    The test suite confirms the core security primitive described by this
    finding: *serialized type information can control which Java class
    Chronicle Wire instantiates during affected deserialization operations*.
    
    The runtime evidence confirms both object construction and execution of
    class-specific deserialization lifecycle behavior:
    
    [CONFIRMED] Externally supplied type selected Java class
    [CONFIRMED] Selected class instantiated
    [CONFIRMED] Constructor executed
    [CONFIRMED] readMarshallable() executed
    [CONFIRMED] Typed class instantiated through readObject()
    [CONFIRMED] Typed class instantiated through map value deserialization
    [CONFIRMED] Typed class instantiated through WireType.fromFile()
    [CONFIRMED] Typed class instantiated through WireType.streamFromFile()
    [CONFIRMED] 13 security tests completed with 0 failures and 0 errors
    
    These results establish the *arbitrary class selection and instantiation
    primitive*. The ultimate security impact remains dependent on the classes
    available on the target application's classpath and the trust boundary
    through which serialized input reaches Chronicle Wire.
    
    Ron Edgerson
    Vulnerability Researcher & Exploit Developer
    
    CVE Research | Binary Exploitation | Application & Systems Security
    Responsible Disclosure • Proof-of-Concept Development
    
    🌐 https://github.com/ob1sec
    🔗 https://www.linkedin.com/in/ronedgerson1
    <https://linkedin.com/in/yourhandle>

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation