Skip to content

Commit

Permalink
added detection of payload in 'dangerous' attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
fcavallarin committed Oct 30, 2019
1 parent 3a5c74b commit bd069d0
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 12 deletions.
27 changes: 20 additions & 7 deletions domdig.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,23 @@ function getUrlMutations(url, payload){
}

async function scanAttributes(crawler){
const elems = await crawler.page().$$('[xssSinkAttribute^="window.___xssSink"]');
for(let e of elems){
// must use evaluate since puppetteer cannot get non-standard attributes
let attr = await e.evaluate(i => i.getAttribute("xssSinkAttribute"));
let key = attr.match(/\(([0-9]+)\)/)[1];
utils.addVulnerability(PAYLOADMAP[key], VULNSJAR, null, VERBOSE);
// use also 'srcdoc' since it can contain also esacped html: <iframe srcdoc="&lt;img src=1 onerror=alert(1)&gt;"></iframe>
// content can have a "timer" so maybe is not executed in time
const attrs = ["href", "action", "formaction", "srcdoc", "content"];
for(let attr of attrs){
const elems = await crawler.page().$$(`[${attr}]`);
for(let e of elems){
// must use evaluate since puppetteer cannot get non-standard attributes
let val = await e.evaluate( (i,a) => i.getAttribute(a), attr);
if(val.match("___xssSink") == null){
continue;
} else {
let key = val.match(/\(([0-9]+)\)/)[1];
let es = await utils.getElementSelector(e);
utils.addVulnerability(PAYLOADMAP[key], VULNSJAR, null, VERBOSE, `Attribute '${attr}' of '${es}' set to payload`);
break;
}
}
}
}

Expand Down Expand Up @@ -194,6 +205,7 @@ function ps(message){
crawler = await loadCrawler(targetUrl.href, payload, options, true);
await scanDom(crawler, options);
await triggerOnpaste(crawler);
await scanAttributes(crawler);
await close(crawler);
ps(cnt + "/" + payloads.length + " payloads checked");
cnt++;
Expand All @@ -212,6 +224,7 @@ function ps(message){
} else {
await scanDom(crawler, options);
}
await scanAttributes(crawler);
await triggerOnpaste(crawler);
await close(crawler);
ps(cnt + "/" + payloads.length + " payloads checked");
Expand All @@ -227,7 +240,7 @@ function ps(message){
console.log(utils.prettifyJson(VULNSJAR));
} else if(VERBOSE){
for(let v of VULNSJAR){
utils.printVulnerability(v[0], v[1], v[2]);
utils.printVulnerability(v[0], v[1], v[2], v[3]);
}
}

Expand Down
46 changes: 41 additions & 5 deletions utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,25 @@ exports.writeJSON = writeJSON;
exports.prettifyJson = prettifyJson;
exports.loadPayloadsFromFile = loadPayloadsFromFile;
exports.error = error;
exports.getElementSelector = getElementSelector;

function addVulnerability(vuln, jar, url, verbose){
function addVulnerability(vuln, jar, url, verbose, message){
message = message || null;
p = vuln.payload.replace("window.___xssSink({0})", "alert(1)");
for(let e of jar){
if(e[0] == p && e[1] == vuln.element && (!url || e[2] == url)){
return;
}
}
jar.push([p, vuln.element, url]);
jar.push([p, vuln.element, url, message]);
if(verbose){
printVulnerability(p, vuln.element, url);
printVulnerability(p, vuln.element, url, message);
}
}

function printVulnerability(payload, element, url){
var msg = chalk.red('[!]') + ` DOM XSS found: ${element}${payload}`;
function printVulnerability(payload, element, url, message){
message = message || "DOM XSS found";
var msg = chalk.red('[!]') + ` ${message}: ${element}${payload}`;
if(url){
msg += " → " + url;
}
Expand Down Expand Up @@ -252,3 +255,36 @@ function prettifyJson(obj, layer){
}
return obj;
}


async function getElementSelector(element){
return await element.evaluate( i => {
function gs(element){
if(!element || !(element instanceof HTMLElement))
return "";
var name = element.nodeName.toLowerCase();
var ret = [];
var selector = ""
var id = element.getAttribute("id");

if(id && id.match(/^[a-z][a-z0-9\-_:\.]*$/i)){
selector = "#" + id;
} else {
let p = element;
let cnt = 1;
while(p = p.previousSibling){
if(p instanceof HTMLElement && p.nodeName.toLowerCase() == name){
cnt++;
}
}
selector = name + (cnt > 1 ? `:nth-of-type(${cnt})` : "");
if(element != document.documentElement && name != "body" && element.parentNode){
ret.push(gs(element.parentNode));
}
}
ret.push(selector);
return ret.join(" > ");
}
return gs(i);
});
}

0 comments on commit bd069d0

Please sign in to comment.