contrib.nodejsscan.eval_vm_injection.vm_code_injection

profile photo of returntocorpreturntocorp
Author
99
Download Count*
License

Untrusted user input reaching vm can result in code injection.

Run Locally

Run in CI

Defintion

rules:
  - id: vm_code_injection
    patterns:
      - pattern-inside: |
          $VM = require('vm');
          ...
      - pattern-either:
          - pattern-inside: function ($REQ, $RES, ...) {...}
          - pattern-inside: function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: $VM.runInContext(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $VM.runInContext(<... $REQ.$BODY ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.runInContext($INPUT,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $VM.runInContext($INPUT,...);
          - pattern: $VM.runInNewContext(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $VM.runInNewContext(<... $REQ.$BODY ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.runInNewContext($INPUT,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $VM.runInNewContext($INPUT,...);
          - pattern: $VM.runInThisContext(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $VM.runInThisContext(<... $REQ.$BODY ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.runInThisContext($INPUT,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $VM.runInThisContext($INPUT,...);
          - pattern: $VM.compileFunction(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $VM.compileFunction(<... $REQ.$BODY ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.compileFunction($INPUT,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $VM.compileFunction($INPUT,...);
          - pattern: new $VM.Script(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: new $VM.Script(<... $REQ.$BODY ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              new $VM.Script($INPUT,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              new $VM.Script($INPUT,...);
    message: Untrusted user input reaching `vm` can result in code injection.
    severity: ERROR
    languages:
      - javascript
    metadata:
      owasp: A01:2017 - Injection
      cwe: "CWE-94: Improper Control of Generation of Code ('Code Injection')"
      category: security
      technology:
        - node.js
        - express
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]

Examples

eval_vm_injection.js

const vm = require('vm')

let ctrl1 = function test1(req, res) {
    // ruleid:vm_runincontext_injection
    var input = req.query.something || ''
    var sandbox = {
        foo: input
    }
    vm.createContext(sandbox)
    vm.runInContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
}
app.get('/', ctrl1)

app.get('/', (req, res) => {
    // ruleid:vm_runincontext_injection
    var sandbox = {
        foo: req.query.userInput
    }
    vm.createContext(sandbox)
    vm.runInContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
})

var ctrl2 = null;
ctrl2 = function test2(req, res) {
    // ruleid:vm_runinnewcontext_injection
    var input = req.query.something || ''
    var sandbox = {
        foo: input
    }
    vm.runInNewContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
}
app.get('/', ctrl2)


app.get('/', function (req, res) {
    // ruleid:vm_runinnewcontext_injection
    var sandbox = {
        foo: req.query.userInput
    }
    vm.runInNewContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
})


app.get('/', function (req, res) {
    // ruleid:vm_code_injection
    const code = `
        var x = ${req.query.userInput};
    `
    vm.runInThisContext(code)
    res.send('hello world')
})


app.get('/', function test4(req, res) {
    const parsingContext = vm.createContext({ name: 'world' })
    // ruleid:vm_code_injection
    const code = `return 'hello ' + ${req.query.userInput}`
    let fn = vm.compileFunction(code, [], { parsingContext })
    res.send('hello world')
})


app.get('/', (req, res) => {
    // ruleid:vm_compilefunction_injection
    const context = vm.createContext({ name: req.query.userInput })
    let code = `return 'hello ' name`
    const fn = vm.compileFunction(code, [], { parsingContext: context })
    res.send('hello world')
})

app.get('/', function (req, res) {
    // ruleid:vm_code_injection
    const script = new vm.Script(`
        function add(a, b) {
          return a + ${req.query.userInput};
        }

        const x = add(1, 2);
    `);

    script.runInThisContext();
    res.send('hello world')
})