0% found this document useful (0 votes)
17 views10 pages

AdaptAuthoringToolNosqli

The blog post discusses two vulnerabilities in Adapt Authoring Tool 0.11.3: Incorrect Access Control (CVE-2024-50671) allowing authenticated users to access all registered email addresses, and NoSQL Injection (CVE-2024-50672) enabling unauthenticated attackers to reset passwords. These vulnerabilities can be exploited to perform remote code execution on the backend server by leveraging the ability to upload malicious plugins. The post details the technical aspects of the vulnerabilities and their potential impact on system security.

Uploaded by

Nguyen Kien
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views10 pages

AdaptAuthoringToolNosqli

The blog post discusses two vulnerabilities in Adapt Authoring Tool 0.11.3: Incorrect Access Control (CVE-2024-50671) allowing authenticated users to access all registered email addresses, and NoSQL Injection (CVE-2024-50672) enabling unauthenticated attackers to reset passwords. These vulnerabilities can be exploited to perform remote code execution on the backend server by leveraging the ability to upload malicious plugins. The post details the technical aspects of the vulnerabilities and their potential impact on system security.

Uploaded by

Nguyen Kien
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

4/23/25, 10:57 PM Adapt Authoring Tool 0.11.

3 - Authenticated Remote Code Execution | m0nk3y's Blog

CVE Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution

Summary
This blog post highlights two vulnerabilities discovered in Adapt Authoring Tool: Incorrect
Access Control (CVE-2024-50671) and NoSQL Injection (CVE-2024-50672). The incorrect access
control vulnerability allows attackers with Authenticated User roles to disclose all registered
email addresses, while the NoSQL injection vulnerability enables unauthenticated attackers to
reset user and administrator passwords. Additionally, users with administrative privileges can
upload custom plugins. Although this is an intended feature, it can be exploited in conjunction
with the aforementioned vulnerabilities, allowing attackers with Authenticated User roles to
perform remote code execution on the backend server.

Incorrect Access Control (CVE-2024-50671)


An incorrect access control vulnerability exists in the Get users feature, which is intended to
be used exclusively by users with Super Admin roles to retrieve information about all users
across every tenant, including their email addresses and roles.

adapt_authoring/lib/permissions.js

/**
* Checks if a resource uri pattern matcher matches a resource uri
*
* @param {string} pattern - resource matching pattern from a statement
* @param {target} target - the resource uri to match against
* @return {boolean} true if it pattern includes target, false
otherwise
*/

captures: function (pattern, target) {


var captured = false;
if ('string' !== typeof pattern || 'string' !== typeof target) {
return captured;
}
pattern = pattern.split('/');
target = target.split('/');

// will iterate over all tokens in pattern until a non-match is found

https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 1/10


4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

// if all tokens are matched either by an exact string or glob, te


result
// is truthy
captured = pattern.every(function (el, index, array) {
if (pattern[index] === '*' || pattern[index] === target[index]) {
return true;
}

return false;
});

return captured;
},

To authorize access to each endpoint, the application employs Role-Based Access Control
(RBAC), which specifies allowed actions for each user role through a predefined set of rules.
One key component of these rules is the resource matching pattern, which determines
whether a requested resource URI matches a specified pattern. The resource URI pattern-
matching logic, as shown in the code, checks for matches by splitting both the pattern and the
target resource URI into segments.

When a path segment in the resource matching pattern is * , the condition pattern[index]
=== '*' evaluates to true , causing the subsequent check, pattern[index] ===
target[index] , to be bypassed. However, the logic does not validate whether the number of
path segments in the pattern matches that of the target URI. As a result, patterns ending with
* inadvertently allow access to resource URIs with one fewer path segment, provided they
match the preceding pattern.

adapt-tenant-master> db.roles.find({ name: "Authenticated User" }, {


_id: 0, name: 1, statement: 1 });
[
{
name: 'Authenticated User',
statement: [
{
condition: {},
resource: 'urn:x-adapt:{{tenantid}}/api/user/*',
action: [ 'read' ],
effect: 'allow'
},
{
condition: {},
resource: 'urn:x-adapt:{{tenantid}}/api/user/me',

https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 2/10


4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

action: [ 'read', 'update' ],


effect: 'allow'
},
{
condition: {},
resource: 'urn:x-adapt:{{tenantid}}/api/output/*',
action: [ 'read' ],
effect: 'allow'
},
{
condition: {},
resource: 'urn:x-adapt:{{tenantid}}/api/shared/*',
action: [ 'read' ],
effect: 'allow'
},
{
condition: {},
resource: 'urn:x-adapt:{{tenantid}}/api/subscribed/*',
action: [ 'read' ],
effect: 'allow'
},
{
condition: {},
resource: 'urn:x-adapt:{{tenantid}}/api/autocomplete/*',
action: [ 'read' ],
effect: 'allow'
}
]
}
]

