id: window-name-domxss

info:
  name: window.name - DOM Cross-Site Scripting
  author: pdteam
  severity: high
  description: The window-name is vulnerable to DOM based cross-site scripting.
  reference:
    - https://public-firing-range.appspot.com/dom/index.html
  classification:
    cvss-metrics: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
    cvss-score: 7.2
    cwe-id: CWE-79
  tags: headless,xss,domxss

headless:
  - steps:
      - action: setheader
        args:
          part: response
          key: Content-Security-Policy
          value: "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:;"

      - action: script
        args:
          hook: true
          code: |
            () => {
              window.alerts = [];

              logger = found => window.alerts.push(found);

              function getStackTrace() {
                var stack;
                try {
                  throw new Error('');
                }
                catch (error) {
                  stack = error.stack || '';
                }
                stack = stack.split('\n').map(function (line) { return line.trim(); });
                return stack.splice(stack[0] == 'Error' ? 2 : 1);
              }
              window.name = "{{randstr_1}}'\"<>";

              var oldEval = eval;
              var oldDocumentWrite = document.write;
              var setter = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML').set;
              Object.defineProperty(Element.prototype, 'innerHTML', {
                set: function innerHTML_Setter(val) {
                  if (val.includes("{{randstr_1}}'\"<>")) {
                    logger({sink: 'innerHTML', source: 'window.name', code: val, stack: getStackTrace()});
                  }
                  return setter.call(this, val)
                }
              });

              eval = function(data) {
                if (data.includes("{{randstr_1}}'\"<>")) {
                  logger({sink: 'eval' ,source: 'window.name', code: data, stack: getStackTrace()});
                }
                return oldEval.apply(this, arguments);
              };

              document.write = function(data) {
                if (data.includes("{{randstr_1}}'\"<>")) {
                  logger({sink: 'document.write' ,source: 'window.name', code: data, stack: getStackTrace()});
                }
                return oldEval.apply(this, arguments);
              };
            }

      - args:
          url: "{{BaseURL}}"
        action: navigate
      - action: waitload

      - action: script
        name: alerts
        args:
          code: |
            () => { window.alerts }

    matchers:
      - type: word
        part: alerts
        words:
          - "sink:"

    extractors:
      - type: kval
        part: alerts
        kval:
          - alerts
# digest: 490a004630440220685d95cd362213f94a3ce6ece2fd10178df3c31d1a40a7c730b6fd6fa174e6c702200a5d9b764b563c7adfbb571e6190145e6aca2b1fc52a2de50ff9163a706c860d:922c64590222798bb761d5b6d8e72950