Summary
In VRCX, these two vulnerabilities exist, which results in the remote command execution when combined:
- Cross-site scripting via overlay notification
- CefSharp browser with over-permission
Mitigation
These vulnerabilities are patched on VRCX 2024.03.23. In addition to the patch, we worked with the VRC team and blocked the older version of VRCX on the VRC's API side.
Users who use the older version of VRCX must update their installation to continue using VRCX.
Technical details
1. Cross-site scripting via overlay notification
In VRCX, there is a feature called Overlay Notification
.
This feature lets the user see the VRCX notification while using the HMD.
The following code is used to display the overlay notification:
html/src/vr.js
line 495-658
$app.methods.playNoty = function (json) {
[...]
switch (noty.type) {
case 'OnPlayerJoined':
text = `<strong>${noty.displayName}</strong> has joined`;
break;
case 'OnPlayerLeft':
text = `<strong>${noty.displayName}</strong> has left`;
break;
case 'OnPlayerJoining':
text = `<strong>${noty.displayName}</strong> is joining`;
break;
case 'GPS':
text = `<strong>${
noty.displayName
}</strong> is in ${this.displayLocation(
noty.location,
escapeTag(noty.worldName),
escapeTag(noty.groupName)
)}`;
break;
[...]
}
if (text) {
new Noty({
type: 'alert',
theme: this.config.notificationTheme,
timeout: this.config.notificationTimeout,
layout: this.config.notificationPosition,
text: `${img}<div class="noty-text">${text}</div>`
}).show();
}
};
Most user-controllable variables are escaped correctly, and cannot be used to achieve cross-site scripting.
Also, the user's display name cannot contain special characters such as <
or >
, so it'd be safe usually.
However, VRChat sometimes produces hard-to-parse logs, which allows a malicious user to spoof the display name, bypassing VRChat's restriction.
2. CefSharp browser with over-permission
To execute tasks that require native methods from the CefSharp browser, VRCX exposes the AppApi object.
This object contains some strong methods that allow arbitrary command/code execution.
One of these methods is StartGameFromPath
, which accepts the executable path and arguments to be controlled.
Dotnet/AppApi/GameHandler.cs
line 114-130
public bool StartGameFromPath(string path, string arguments)
{
if (!path.EndsWith(".exe"))
path = Path.Combine(path, "launch.exe");
if (!File.Exists(path))
return false;
Process.Start(new ProcessStartInfo
{
WorkingDirectory = Path.GetDirectoryName(path),
FileName = path,
UseShellExecute = false,
Arguments = arguments
})?.Close();
return true;
}
While it's impossible to exploit it independently, using the cross-site scripting mentioned above, it's possible to invoke this function with JavaScript like the following:
(async () => {
await CefSharp.BindObjectAsync('AppApi');
AppApi.StartGameFromPath('C:/Windows/System32/calc.exe', '');
})();
Summary
In VRCX, these two vulnerabilities exist, which results in the remote command execution when combined:
Mitigation
These vulnerabilities are patched on VRCX 2024.03.23. In addition to the patch, we worked with the VRC team and blocked the older version of VRCX on the VRC's API side.
Users who use the older version of VRCX must update their installation to continue using VRCX.
Technical details
1. Cross-site scripting via overlay notification
In VRCX, there is a feature called
Overlay Notification
.This feature lets the user see the VRCX notification while using the HMD.
The following code is used to display the overlay notification:
html/src/vr.js
line 495-658Most user-controllable variables are escaped correctly, and cannot be used to achieve cross-site scripting.
Also, the user's display name cannot contain special characters such as
<
or>
, so it'd be safe usually.However, VRChat sometimes produces hard-to-parse logs, which allows a malicious user to spoof the display name, bypassing VRChat's restriction.
2. CefSharp browser with over-permission
To execute tasks that require native methods from the CefSharp browser, VRCX exposes the AppApi object.
This object contains some strong methods that allow arbitrary command/code execution.
One of these methods is
StartGameFromPath
, which accepts the executable path and arguments to be controlled.Dotnet/AppApi/GameHandler.cs
line 114-130While it's impossible to exploit it independently, using the cross-site scripting mentioned above, it's possible to invoke this function with JavaScript like the following: