--- title: "Headless" --- ## Basic Headless Navigation Example This template visits a URL in the headless browser and waits for it to load. ```yaml id: basic-headless-request info: name: Basic Headless Request author: pdteam severity: info headless: - steps: - action: navigate args: url: "{{BaseURL}}" - action: waitload ``` ## Headless prototype pollution detection The below template detects prototype pollution on pages with Nuclei headless capabilities. The code for detection is taken from [https://github.com/msrkp/PPScan](https://github.com/msrkp/PPScan). We make use of script injection capabilities of nuclei to provide reliable detection for prototype pollution. ```yaml id: prototype-pollution-check info: name: Prototype Pollution Check author: pd-team severity: medium reference: https://github.com/msrkp/PPScan headless: - steps: - action: setheader args: part: response key: Content-Security-Policy value: "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:;" - action: setheader args: part: response key: X-Frame-Options value: foo - action: setheader args: part: response key: If-None-Match value: foo # Set the hook to override window.data for xss detection - action: script args: hook: true code: | // Hooking code adapted from https://github.com/msrkp/PPScan/blob/main/scripts/content_script.js (function() {window.alerts = []; function logger(found) { window.alerts.push(found); } function check() { loc = location.href; if (loc.indexOf("e32a5ec9c99") >= 0 && loc.search("a0def12bce") == -1) { setTimeout(function() { if (Object.prototype.e32a5ec9c99 == "ddcb362f1d60") { logger(location.href); } var url = new URL(location.origin + location.pathname); url.hash = "__proto__[a0def12bce]=ddcb362f1d60&__proto__.a0def12bce=ddcb362f1d60&dummy"; location = url.href; }, 5 * 1000); } else if (loc.search("a0def12bce") != -1) { setTimeout(function() { if (Object.prototype.a0def12bce == "ddcb362f1d60") { logger(location.href); } window.close(); }, 5 * 1000); } else { var url = new URL(loc); url.searchParams.append("__proto__[e32a5ec9c99]", "ddcb362f1d60"); url.searchParams.append("__proto__.e32a5ec9c99", "ddcb362f1d60"); location = url.href; } } window.onload = function() { if (Object.prototype.e32a5ec9c99 == "ddcb362f1d60" || Object.prototype.a0def12bce == "ddcb362f1d60") { logger(location.href); } else { check(); } }; var timerID = setInterval(function() { if (Object.prototype.e32a5ec9c99 == "ddcb362f1d60" || Object.prototype.a0def12bce == "ddcb362f1d60") { logger(location.href); clearInterval(timerID); } }, 5 * 1000)})(); - args: url: "{{BaseURL}}" action: navigate - action: waitload - action: script name: alerts args: code: "window.alerts" matchers: - type: word part: alerts words: - "__proto__" extractors: - type: kval part: alerts kval: - alerts ``` ## DVWA XSS Reproduction With Headless Mode This template logs into DVWA (Damn Vulnerable Web App) and tries to automatically reproduce a Reflected XSS, returning a match if it found that the payload was executed successfully. ```yaml id: dvwa-xss-verification info: name: DVWA Reflected XSS Verification author: pd-team severity: info headless: - steps: - args: url: "{{BaseURL}}" action: navigate - action: waitload # Set the hook to override window.data for xss detection - action: script args: hook: true code: "(function() { window.alert = function() { window.data = 'found' } })()" - args: by: x value: admin xpath: /html/body/div/div[2]/form/fieldset/input action: text - args: by: x value: password xpath: /html/body/div/div[2]/form/fieldset/input[2] action: text - args: by: x xpath: /html/body/div/div[2]/form/fieldset/p/input action: click - action: waitload - args: by: x xpath: /html/body/div/div[2]/div/ul[2]/li[11]/a action: click - action: waitload - args: by: x value: '">' xpath: /html/body/div/div[3]/div/div/form/p/input action: text - args: keys: "\r" # Press the enter key on the keyboard action: keyboard - action: waitload - action: script name: alert args: code: "window.data" matchers: - part: alert type: word words: - "found" ``` ## DOM XSS Detection This template performs detection of DOM-XSS for `window.name` source by hooking common sinks such as `eval`, `innerHTML` and `document.write`. ```yaml id: window-name-domxss info: name: window.name DOM XSS author: pd-team severity: medium 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: | (function() {window.alerts = []; function 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 ```