Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

[Fix] Patched RCE arising inside the "launchpad" library #1

Merged
merged 7 commits into from
Jul 3, 2020

Conversation

Mik317
Copy link

@Mik317 Mik317 commented Jun 26, 2020

📊 Metadata *

Please enter the direct URL for this bounty on huntr.dev. This is compulsory and will help us process your bounty submission quicker.

Bounty URL: https://www.huntr.dev/app/bounties/open/1-npm-launchpad

⚙️ Description *

The issue arised in multiple locations, so I to validate the type of data the functions were going to use (like the paths) and since there were some multi commands I didn't use the execFile function, which should have had stored many new variables because we wouldn't have been to concatenate and return results that would have been used in a second/third command correlated to the first one executed. In this case I simply made a functionality that deletes every quote from the variable, making impossible threat the variables concatenated as commands, but only as arguments of the specific command.

💻 Technical Description *

The fix has been applied in 3 different ways inside 3 different files, so I'll comment each one.

  1. The issue arises firstly here: https://github.com/bitovi/launchpad/blob/master/lib/local/instance.js#L12 because of the fact the name variable is concatenated inside the various commands without being sanitized. Since the name is inside some single-quotes it would have been useless split the 3 different commands inside 3 different execFile that would have used more resources to store the content of the singular commands that should be concatenated again ... instead I introduced the safe function which deletes the quotes from the name in order to make it to be only an argument not escapable from quotes.
var safe = function (str) {
   // Avoid quotes makes impossible escape the `multi command` scenario
   return str.replace(/['"]+/g, '');
}

Note I've used the execFile function later in this file in the following lines: https://github.com/Mik317/launchpad/blob/master/lib/local/instance.js#L104 and https://github.com/Mik317/launchpad/blob/master/lib/local/instance.js#L110, in order to avoid commands could be executed in a dangerous context.
2. The 2' issue arised inside the following line: https://github.com/bitovi/launchpad/blob/master/lib/local/version.js#L21
In this case it I used execFile in order to avoid concatenation of other strings containing dangerous characters.
Patched with:

    exec(command.split(' ')[0], command.split(' ').slice(1), function(error, stdout) {

In this case the first part of the command is taken as command to execute (surely a path since the command variable is made through

    var command = path.join('"' + __dirname, '..', '..', 'resources', 'ShowVer.exe" "' + browser.command + '"');

), while the second part are the arguments.

  1. The last issue arised here: https://github.com/bitovi/launchpad/blob/master/lib/local/version.js#L50
    In this case the browser path and filename weren't checked completely, and even if the execution of malicious code would have been possible only if the default browser of the victim has a badly crafted filename, I inserted a check to see if the path+filename pointing to the browser is a valid path.
    The issue has been fixed through this function:
// Validate paths supplied by the user in order to avoid "arbitrary command execution"
var validPath = function (filename){
  var filter = /[`!@#$%^&*()_+\-=\[\]{};':"\\|,<>\/?~]/;
  if (filter.test(filename)){
    console.log('\nInvalid characters inside the path to the browser\n');
    return
  }
  return filename;
}

🐛 Proof of Concept (PoC) *

  1. Download the JS library (launchpad)
  2. Go inside the path you downloaded it and make the following poc.js file:
// poc.js
var launchpad = require("./launchpad/lib/local/instance");
var tst = new launchpad.Instance('t', {}, {}, {process:"s'; touch HACKED; # "});
tst.getPid(function(){});
  1. Execute through node poc.js
  2. The HACKED file will we created
    Screenshot from 2020-06-26 19-34-24

🔥 Proof of Fix (PoF) *

  1. Download the fixed version
  2. Use the same POC previously indexed
  3. The HACKED file is NOT created anymore
    Screenshot from 2020-06-26 19-52-10

👍 User Acceptance Testing (UAT)

It doesn't introduce any error (at least using the module through the PoC I crafted)

Regards,
Mik

@@ -90,11 +97,11 @@ Instance.prototype.stop = function (callback) {
} catch (error) {}
} else {
if (this.options.command.indexOf('open') === 0) {
command = 'osascript -e \'tell application "' + self.options.process + '" to quit\'';
command = 'osascript -e \'tell application "' + safe(self.options.process) + '" to quit\'';
debug('Executing shutdown AppleScript', command);
exec(command);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the dangerous function exec() should be avoided.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @toufik-airane :),
I've replaced the exec function with the execFile one in order to avoid problems. I removed also the safe function in the windows case because I saw certain systems won't recognize the string inside as a argument (edge cases covered anyway by execFile now introduced).

Regards,
Mik

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you run the tests and update the description?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I updated the description of the fixes applied and I retested the new code.
I saw I had missed a ) ... however now it should be fixed well and running the POC I previously used it doesn't lead to RCE or errors due to malformed syntax.

Regards,
Mik

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. 😊

Copy link

@mufeedvh mufeedvh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good fix, just complete this one change request and it's all perfect! 👏🎉

huntr sheriff

@@ -18,7 +28,7 @@ module.exports = function(browser) {

debug('Retrieving version for windows executable', command);
// Can't use Q.nfcall here unfortunately because of non 0 exit code
exec(command, function(error, stdout) {
exec(command.split(' ')[0], command.split(' ').slice(1), function(error, stdout) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please change this exec() to execFile()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mik317 - once you have made the fix - we will get this merged - cheers! 🍰

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I just updated the fix as requested. In the meanwhile I also applied a minor fix because I had inserted certain characters as blacklisted that however could be part of a path (not only of a filename) :).

Regards,
Mik

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mik317 - awesome, thanks for making the changes!

@JamieSlome JamieSlome merged commit de5aca1 into 418sec:master Jul 3, 2020
@huntr-helper
Copy link

Congratulations Mik317 - your fix has been selected! 🎉

Thanks for being part of the community & helping secure the world's open source code.
If you have any questions, please respond in the comments section. Your bounty is on its way - keep hunting!

lucamilanesio pushed a commit to GerritCodeReview/gerrit that referenced this pull request Sep 1, 2021
 - polymer-cli is imported twice, once in package.json and once in
 tools/node_tools/package.json.  Only the latter is used by CI system.
 - Add patch 418sec/launchpad#1 for launchpad

Fixes: 198040317
Change-Id: Ibdf877a1e24909904edfad50a12729abf25bd735
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants