Team82 Logo Claroty

Team82 Research

Synology NAS DSM Account Takeover: When Random is not Secure

Sharon Brizinov
/ October 17th, 2023

Executive Summary 

  • Team82 has discovered the use of a weak random number generator in Synology’s DiskStation Manager (DSM) Linux-based operating system running on the company’s network-attached storage (NAS) products

  • The insecure Math.random() method was used to generate the password of the admin password for the NAS device itself. 

  • Under some rare conditions, an attacker could leak enough information to restore the seed of the pseudorandom number generator (PRNG), reconstruct the admin password, and remotely take over the admin account.

  • The vulnerability, tracked as CVE-2023-2729, has been addressed by Synology. Synology’s advisory is here

Table of Contents

  1. What is Synology DSM?

  2. Creating Our Setup

  3. Generating the Synology DSM Admin Password

  4. Example, Please

  5. Key Takeaways

What is Synology DSM?

Synology DiskStation Manager (DSM) is the operating system under the hood of every Synology network-attached storage (NAS) system. DSM supports a number of functions that help the user access, manage, and share their files remotely through a centralized platform.

The DSM OS firmware comes with two pre-built Linux users: admin and guest (disabled by default).

When we install the system, the installation wizard requires us to create a new administrator-level account which is different from the built-in “admin” user. While creating the new user, the setup wizard will generate a “random” password and disable the original “admin” user account.

We found a flaw in the algorithm that generates the admin password because the password is being generated on the browser client side using the Javascript Math.Random() function, which is not cryptographically secured.

Math.random() documentation
A note from the Math.random() documentation.

By leaking the output of a few Math.Random() generated numbers, we were able to reconstruct the seed for the PRNG and use it to brute-force the admin password. Finally we were able to use the password to login to the admin account (after enabling it). 

To execute the attack, some Math.Random values need to be leaked. One possible way to achieve this is by leaking some GUIDs (e.g. 72e14742-b0e5-4826-b7c9-eb16284fe9cd) that are also being generated using Math.Random at the first installation wizard, which means they are based on the same PRNG seed as the admin user-account password.

For example, we found that the following class name GUIDs are generated on first install and with the same Math.Random seed in the same “session” as the admin password is generated:

  1. SYNO.SDS.PkgManApp.Instance

  2. SYNO.SDS.AdminCenter.Application

  3. SYNO.SDS.App.FileStation3.Instance

  4. SYNO.SDS.HelpBrowser.Application

These GUIDs can be leaked and be used by attackers to reconstruct the seed of the PRNG of when the password was generated.

synology guide

But since the GUID is generated using Math.floor(16 * Math.Random()), additional sophistication is needed to restore the full state of the seed (see here).

In addition, since we know exactly how many Math.Random calls we have between one of the aforementioned GUIDs and the password generation, once we have the seed state we can fully simulate the advancement of the PRNG state until we reach the password generation algorithm. This will result in a full admin password extraction.

Creating Our Setup

We are starting with a factory reset to our device, so we could be faced with the installation wizard again after reboot.

Next, we are installing the DSM OS.

synology dsm welcome
synology DSM loading

After a short period of time, we are faced with the new setup wizard, asking us to choose a new administrator account.

synology NAS started

If we intercept the request, we will see that a JSON with specific user-related commands is sent to the SYNO.Core.User API:

  • Set (AKA update) the admin user with new password

  • Create the new account and add to the administrators

synology set admin

As part of this procedure, the admin password is “randomly” generated by the  WelcomeApp/WelcomApp.js ! password on the client side (browser). It is then sent to the NAS, and forgotten and cannot be restored.

Synology NAS DSM Account Takeover: When Random is not Secure

Generating the Synology DSM Admin Password

This function is fairly simple:

  • First it draws a number,  which will be the password length + 16

  • First character is a random digit

  • Second character is a random lower-case letter

  • Third character is a random uppercase letter

  • Forth is character is a special character

  • Next, it will draw the rest of the characters

  • And finally it will replace in place some characters, randomly too.


Here is a simplified version of the algorithm we wrote in Python:

Synology Password Generate

