contrib.nodejsscan.header_injection.generic_header_injection

profile photo of returntocorpreturntocorp
Author
99
Download Count*
License

Untrusted user input in response header will result in HTTP Header Injection or Response Splitting Attacks.

Run Locally

Run in CI

Defintion

rules:
  - id: generic_header_injection
    patterns:
      - 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: |
              $INP = $REQ.$QUERY;
              ...
              $RES.set(..., <... $INP ...>, ...);
          - pattern: |
              $INP = $REQ.$QUERY.$VAR;
              ...
              $RES.set(..., <... $INP ...>, ...);
          - pattern: |
              $INP = $REQ.$VAR;
              ...
              $RES.set(..., { $X: <... $INP ...>}, ...);
          - pattern: |
              $INP = $REQ.$QUERY.$FOO;
              ...
              $RES.set(..., { $X: <... $INP ...>}, ...);
          - pattern: |
              $INP = $REQ.$VAR;
              ...
              $RES.writeHead(..., { $X: <... $INP ...> }, ...);
          - pattern: |
              $INP = $REQ.$QUERY.$FOO;
              ...
              $RES.writeHead(..., { $X: <... $INP ...> }, ...);
          - pattern: |
              $RES.set(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $RES.set(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $RES.set(..., { $X: <... $REQ.$VAR ...>}, ...)
          - pattern: |
              $RES.set(..., { $X: <... $REQ.$QUERY.$FOO ...>}, ...);
          - pattern: |
              $RES.writeHead(..., { $X: <... $REQ.$VAR ...> }, ...);
          - pattern: |
              $RES.writeHead(..., { $X: <... $REQ.$QUERY.$FOO ...> }, ...);
    message: Untrusted user input in response header will result in HTTP Header
      Injection or Response Splitting Attacks.
    languages:
      - javascript
    severity: ERROR
    metadata:
      owasp: A01:2017 - Injection
      cwe: "CWE-644: Improper Neutralization of HTTP Headers for Scripting Syntax"
      category: security
      technology:
        - node.js
        - express
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]

Examples

header_injection.js

var server = http.createServer(function (req, res) {
    var bla = 'dsdsd';
    switch (testIndex++) {
        case 0:
            // ruleid:generic_header_injection
            res.writeHead(200, { test: 'foo \r\ninvalid: bar' + req.foo });
            break;
        case 1:
            // ruleid:generic_header_injection
            res.writeHead(200, { test: req.foo + 'foo \ninvalid: bar' });
            break;
        case 2:
            // ruleid:generic_header_injection
            res.writeHead(200, { test: 'foo \rinvalid: bar' + req.foo + 'asdadasd', foo: bar });
            break;
        case 3:
            // ruleid:generic_header_injection
            res.writeHead(200, { test: bla + 'foo \n\n\ninvalid: bar' + req.foo });
            break;
        case 5:
            // ruleid:generic_header_injection
            res.writeHead(200, { test: bla + 'foo \n\n\ninvalid: bar' + req.foo('asd') });
            break;
        case 4:
            // ruleid:generic_header_injection
            res.writeHead(200, { test: req.foo });
            server.close();
            break;
        default:
            assert(false);
    }
    res.end('Hi mars!');
});
server.listen(common.PORT);

var express = require('express');
var app = express();
app.get('/', function (req, res) {
    // ruleid:generic_header_injection
    res.writeHead(200, { test: 'foo \r\ninvalid: bar' + req.foo });

    // ruleid:generic_header_injection
    res.set('Content-Type', req.query.foo);
    // ruleid:generic_header_injection
    res.set('foo', 'asdad' + req.query.foo);
    // ruleid:generic_header_injection
    res.set(req.query.foo, 'asdadad');
    // ruleid:generic_header_injection
    res.set('asda' + req.query.foo, 'asdadad');
    // ruleid:generic_header_injection
    res.set('asda' + req.query["foo"], 'asdadad');
    // ruleid:generic_header_injection
    res.set('asda' + req.query("foo"), 'asdadad');
    // ruleid:generic_header_injection
    res.set({
        'Content-Type': 'text/plain',
        'Content-Length': req.query.foo,
        'ETag': '12345'
    })
    //do not detect
    res.writeHead(200, { tast: ddd })
    res.set(ffff)
});