Since users with Authenticated User roles are allowed to perform the read action on
endpoints that match {{tenantid}}/api/user/* , attackers with this role can perform the
read action on {{tenantid}}/api/user to access the Get users feature and obtain all
registered email addresses.

NoSQL Injection (CVE-2024-50672)


A NoSQL injection vulnerability exists in the Reset password feature, where user input is used
as a query in Mongoose's find() function.

adapt_authoring/plugins/auth/local/index.js

https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 3/10


4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

LocalAuth.prototype.resetPassword = function (req, res, next) {


var resetPasswordToken = req.body.token;
var self = this;

if (!resetPasswordToken) {
return next(new auth.errors.UserResetPasswordError('Token was not
found'));
}
usermanager.retrieveUserPasswordReset({ token: resetPasswordToken },
function (error, usrReset) {
if (error) {
logger.log('error', error);
return res.status(500).end();
}
if (!usrReset) {
return res.status(200).json({});
}
self.internalResetPassword({ id: usrReset.user, password:
req.body.password }, function (error, user) {
if (error) {
logger.log('error', error);
return res.status(500).end();
}
res.status(200).json(user);
});
});
};

adapt_authoring/lib/usermanager.js

/**
* Retrieves a single user password reset
*
* @param {object} search - fields to match: should use 'token' which
is unique
* @param {function} callback - function of the form function (error,
userReset)
*/
retrieveUserPasswordReset: function (search, callback) {
var timestampMinAge = this.xHoursAgo(MAX_TOKEN_AGE);
database.getDatabase(function(err, db) {
db.retrieve('UserPasswordReset', search, function (error, results)
{
if (error) {
return callback(error);
https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 4/10
4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

}
if (!results || results.length === 0) {
return callback();
}
if (results.length > 1) {
return callback(new Error('User password reset search expected
a single result but returned ' + results.length + ' results'));
}
var resetData = results[0];
if (resetData.issueDate.getTime() < timestampMinAge) {
return callback(new Error('Reset token has expired'));
}
callback(null, resetData);
});
}, configuration.getConfig('dbName'));
},

adapt_authoring/lib/dml/mongoose/index.js

/**
* Implements Database.retrieve
*
* @param {string} objectType - the type of object to find, e.g. 'user'
* @param {object} search - fields to search on
* @param {object} [options] -
* @param {function} callback - of the form function (error, results)
...
*/
MongooseDB.prototype.retrieve = function(objectType, search, options,
callback) {
[...]
if (Model = this.getModel(objectType)) {
var query = {};

if (distinct) {
query = Model.distinct(distinct, search);
} else if (elemMatch) {
query = Model.find(null).elemMatch(elemMatch.collection,
elemMatch.query);
} else {
query = Model.find(search, fields);
}
[...]
if (!jsonOnly) {
query.exec(callback);

https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 5/10


4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

} else {
query.lean().exec(callback);
}
} else {
callback(new Error('MongooseDB#retrieve: Failed to retrieve model
with name ' + objectType));
}
};

The sanitizeFilter option is not explicitly set when using the find() function, causing it
to default to false . In this case, Mongoose does not sanitize the query filters against query
selector injection attacks.

sanitizeFilter : false by default. Set to true to enable the sanitization of the query
filters against query selector injection attacks by wrapping any nested objects that have a
property whose name starts with $ in a $eq .

Mongoose - API - Mongoose

Due to the lack of input validation and Mongoose's default behavior, unauthenticated
attackers can perform NoSQL injection to reset user and administrator account passwords.

Remote Code Execution


The Adapt framework uses Grunt, a JavaScript task runner, to automate various development
and deployment tasks. When an administrator previews a course, it triggers the server-
build task, which acts an alias for several other tasks. These include
scripts:adaptpostcopy and scripts:adaptpostbuild , which invoke the scripts task
with adaptpostcopy and adaptpostbuild as arguments.

adapt_framework/grunt/tasks/server-build.js

module.exports = function(grunt) {
grunt.registerTask('server-build', 'Builds the course without JSON
[used by the authoring tool]', function(mode) {
const requireMode = (mode === 'dev') ? 'dev' : 'compile';

grunt.task.run([
'_log-vars',
'build-config',
'copy',

https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 6/10


4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

'scripts:adaptpostcopy',
'schema-defaults',
'language-data-manifests',
'less:' + requireMode,
'handlebars',
'javascript:' + requireMode,
'replace',
'scripts:adaptpostbuild',
'clean:temp'
]);
});
};

The scripts task iterates through all installed plugins to check if the scripts.${mode}
property exists in the plugin's bower.json file. In this context, mode refers to the provided
argument—either adaptpostcopy or adaptpostbuild . If the property exists, its value,
which specifies the path to the compile-time script, is retrieved, and the corresponding
function within the script is executed.

adapt_framework/grunt/tasks/scripts.js

grunt.registerTask('scripts', 'Run plugin build tasks', function(mode)


{
[...]
if (options.plugins) {

const paths = options.plugins.reduce((paths, src) => {


paths.push(...grunt.file.expand({
filter: options.pluginsFilter
}, src));
return paths;
}, []);

async.each(paths, function(bowerJSONPath, done) {

if (bowerJSONPath === undefined) return done();


const bowerJSON = grunt.file.readJSON(bowerJSONPath);

if (!bowerJSON.scripts) return done();

const plugindir = path.dirname(bowerJSONPath);


const script = bowerJSON.scripts[mode];

if (!script) return done();

https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 7/10


4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

try {
const buildModule = require(path.join(process.cwd(), plugindir,
script));
buildModule(fs, path, grunt.log.writeln, {
...buildConfig,
...options,
plugindir
}, done);
} catch (err) {
grunt.log.writeln(chalk.red(err));
done();
}

}, taskCallback);

}
});

Users with Super Admin roles can upload custom plugins using the Upload plugin feature.
However, the system restricts most plugins from executing compile-time scripts. The
pluginsFilter() function ensures that only whitelisted plugins— adapt-contrib-xapi
and adapt-contrib-spoor by default—are allowed to execute these scripts.

adapt_framework/grunt/config/scripts.js

module.exports = function(grunt, options) {


return {
options: {
outputdir: '<%= outputdir %>',
sourcedir: '<%= sourcedir %>',
plugins: [
'<%= sourcedir %>components/*/bower.json',
'<%= sourcedir %>extensions/*/bower.json',
'<%= sourcedir %>menu/<%= menu %>/bower.json',
'<%= sourcedir %>theme/<%= theme %>/bower.json'
],
pluginsFilter: function(filepath) {
return grunt.config('helpers').includedFilter(filepath) &&
grunt.config('helpers').scriptSafeFilter(filepath);
}
}
};
};

https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 8/10


4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

adapt_framework/grunt/helpers.js

const exports = {
[...]
scriptSafe: [
'adapt-contrib-xapi',
'adapt-contrib-spoor'
]
},
[...]
isPluginScriptSafe: function(pluginPath) {

pluginPath = pluginPath.replace(convertSlashes, '/');


const includes = grunt.config('scriptSafe');
const isExplicitlyDefined = (includes &&
pluginPath.search(getScriptSafeRegExp()) !== -1);
const isIncluded = grunt.option('allowscripts') || includes[0] ===
'*' || isExplicitlyDefined;

return isIncluded;

},
[...]
scriptSafeFilter: function(filepath) {
return exports.isPluginScriptSafe(filepath);
},

Attackers can exploit this functionality by creating a malicious plugin that:

Is named adapt-contrib-xapi or adapt-contrib-spoor .


Contains a bower.json file with a scripts.adaptpostcopy or
scripts.adaptpostbuild property, where the value specifies the path to the compile-
time script.
Embeds an exploit within the compile-time script.

If a plugin with the name adapt-contrib-xapi or adapt-contrib-spoor already exists, it


must be removed before uploading the malicious version. By utilizing the crafted plugin,
attackers with Super Admin roles can perform remote code execution on the backend server.

Proof-of-Concept Exploit
Adapt Authoring Tool 0.11.3 - Remote Command Execution (RCE) | Exploit Database
https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 9/10
4/23/25, 10:57 PM Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution | m0nk3y's Blog

https://pages.dos-m0nk3y.com/blog/cve/Adapt Authoring Tool 0.11.3 - Authenticated Remote Code Execution 10/10

You might also like