The key issue is that the algorithm relies on the insecure Math.Random() PRNG to generate a cryptographically random number. The PRNG is insecure because knowing the seed for Math.Random() allows us (and others) to restore the full state of the PRNG function and predict all past/future generated numbers.

Modern browsers use the XorShift128 algorithm as their underlying pseudo-random generator to draw numbers. This algorithm is deterministic given an initial state. Therefore, based on previous research on this topic (see here), we were able to write a simple python script that uses Z3 as a symbolic execution solver, which receives some constraints and brute-forces its way to the current XorShift128 state. From there, it’s easy to just run the algorithm forward or backward to get future or past values to feed the password generation function.

The inner implementation of the XorShift128 function in each browser is a bit different, for example in how the next state is calculated, how the conversion to double works, and how the values are kept (for example, Chrome uses a cache of 64 reverse values). In our PoC, we implemented support for Chrome, Firefox, and Safari (again, most of this work was based on this research and the implementation details from V8 dev blog).

Example, Please

First, for testing purposes, we modified the Math.Random Javascript function to log all the generated values. We did this by overriding the Math.Random JS function to first generate the next number, print it, and only then return the value. Here is our JS code:

realrandom = Math.random
Math.random = function(){ let a1 = realrandom();console.log(a1);return a1}

This way we could debug exactly what is going on. We ran the password generation function and got the following values:

  • 0.39728164765656415

  • 0.05808893736455878

  • 0.3076672729400183

  • 0.9305761671416779

  • 0.6532915866832478

  • 0.178642200654346

  • 0.4667003145378774

  • … (many more values) …

  • 0.623736669967158

  • 0.5234587422704924

  • 0.9892502895452078

  • 0.8339311695933369

  • 0.11401941881097533

  • 0.46301481070375705

  • 0.66201529035177


Next we added a debug print for the real admin password that is being generated. We found it to be: wH~\DYx*V|AYqwgM>|01Y|WIhH|V

Synology Console

Now we need to see if we can reconstruct it ourselves. We generated some more math.random values and used them as our testing point to reconstruct the seed for the PRNG:

  • 0.2894862350439742

  • 0.4728609250322471

  • 0.8945401711791927

  • 0.2303535523638034

  • 0.9793111259581007


We used them as input seeds to our script, which is heavily based on @cyberingcc XorShift128Plus work, and generated all the past/future potential passwords. 

python3 xs128p_calc_forward_backward.py chrome 200 "0.2894862350439742 0.4728609250322471 0.8945401711791927 0.2303535523638034 0.9793111259581007"

And indeed, it generated all the 200 past/future values and one of the generated values was the real password:

Synology 200

We enabled the admin user account and tried to login, and it worked!

Synology Admin

Obviously, in a real life scenario the attacker will first need to leak the aforementioned GUIDs, brute force the Math.Random state, and gain the admin password. Even after doing so, by default the builtin admin user account is disabled and most users won’t enable it (for a good reason!).

Key Takeaways

We found this vulnerability while preparing for Pwn2Own. It was a nice “aha” moment, but we quickly realized it would be almost impossible to exploit it in a real life situation. Anyway we felt it would be appropriate to write about this in order to stress the importance of using cryptographically secure Random when generating passwords.

Again, it’s important to remember that Math.random() does not provide cryptographically secure random numbers. Do not use them for anything related to security. Use the Web Crypto API instead, and more precisely the window.crypto.getRandomValues() method.

We disclosed CVE-2023-2729 to Synology, which changed the vulnerable algorithm and has pushed the fix to affected devices. DSM 7.2 is affected by the vulnerability and users are asked to upgrade to 7.2-64561 or above. 

Acknowledgement

We would like to thank Synology for fixing all reported vulnerabilities.

Stay in the know

Get the Team82 Newsletter

Related Vulnerability Disclosures

Math.random() documentation
synology guide
synology dsm welcome
synology DSM loading
synology NAS started
synology set admin
Synology NAS DSM Account Takeover: When Random is not Secure
Synology Password Generate
Synology Console
Synology 200
Synology Admin
Claroty
LinkedIn Twitter YouTube Facebook