Talos Vulnerability Report

TALOS-2025-2154

MedDream PACS Premium setup incorrect default permissions vulnerability

July 28, 2025
CVE Number

CVE-2025-26469

SUMMARY

An incorrect default permissions vulnerability exists in the CServerSettings::SetRegistryValues functionality of MedDream PACS Premium 7.3.3.840. A specially crafted application can decrypt credentials stored in a configuration-related registry key. An attacker can execute a malicious script or application to exploit this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

MedDream PACS Premium 7.3.3.840

PRODUCT URLS

MedDream PACS Premium - https://meddream.com/products/meddream-pacs-server/

CVSSv3 SCORE

9.3 - CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

CWE

CWE-732 - Incorrect Permission Assignment for Critical Resource

DETAILS

MedDream PACS is a DICOM 3.0-compliant server for storing, managing, and retrieving medical images. It includes a web-based DICOM viewer and administration interface, with features like user access control, study forwarding, and multi-format image support.

Inadequate permissions on the registry values enable anyone with login access to read the encrypted data and subsequently recover the plaintext username and password, granting full access to the database. As a result, the host, database name, port number, username, and password become accessible from a hacker’s perspective.
It is worth mentioning that the problem with accessing credentials is even more significant when the database is installed on the same system as the MedDream server. In such cases, the credentials are stored in plaintext in one of the files.

Below part of pseudo code responsible for handling registry values to construct some object i named CServerSettings. LINE 44 encryption key is stored hardcoded from the binary itself and is used to encrypt/decrypt data from registry LINE 68 for database username and LINE 93 for corresponding password.

