Impact
A malicious user with enough system knowledge can take control of another server on the same node by utilizing improper permissions checking in the websocket connection for the daemon.
Limitations of Scope
- Malicious user must have an account on the Panel and have access to at least one server on the same node as the target.
- User must know the full UUID of their target server.
- Target server must have received a valid websocket connection from an authorized account at least once since the last Daemon restart.
This bug is not exploitable by unauthenticated users, or users whom do not have access to a server on the same node as their target.
Source
When a new websocket request is initiated with the Daemon, a remote endpoint is hit by the Daemon requesting validation of the token passed. If the token is valid at a system level, the Daemon then moves to check if the server the token belongs to is the same as the server whose websocket connection is being requested.
Initially, if they do not match, an error is returned and nothing else will happen. However, upon successful connection the key is stored in the cache to avoid excessive HTTP calls from the Daemon. This is where the bug comes into play.
Once a valid user connects to a server's websocket, the key is cached in the Daemon. A malicious user can then connect using their own key from a server they control. This key will validate successfully since it does belong to a server, however the subsequent permission checking calls do not validate that the server being accessed is the same server that the key belongs to.
This stems from the cache behavior where the cached key would skip the server UUID checking, and proceed straight through to permission checking.
Patch
The patch for this particular bug involved moving the UUID checking logic out of the key checking logic, and always checking that the server assigned to the key is the same as the server we are checking permissions on.
diff --git a/src/controllers/server.js b/src/controllers/server.js
index 9d125ee..8a47636 100644
--- a/src/controllers/server.js
+++ b/src/controllers/server.js
@@ -140,13 +140,9 @@ class Server extends EventEmitter {
hasPermission(perm, token, next) {
const tokenData = Cache.get(`auth:token:${token}`);
if (_.isNull(tokenData)) {
- this.validateToken(token, (err, data) => {
+ this.getTokenData(token, (err, data) => {
if (err) return next(err);
- if (_.get(data, 'server') !== this.uuid) {
- return next(null, false, 'uuidDoesNotMatch');
- }
-
Cache.put(`auth:token:${token}`, data, data.expires_at);
return this.validatePermission(data, perm, next);
@@ -157,16 +153,21 @@ class Server extends EventEmitter {
}
validatePermission(data, permission, next) {
+ if (_.get(data, 'server') !== this.uuid) {
+ return next(null, false, 'uuidDoesNotMatch');
+ }
+
if (!_.isUndefined(permission)) {
if (_.includes(data.permissions, permission) || _.includes(data.permissions, 's:*')) {
// Check Suspension Status
return next(null, !this.isSuspended(), 'isSuspended');
}
}
+
return next(null, false, 'isUndefined');
}
- validateToken(token, next) {
+ getTokenData(token, next) {
Request.get({
url: `${Config.get('remote.base')}/api/remote/authenticate/${token}`,
headers: {
Workarounds
No known workarounds exist, the only way to protect against this vulnerability is to update your daemon.
Impact
A malicious user with enough system knowledge can take control of another server on the same node by utilizing improper permissions checking in the websocket connection for the daemon.
Limitations of Scope
This bug is not exploitable by unauthenticated users, or users whom do not have access to a server on the same node as their target.
Source
When a new websocket request is initiated with the Daemon, a remote endpoint is hit by the Daemon requesting validation of the token passed. If the token is valid at a system level, the Daemon then moves to check if the server the token belongs to is the same as the server whose websocket connection is being requested.
Initially, if they do not match, an error is returned and nothing else will happen. However, upon successful connection the key is stored in the cache to avoid excessive HTTP calls from the Daemon. This is where the bug comes into play.
Once a valid user connects to a server's websocket, the key is cached in the Daemon. A malicious user can then connect using their own key from a server they control. This key will validate successfully since it does belong to a server, however the subsequent permission checking calls do not validate that the server being accessed is the same server that the key belongs to.
This stems from the cache behavior where the cached key would skip the server UUID checking, and proceed straight through to permission checking.
Patch
The patch for this particular bug involved moving the UUID checking logic out of the key checking logic, and always checking that the server assigned to the key is the same as the server we are checking permissions on.
Workarounds
No known workarounds exist, the only way to protect against this vulnerability is to update your daemon.