contrib.nodejsscan.ssrf_phantomjs.phantom_ssrf

profile photo of returntocorpreturntocorp
Author
59
Download Count*
License

If unverified user data can reach the phantom methods it can result in Server-Side Request Forgery vulnerabilities.

Run Locally

Run in CI

Defintion

rules:
  - id: phantom_ssrf
    patterns:
      - pattern-inside: |
          require('phantom');
          ...
      - pattern-either:
          - 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: $PAGE.open(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $PAGE.setContent(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $PAGE.open(<... $REQ.$BODY ...>,...)
          - pattern: $PAGE.setContent(<... $REQ.$BODY ...>,...)
          - pattern: $PAGE.openUrl(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $PAGE.openUrl(<... $REQ.$BODY ...>,...)
          - pattern: $PAGE.evaluateJavaScript(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $PAGE.evaluateJavaScript(<... $REQ.$BODY ...>,...)
          - pattern: $PAGE.property("content",<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: $PAGE.property("content",<... $REQ.$BODY ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.open(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.open(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.openUrl(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.openUrl(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateJavaScript(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateJavaScript(<... $INPUT ...>,...);
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.property("content",<... $INPUT ...>,...);
          - pattern: |-
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.property("content",<... $INPUT ...>,...);
    message: If unverified user data can reach the `phantom` methods it can result
      in Server-Side Request Forgery vulnerabilities.
    metadata:
      owasp: A01:2017 - Injection
      cwe: "CWE-918: Server-Side Request Forgery (SSRF)"
      category: security
      technology:
        - node.js
        - express
        - phantomjs
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    severity: ERROR
    languages:
      - javascript

Examples

ssrf_phantomjs.js

const express = require('express')
const app = express()
const port = 3000
const phantom = require('phantom');

app.get('/test', async (req, res) => {
    const instance = await phantom.create();
    const page = await instance.createPage();
    await page.on('onResourceRequested', function (requestData) {
        console.info('Requesting', requestData.url);
    });

    // ruleid: phantom_ssrf
    const status = await page.property('content', req.get('name'));

    // ruleid: phantom_ssrf
    await page.setContent(req.query.q);

    res.send('Hello World!')
})

app.post('/test2', async (req, res) => {
    const instance = await phantom.create();
    const page = await instance.createPage();
    await page.on('onResourceRequested', function (requestData) {
        console.info('Requesting', requestData.url);
    });

    // ruleid: phantom_ssrf
    const status = await page.property('content', req.query.q);

    // ruleid: phantom_ssrf
    await page.setContent(req.body);

    const express = require('express')
    const app = express()
    const port = 3000
    const phantom = require('phantom');

    app.get('/test', async (req, res) => {
        const instance = await phantom.create();
        const page = await instance.createPage();
        await page.on('onResourceRequested', function (requestData) {
            console.info('Requesting', requestData.url);
        });

        // ruleid: phantom_ssrf
        const status = await page.property('content', req.get('name'));

        // ruleid: phantom_ssrf
        await page.setContent(req.query.q);

        res.send('Hello World!')
    })

    app.post('/test2', async (req, res) => {
        const instance = await phantom.create();
        const page = await instance.createPage();
        await page.on('onResourceRequested', function (requestData) {
            console.info('Requesting', requestData.url);
        });

        // ruleid: phantom_ssrf
        const status = await page.property('content', req.query.q);

        // ruleid: phantom_ssrf
        await page.setContent(req.body);


        await instance.exit();

        res.send('Hello World!')
    })

    app.post('/test3', async (req, res) => {
        const instance = await phantom.create();
        const page = await instance.createPage();
        await page.on('onResourceRequested', function (requestData) {
            console.info('Requesting', requestData.url);
        });

        // ruleid: phantom_ssrf
        const status = await page.openUrl(req.params.url, {}, {});

        // ruleid: phantom_ssrf
        await page.evaluateJavaScript(req.body.script);


        await instance.exit();

        res.send('Hello World!')
    })


    app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
    await instance.exit();

    res.send('Hello World!')
})

app.post('/test3', async (req, res) => {
    const instance = await phantom.create();
    const page = await instance.createPage();
    await page.on('onResourceRequested', function (requestData) {
        console.info('Requesting', requestData.url);
    });

    // ruleid: phantom_ssrf
    const status = await page.openUrl(req.params.url, {}, {});

    // ruleid: phantom_ssrf
    await page.evaluateJavaScript(req.body.script);



    await instance.exit();

    res.send('Hello World!')
})


app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))