LINE1    void __thiscall CServerSettings::SetRegistryValues(CServerSettings *this)
LINE2       {
LINE3         undefined *puVar1;
LINE4         BYTE *pBVar2;
LINE5         BYTE *lpData;
LINE6         BYTE *lpData_00;
LINE7         undefined1 *lpData_01;
LINE8         LSTATUS LVar3;
LINE9         int iVar4;
LINE10        uint uVar5;
LINE11        longlong lVar6;
LINE12        ulonglong l_len_passkey;
LINE13        undefined stack [32];
LINE14        DWORD encrypted_buffer [2];
LINE15        DWORD l_buffer;
LINE16        undefined4 uStack_2ac;
LINE17        undefined8 local_2a8;
LINE18        SymmetricCipherFinal local_2a0;
LINE19        char cipher_key [40];
LINE20        BYTE l_encrypted_data [256];
LINE21        ulonglong cookie;
LINE22        INT64 uVar6;
LINE23  
LINE24       [...]
LINE25        this->SessionTimeout = encrypted_buffer[0];
LINE26        _l_buffer = (char *)CONCAT44(uStack_2ac,0x100);
LINE27        lpData_01 = &this->DatabaseHost;
LINE28        LVar3 = RegQueryValueExA(this->hkey,"DatabaseHost",(LPDWORD)0x0,(LPDWORD)0x0,lpData_01,&l_buffer);
LINE29        if (LVar3 != 0) {
LINE30          strncpy(lpData_01,"localhost",0x100);
LINE31          iVar4 = lstrlenA(lpData_01);
LINE32          RegSetValueExA(this->hkey,"DatabaseHost",0,1,lpData_01,iVar4 + 1);
LINE33        }
LINE34        _l_buffer = (char *)CONCAT44(uStack_2ac,0x20);
LINE35        puVar1 = &this->field_0x278;
LINE36        LVar3 = RegQueryValueExA(this->hkey,"Database",(LPDWORD)0x0,(LPDWORD)0x0,puVar1,&l_buffer);
LINE37        if (LVar3 != 0) {
LINE38          strncpy(puVar1,"Database",32);
LINE39          iVar4 = lstrlenA(puVar1);
LINE40          RegSetValueExA(this->hkey,"Database",0,1,puVar1,iVar4 + 1);
LINE41        }
LINE42        encrypted_buffer[0] = 0x100;
LINE43        memset(l_encrypted_data,0,256);
LINE44        builtin_strncpy(cipher_key,"XiaohuiLi",10);
LINE45        uVar6 = -1;
LINE46        do {
LINE47          l_len_passkey = uVar6 + 1;
LINE48          lVar6 = uVar6 + 1;
LINE49          uVar6 = l_len_passkey;
LINE50        } while (cipher_key[lVar6] != '\0');
LINE51        LVar3 = RegQueryValueExA(this->hkey,"MySqlUser",(LPDWORD)0x0,(LPDWORD)0x0,l_encrypted_data,
LINE52                                 encrypted_buffer);
LINE53        if (LVar3 == 0) {
LINE54          FUN_140001b80(&local_2a0);
LINE55          FUN_140001b80(&local_2a0.StreamInformation_vftable);
LINE56          _l_buffer = local_2a0.buffer;
LINE57          local_2a0.size_buffer = 256;
LINE58          local_2a0.size_below_0x101 = 1;
LINE59          local_2a0.ptr_buffer = local_2a0.buffer;
LINE60          local_2a0.SymmetricCipherFinal1_vtable = &CryptoPP::SymmetricCipherFinal<>::vftable;
LINE61          local_2a0.StreamInformation_vftable = &CryptoPP::SymmetricCipherFinal<>::vftable;
LINE62          local_2a0.SymmetricCipherFinal2_vtable = &CryptoPP::SymmetricCipherFinal<>::vftable;
LINE63          somecrypto((SymmetricCipherFinal *)&local_2a0.SymmetricCipherFinal2_vtable,cipher_key,
LINE64                     l_len_passkey & 0xffffffff,&PTR_vftable_14030e4c8);
LINE65          (*(code *)(local_2a0.StreamInformation_vftable)->140001f20_StreamTransform)
LINE66                    (&local_2a0.StreamInformation_vftable,l_encrypted_data,l_encrypted_data,
LINE67                     encrypted_buffer[0]);
LINE68          strncpy(&this->encrypted_mysqluser,(char *)l_encrypted_data,32);
LINE69          clear_buffer(&local_2a0);
LINE70        }
LINE71        else {
LINE72          strncpy(&this->encrypted_mysqluser,"dicom",32);
LINE73        }
LINE74        encrypted_buffer[0] = 0x100;
LINE75        memset(l_encrypted_data,0,256);
LINE76        LVar3 = RegQueryValueExA(this->hkey,"MySqlPassword",(LPDWORD)0x0,(LPDWORD)0x0,l_encrypted_data,
LINE77                                 encrypted_buffer);
LINE78        if (LVar3 == 0) {
LINE79          FUN_140001b80(&local_2a0);
LINE80          FUN_140001b80(&local_2a0.StreamInformation_vftable);
LINE81          _l_buffer = local_2a0.buffer;
LINE82          local_2a0.size_buffer = 0x100;
LINE83          local_2a0.size_below_0x101 = 1;
LINE84          local_2a0.ptr_buffer = local_2a0.buffer;
LINE85          local_2a0.SymmetricCipherFinal1_vtable = &CryptoPP::SymmetricCipherFinal<>::vftable;
LINE86          local_2a0.StreamInformation_vftable = &CryptoPP::SymmetricCipherFinal<>::vftable;
LINE87          local_2a0.SymmetricCipherFinal2_vtable = &CryptoPP::SymmetricCipherFinal<>::vftable;
LINE88          somecrypto((SymmetricCipherFinal *)&local_2a0.SymmetricCipherFinal2_vtable,cipher_key,
LINE89                     l_len_passkey & 0xffffffff,&PTR_vftable_14030e4c8);
LINE90          (*(code *)(local_2a0.StreamInformation_vftable)->140001f20_StreamTransform)
LINE91                    (&local_2a0.StreamInformation_vftable,l_encrypted_data,l_encrypted_data,
LINE92                     encrypted_buffer[0]);
LINE93          strncpy(&this->encryped_mysql_password,(char *)l_encrypted_data,32);
LINE94          clear_buffer(&local_2a0);
LINE95        }
LINE96        else {
LINE97          strncpy(&this->encryped_mysql_password,"",32);
LINE98        }
LINE99         [...]
LINE100           }
LINE101           __security_check_cookie(cookie ^ (ulonglong)stack);
LINE102           return;
LINE103         }
LINE104 

It is important to highlight that choosing the RC4 algorithm these days is not the best choice due to its vulnerability to a variety of attacks.

Incorrect permissions on registry keys and values allow anyone with access to the system to read the encrypted data and decrypt it using the RC4 algorithm with a hardcoded cipher key. This results in full access to the database associated with the registry records, local or remote.

TIMELINE

2025-03-04 - Initial Vendor Contact
2025-03-04 - Vendor Disclosure
2025-07-28 - Vendor Patch Release
2025-07-28 - Public Release

Credit

Discovered by Emmanuel Tacheau of Cisco Talos.