Thirteen distinct vulnerabilities in Apache Dubbo related to insecure deserialization, and an excellent look at using CodeQL to assist manual vulnerability research and attack surface discovery. A lot of the interesting points in this post are more about the discovery of new attack surface rather than in the vulnerabilities themselves.
Bypass Hessian2 allowlist via alternative protocols - Deserialization issues were previously found, leading to the introduction of an allowlist to control allowable types that can be deserialized. The allow list protects the obvious routes to an RPC invocation object, but Dubbo supports other protocols and message types that can lead to deserialization. In particular the TelnetCodec which is used when the message doesn’t start with
0xADBB provided several requests and responses that could be sent and lead to deserialization.
Pre-auth RCE via Java native deserialization in the Generic filter - When using the GenericFilter, which is a default-installed filter extension it will use reflection to determine the user-specified service to invoke. This puts the attack in control of the RPC attachment which specifies how the arguments should be parsed,
nativejava being an option.
Pre-auth RCE via arbitrary bean manipulation in the Generic filter - Using the GenericFilter there are other options for deserialization and code execution beyond
raw.return will use the builtin
PojoUtils.realize() method. This reads the class to instantiate out of a dictionary with a
class key, the rest of the entries are used to set or invoke the setter for other fields. If there is an RCE setter gadget in the classpath than an attacker could obtain RCE through this. The
bean option can be used similarly but also allows for invoking the default constructors on the classes.
Pre-auth RCE via arbitrary bean manipulation in the Telnet handler - Using CodeQL the author finds other places the decoders used in the previously vulnerability are used. This results in a deserialization attack by causing the Telnet protocol to perform and RPC invocation.
RCEs via unsafe YAML unmarshalling - Moving away from the RPC invocation code, the author looks for other dangerous APIs being used and found some configuration classes were insecurely unmarshalling YAML. These enabled remote code execution should an attacker be able to modify one of the target configurations. While this might appear to be a larger ask, most of the configuration managers run without any authentication or authorization enabled.
RCE on customers via script route poisoning (Nashorn script injection) - Routing rules can provide a script in any of the scripting languages available in the JDK. An attacker with access to a non-authenticated registry can register a new scripted route, this route will be downloaded and evaluated on the consumers.
Pre-auth unsafe Java deserialization (bypass of checkSerialization control - This is the most interesting issue in my opinion. In
DecodeableRpcInvocation.decode there is a call to
CodecSupport.checkSerialization which enforces the type that gets deserialized. Both the
decode method and the
checkSerialization methods will attempt to look up the class/service path to obtain an object representing the service.
checkSerialization does this using
lookupExportedServiceWithoutGroup, and if it fails it leaves the function without throwing an exception. Later when
decode is using the service, it uses
lookupService. These two methods work slightly differently, the former will lookup an exported service using its path and its version, whereas the later simply uses the class path. This allows an attacker to craft a request that will get through the
checkSerialization method by including a non-existent version, but when used later will be found bypassing the check.