GitHub Advisoryadvisory · Maven
GHSA-jj95-55cr-9597 Aerospike Java Client vulnerable to unsafe deserialization of server responses
CRITICAL
Official page ↗
- Aliases
- CVE-2023-36480
- Ecosystem
- Maven
- Packages
- com.aerospike:aerospike-client
- Severity
- Critical
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Weaknesses
- CWE-502
- Reviewed
- 2023-08-03
Affected packages
• Maven com.aerospike:aerospike-client: >= 6.0.0, < 6.2.0 → fixed in 6.2.0
• Maven com.aerospike:aerospike-client: >= 5.0.0, < 5.2.0 → fixed in 5.2.0
• Maven com.aerospike:aerospike-client: < 4.5.0 → fixed in 4.5.0
Description
GitHub Security Lab (GHSL) Vulnerability Report: `GHSL-2023-044`
The GitHub Security Lab team has identified a potential security vulnerability in Aerospike Java Client.
We are committed to working with you to help resolve this issue. In this report you will find everything you need to effectively coordinate a resolution of this issue with the GHSL team.
If at any point you have concerns or questions about this process, please do not hesitate to reach out to us at `[email protected]` (please include `GHSL-2023-044` as a reference).
If you are _NOT_ the correct point of contact for this report, please let us know!
Summary
The Aerospike Java client is a Java application that implements a network protocol to communicate with an Aerospike server. Some of the messages received from the server contain Java objects that the client deserializes when it encounters them without further validation. Attackers that manage to trick clients into communicating with a malicious server can include especially crafted objects in its responses that, once deserialized by the client, force it to execute arbitrary code. This can be abused to take control of the machine the client is running on.
Product
Aerospike Java Client
Tested Version
6.1.7
Details
Issue: Unsafe deserialization of server responses (`GHSL-2023-044`)
The Aerospike Java client implements different ways of communicating with an Aerospike server to perform several operations. Asynchronous commands can be executed using the Netty framework using the `NettyCommand` class. This class includes an `InboundHandler` that extends Netty's `ChannelInboundHandlerAdapter`, which handles inbound data coming from the Netty channel established with the server. This is implemented in the `channelRead` method:
`client/src/com/aerospike/client/async/NettyCommand.java:1157`
```java
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
command.read((ByteBuf)msg);
}
```
The incoming `msg` object is handled by the `NettyCommand.read` method, which behaves differently depending on the `state` variable. Several states produce paths to the vulnerable code — for instance, we will follow the path through `AsyncCommand.COMMAND_READ_HEADER`:
`/client/src/com/aerospike/client/async/NettyCommand.java:489`
```java
private void read(ByteBuf byteBuffer) {
eventReceived = true;
try {
switch (state) {
// --snip--
case AsyncCommand.COMMAND_READ_HEADER:
if (command.isSingle) {
readSingleHeader(byteBuffer);
}
// --snip--
}
// --snip--
}
// --snip---
}
```
Some bytes are read from the message buffer and saved in `command.dataBuffer` in the `readSingleHeader` method, after which `parseSingleBody` is called:
`client/src/com/aerospike/client/async/NettyCommand.java:596`
```java
private void readSingleHeader(ByteBuf byteBuffer) {
int readableBytes = byteBuffer.readableBytes();
int dataSize = command.dataOffset + readableBytes;
// --snip--
byteBuffer.readBytes(command.dataBuffer, 0, dataSize);
command.dataOffset = dataSize;
if (command.dataOffset >= receiveSize) {
parseSingleBody();
}
}
```
`parseSingleBody` simply delegates on `AsyncCommand.parseCommandResult`, which unless the message is compressed, directly calls `AsyncCommand.parseResult`. The implementation of this method depends on the command type. For an `AsyncRead` command, we have the following:
`client/src/com/aerospike/client/async/AsyncRead.java:68`
```java
@Override
protected final boolean parseResult() {
validateHeaderSize();
int resultCode = dataBuffer[dataOffset + 5] & 0xFF;
int generation = Buffer.bytesToInt(dataBuffer, dataOffset + 6);
int expiration = Buffer.bytesToInt(dataBuffer, dataOffset + 10);
int fieldCount = Buffer.bytesToShort(dataBuffer, dataOffset + 18);
int opCount = Buffer.bytesToShort(dataBuffer, dataOffset + 20);
dataOffset += Command.MSG_REMAINING_HEADER_SIZE;
if (resultCode == 0) {
// --snip--
skipKey(fieldCount);
record = parseRecord(opCount, generation, expiration, isOperation);
return true;
}
```
It can be seen that several fields are read from the message's bytes, and then a call to `Command.parseRecord` happens:
`client/src/com/aerospike/client/command/Command.java:2083`
```java
protected final Record parseRecord(
int opCount,
int generation,
int expiration,
boolean isOperation
) {
Map bins = new LinkedHashMap<>();
for (int i = 0 ; i < opCount; i++) {
int opSize = Buffer.bytesToInt(dataBuffer, dataOffset);
byte particleType = dataBuffer[dataOffset + 5];
byte nameSize = dataBuffer[dataOffset + 7];
String name = Buffer.utf8ToString(dataBuffer, dataOffset + 8, nameSize);
dataOffset += 4 + 4 + nameSize;
int particleBytesSize = opSize - (4 + nameSize);
Object value = Buffer.bytesToParticle(particleType, dataBuffer, dataOffset, particleBytesSize);
```
`Buffer.bytesToParticle` converts the remaining bytes in the data buffer depending on the `particleType` field. We're interested in the `JBLOB` case:
`client/src/com/aerospike/client/command/Buffer.java:53`
```java
public static Object bytesToParticle(int type, byte[] buf, int offset, int len)
throws AerospikeException {
switch (type) {
// --snip--
case ParticleType.JBLOB:
return Buffer.bytesToObject(buf, offset, len);
```
In `bytesToObject`, the deserialization of an object from the message bytes happens:
`client/src/com/aerospike/client/command/Buffer.java:300`
```java
public static Object bytesToObject(byte[] buf, int offset, int length) {
// --snip--
try (ByteArrayInputStream bastream = new ByteArrayInputStream(buf, offset, length)) {
try (ObjectInputStream oistream = new ObjectInputStream(bastream)) {
return oistream.readObject();
}
}
// --snip--
}
```
NOTE: Take into account that there exists a similar sink, that can be reached in a similar way, in `Unpacker.unpackBlock`:
`client/src/com/aerospike/client/util/Unpacker.java:227`
```java
private T unpackBlob(int count) throws IOException, ClassNotFoundException {
// --snip--
case ParticleType.JBLOB:
// --snip--
try (ByteArrayInputStream bastream = new ByteArrayInputStream(buffer, offset, count)) {
try (ObjectInputStream oistream = new…
Related
Alias · CVE (1)
- CVE-2023-36480 The Aerospike Java client is a Java application that implements a network protocol to communicate with an Aerospike server. vulnerability
Weakness · CWE (1)
- CWE-502 Deserialization of Untrusted Data weakness
References
- https://github.com/aerospike/aerospike-client-java/security/advisories/GHSA-jj95-55cr-9597 web
- https://nvd.nist.gov/vuln/detail/CVE-2023-36480 advisory
- https://github.com/aerospike/aerospike-client-java/commit/02bf28e62fb186f004c82c87b219db2fc5b8262a web
- https://github.com/aerospike/aerospike-client-java/commit/51c65e32837da29435161a2d9c09bbdc2071ecae web
- https://github.com/aerospike/aerospike-client-java/commit/66aafb4cd743cf53baffaeaf69b035f51d2e2e36 web
- https://github.com/aerospike/aerospike-client-java/commit/80c508cc5ecb0173ce92d7fab8cfab5e77bd9900 web
- https://github.com/aerospike/aerospike-client-java package
- https://github.com/aerospike/aerospike-client-java/blob/e40a49b3db0d2b3d45068910e1cb9d917c795315/client/src/com/aerospike/client/async/AsyncRead.java#L68 web
- https://github.com/aerospike/aerospike-client-java/blob/e40a49b3db0d2b3d45068910e1cb9d917c795315/client/src/com/aerospike/client/async/NettyCommand.java#L1157 web
- https://github.com/aerospike/aerospike-client-java/blob/e40a49b3db0d2b3d45068910e1cb9d917c795315/client/src/com/aerospike/client/async/NettyCommand.java#L489 web
- https://github.com/aerospike/aerospike-client-java/blob/e40a49b3db0d2b3d45068910e1cb9d917c795315/client/src/com/aerospike/client/async/NettyCommand.java#L596 web
- https://github.com/aerospike/aerospike-client-java/blob/e40a49b3db0d2b3d45068910e1cb9d917c795315/client/src/com/aerospike/client/command/Buffer.java#L53 web
- https://github.com/aerospike/aerospike-client-java/blob/e40a49b3db0d2b3d45068910e1cb9d917c795315/client/src/com/aerospike/client/command/Command.java#L2083 web
- https://github.com/aerospike/aerospike-client-java/blob/e40a49b3db0d2b3d45068910e1cb9d917c795315/client/src/com/aerospike/client/util/Unpacker.java#L227 web
- https://support.aerospike.com/s/article/CVE-2023-36480-Aerospike-Java-Client-vulnerable-to-unsafe-deserialization-of-server-responses web