what is it? why should i care? how can i avoid it? - aws

108
XSS Everywhere! XSS Everywhere! What is it? What is it? Why should I care? Why should I care? How can I avoid it? How can I avoid it? Nick Blundell — AppCheck NG

Upload: khangminh22

Post on 22-Nov-2023

0 views

Category:

Documents


0 download

TRANSCRIPT

XSS EverywhereXSS EverywhereWhat is itWhat is it

Why should I careWhy should I careHow can I avoid itHow can I avoid itNick Blundell mdash AppCheck NG

About UsAbout UsWe provide a vulnerability scanning service viasoware forged through day-to-day pen testingexperience We specialise in Web App securityWe live and breathe vulnerability research and wecontinue to push our scanning technology to thelimits and beyond

httpappcheck-ngcom

OverviewOverviewWhat is a Web ApplicationWhat is XSSHow can we avoid itHow can it be mitigated

What is a Web ApplicationWhat is a Web Application

It is an application that involves your Browser and aWeb Server

You put a URL in your browser and it loads a (HTML)page

Your browser then renders the page from theresponses HTML which looks like this

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltpgtMy first paragraphltpgt

lta href=some_other_pagegtVisit other pageltagt

ltbodygt

lthtmlgt

The HTML usually also contains scripts typicallyJavaScript which the browser executes alongsiderendering the page

This script execution is what enables the dynamicnature of day-to-day web applications

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltscriptgtwindowalert(Hello World)ltscriptgt

ltbodygt

lthtmlgt

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

About UsAbout UsWe provide a vulnerability scanning service viasoware forged through day-to-day pen testingexperience We specialise in Web App securityWe live and breathe vulnerability research and wecontinue to push our scanning technology to thelimits and beyond

httpappcheck-ngcom

OverviewOverviewWhat is a Web ApplicationWhat is XSSHow can we avoid itHow can it be mitigated

What is a Web ApplicationWhat is a Web Application

It is an application that involves your Browser and aWeb Server

You put a URL in your browser and it loads a (HTML)page

Your browser then renders the page from theresponses HTML which looks like this

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltpgtMy first paragraphltpgt

lta href=some_other_pagegtVisit other pageltagt

ltbodygt

lthtmlgt

The HTML usually also contains scripts typicallyJavaScript which the browser executes alongsiderendering the page

This script execution is what enables the dynamicnature of day-to-day web applications

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltscriptgtwindowalert(Hello World)ltscriptgt

ltbodygt

lthtmlgt

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

OverviewOverviewWhat is a Web ApplicationWhat is XSSHow can we avoid itHow can it be mitigated

What is a Web ApplicationWhat is a Web Application

It is an application that involves your Browser and aWeb Server

You put a URL in your browser and it loads a (HTML)page

Your browser then renders the page from theresponses HTML which looks like this

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltpgtMy first paragraphltpgt

lta href=some_other_pagegtVisit other pageltagt

ltbodygt

lthtmlgt

The HTML usually also contains scripts typicallyJavaScript which the browser executes alongsiderendering the page

This script execution is what enables the dynamicnature of day-to-day web applications

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltscriptgtwindowalert(Hello World)ltscriptgt

ltbodygt

lthtmlgt

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

What is a Web ApplicationWhat is a Web Application

It is an application that involves your Browser and aWeb Server

You put a URL in your browser and it loads a (HTML)page

Your browser then renders the page from theresponses HTML which looks like this

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltpgtMy first paragraphltpgt

lta href=some_other_pagegtVisit other pageltagt

ltbodygt

lthtmlgt

The HTML usually also contains scripts typicallyJavaScript which the browser executes alongsiderendering the page

This script execution is what enables the dynamicnature of day-to-day web applications

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltscriptgtwindowalert(Hello World)ltscriptgt

ltbodygt

lthtmlgt

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

It is an application that involves your Browser and aWeb Server

You put a URL in your browser and it loads a (HTML)page

Your browser then renders the page from theresponses HTML which looks like this

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltpgtMy first paragraphltpgt

lta href=some_other_pagegtVisit other pageltagt

ltbodygt

lthtmlgt

The HTML usually also contains scripts typicallyJavaScript which the browser executes alongsiderendering the page

This script execution is what enables the dynamicnature of day-to-day web applications

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltscriptgtwindowalert(Hello World)ltscriptgt

ltbodygt

lthtmlgt

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

You put a URL in your browser and it loads a (HTML)page

Your browser then renders the page from theresponses HTML which looks like this

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltpgtMy first paragraphltpgt

lta href=some_other_pagegtVisit other pageltagt

ltbodygt

lthtmlgt

The HTML usually also contains scripts typicallyJavaScript which the browser executes alongsiderendering the page

This script execution is what enables the dynamicnature of day-to-day web applications

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltscriptgtwindowalert(Hello World)ltscriptgt

ltbodygt

lthtmlgt

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Your browser then renders the page from theresponses HTML which looks like this

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltpgtMy first paragraphltpgt

lta href=some_other_pagegtVisit other pageltagt

ltbodygt

lthtmlgt

The HTML usually also contains scripts typicallyJavaScript which the browser executes alongsiderendering the page

This script execution is what enables the dynamicnature of day-to-day web applications

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltscriptgtwindowalert(Hello World)ltscriptgt

ltbodygt

lthtmlgt

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

The HTML usually also contains scripts typicallyJavaScript which the browser executes alongsiderendering the page

This script execution is what enables the dynamicnature of day-to-day web applications

ltDOCTYPE htmlgt

lthtmlgt

ltbodygt

lth1gtMy First Headinglth1gt

ltscriptgtwindowalert(Hello World)ltscriptgt

ltbodygt

lthtmlgt

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

You Have MailYou Have Mail

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Congratulations You have WONCongratulations You have WONan iPhone Xan iPhone X

Well done Ben As a long-term and valued member ofour online banking service you were shortlisted and

selected as the lucky winner of an iPhone X

Claim your iPhone X

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thats FantasticThats Fantastic

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Hmmm but wait

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Erm why is my bank balance now at zero

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Probably just a temporary issue

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

and nice about the iPhone

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Time for a little scrutinyTime for a little scrutinyThat link in my email

points to this (yikes)httpsecure-bankcom8686mini_pres_appvanilla

report_id=A6FE254233Cscript3Epayload203D202224(2723firstName27)val(27Evil27)3B24(2723lastName27)val(27Hacker27)3B24(2723account_num27)val(1236532)3B24(2723sort_code27)val(547345)3B24(2723amount27)val(12354434)3B24(2723do_transfer

27)click()220dsetTimeout(function()w=$(22iframe22)[0]contentWindow0dweval(payload)1000)3B24(22body22)prepend(2

73Cimg20src3D222Fcongratsjpg2220style3D22margin-le3A300px223E27)3Cscript3E3Ciframe20name=22theFrame22

width=221002220height=2230022src=22transfer_money223E

Claim your iPhone X

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Time for a little scrutinyTime for a little scrutinyBut still just looks like some long URL and Imused to seeing obscure stuff like thisAnd it is pointing to the secure banking applicationthat Im familiar with secure-bankcom

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Lets make that clearerLets make that clearerIf we URL decode that (ie change patterns like 3C tothe characters they represent like lt) it looks likethis

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtpayload =

$(firstName)val(Evil)$(lastName)val(Hacker)$(account_num)val(1236532)$(sort_code)val(547345)$(amount)val(12354434)$(do_transfer)click()

setTimeout(function()w=$(iframe)[0]contentWindowweval(payload)1000)$(body)prepend(ltimg

src=congratsjpg style=margin-le300pxgt)ltscriptgtltiframename=theFramewidth=100 height=300src=transfer_moneygt

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

This doesnt look right to meThis doesnt look right to me$(firstName)val(Evil)$(lastName)val(Hacker)

$(do_transfer)click()

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Looks like the intention is hiddenLooks like the intention is hiddenherehere

They are masking (ie hiding) something with thisimage

$(body)prepend(ltimg src=congratsjpg left300pxgt)

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Looks like the intention is hiddenLooks like the intention is hiddenherehere

Lets take away the mask and see

Claim your iPhone X (unmasked attack)

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Nooooo we Just got XSS-edNooooo we Just got XSS-edThis was a bog standard XSS attackwith a devastating impactA simple slip up can result in stuff like thisLets figure out how all of that just happened

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Recall that the web server responds to the browserwith HTML pagesThese pages have a structure which describes howthe page is to be laid out visuallyThat structure can also contain script elementswhich run code on the page to do fancy stuff likedisplaying a new social media event

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Usually an application does not want the user todirectly control the HTML structure perhaps just portions of it like actual content and definitely the application does not wantusers to add arbitrary script elements to the pages

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Confidentiality is lost when a single instance of XSSoccurs where a users input is mishandled to allow it toalter the wider HTML structure of a pageAn attacker will usually then exploit this to run ascript on a page accessed by another user so the hackers script will run within another usersprivateauthenticated session

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

What is a Cross-Site ScriptingWhat is a Cross-Site Scripting(XSS) Vulnerability(XSS) Vulnerability

Depending on the context an attack can be deliveredeither

Directly by a craed URL to a victim (via email otherwebsites etc) known as a Reflected XSS attackIndirectly by the hacker laying a trap in theapplication itself for victims to stumble uponknown as Persistent XSS

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How bad can it beHow bad can it beUsers may be exploited to the hackers financialadvantageAdmin accounts may be targeted leading to fullapplication compromise

With admin control of the application an attackerwill then seek to compromise the host and all dataaccessible from it usually through some privileged admin featurelike for system configuration

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Lets start with the basics aLets start with the basics avanilla examplevanilla example

When we are logged into our online banking app andwe view a report on the following URL

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

A vanilla exampleA vanilla exampleThe first thing we notice is that the ID of the report isreflected in the page from the web server

Requesting this URLhttpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423

Gives this response with the value reflectedlth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

A vanilla exampleA vanilla exampleSo the next question on an attackers mind is

How does the application respond if I try to input thespecial HTML tag brackets lt and gt

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltbgtevilltbgt

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltbgtevilltbgt

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

A vanilla exampleA vanilla example

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

A vanilla exampleA vanilla exampleSo the hacker can create an HTML tag on the victimspage how about running a script in the victims page(ie in their authenticated session)

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We see the script tag was injected without anyinterference through validation

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltscriptgtalert(evil)ltscriptgt

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

A vanilla exampleA vanilla exampleAnd so we see the hackers script executesNote oen XSS is demonstrated by popping openan alert box this is proof the hacker can perform any action onthe victims page

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the AttackerRight Mr Hacker you mess with us we mess with

you

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Approach If the hacker tries to inject a script tag weare going to strip the keyword out

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Application responds

We Win Boof the script keyword was stripped out sono malicious script will execute

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Application does

We Lose we stripped only lowercase script but HTMLtags are case insensitive so the hacker wins again

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Approach Strip out the keyword script whatever thecase of its characters

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Lose Hmmmm this guy knows some tricks Heanticipated me stripping out the keyword and createda keyword sandwich which made my efforts look really

lame

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltsCriPtgtalert(evil)ltsCriPtgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Approach If the hacker is going to sandwichkeywords like that I will now repeatedly strip out the keyword scriptuntil there are no more

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltsCrScripTiPtgtalert(evil)ltsCrScripTiPtgt

Application does

We Win The keyword script is stripped out regardlessof its case and if sandwiched

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltgtalert(evil)ltgt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)But but but then the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Lose Whaaat So they injected an img tag insteadof a script tag to run a script

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423ltimg src=1 onerror=alert(evil)gt

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Okay it turns out there are many ways to cause ascript to run in a browser so blocking the keywords like script is a completewaste of time

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)Right thats it Mr HackerWe know what needs to be doneStrip out any tag brackets so these lt and gtThen an attacker can inject no tags into the page noscript no img no nothing

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 4)Thwarting the Attacker (Take 4)So now when the hacker does

httpsecure-bankcom8686mini_pres_appvanillareport_id=A6FE25423ltimg+src=1+onerror=alert(evil)gt

Application does

We Win Ha ha Now you cannot create any HTML tags atall

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423img src=1 onerror=alert(evil) [No tag brackets]

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Job DoneJob DoneNo more XSS for me )

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

News just News just ininA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets from all user input

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appattr_valuereport_id=A6FE25423

lth2 id=A6FE25423gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThe hacker found this then created a yet stranger

payload like thishttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Lose In this context they didnt even need to add anew tag to get execution they just added an event

attribute to an existing tag

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the AttackerOkay Wise Guy I can see what you are doing hereand I know just what to doI will strip out quotes and double quotes too so you dont try any moretricks like that

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the Attackerhttpsecure-bankcom8686mini_pres_appattr_value

report_id=A6FE2542327+onmouseover=alert(27evil27)+foo=27

We Win Ha ha With your quotes stripped out youcannot break out of the quoted attribute value to add a

script executing attribute

lth2 id=A6FE25423 onmouseover=alert(evil) foo=gtViewing Reportlth2gt

Client Mr Jones

Report ID A6FE25423 onmouseover=alert(evil) foo=

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Home FreeHome FreeDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotesThere must be some mistake surely

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThere is another part of my application that reflects

input within a script tag like thishttpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564

lth2gtViewing Reportlth2gt

Client Mr Jones

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564

ltscriptgt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Lose The devils They found a place in a scriptwhere I reflected input without quotes - so they didnt

even need to use a quote (Id have stripped) to escape

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the AttackerOkay to reward your tenacity Mr Hacker Im goingto trap all of my input between quotes

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the AttackerNow when the hacker does this

httpsecure-bankcom8686mini_pres_appscript_unquotedreport_id=1236564-alert(1)

We Win Have that Mr Hacker Your attack is nowtrapped as a simple string and can no longer execute

Report ID ltspan id=id_placeholdergtNoneltspangt

ltscriptgt

documentgetElementById(id_placeholder)innerText = 1236564-alert(1)

ltscriptgt

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Hang up Your Hat Mr HackerHang up Your Hat Mr HackerDefinitely no more XSS for me now )

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I stripped out tag brackets and quotes and I ensured all input was trapped betweenquotesThis is becoming te di ous (

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Client Mr Jones

Report link lta onclick=log(Report opened + A6FE25423)gtlinkltagt

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThe hacker found this then did this

httpsecure-bankcom8686mini_pres_appevent_attrreport_id=A6FE2542326apos3b-alert(1)-26apos3b

We Lose but it is not clear why from this since itseems they did not break out of the quotes so how

on Earth did it manage to execute

lth2gtViewing Reportlth2gt

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenWe need to understand what HTML entity encoding is

to understand how this attack succeeded

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

So What is HTML EntitySo What is HTML EntityEncodingEncoding

Usually when we have a text-based structure like anHTML page there is a way of escaping special characters sothey can be displayed literally and so without being interpreted dangerously bythe browser as part of the wider HTML structure

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

HTML Entity EncodingHTML Entity EncodingSo encodings of key characters are encoded like this(with many other possible representations)

lt becomes ampltgt becomes ampgt becomes ampapos becomes ampquotamp becomes ampamp

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

So how did it happenSo how did it happenSubtle insight When the browser parses tag attributes

it automatically HTML decodes their values

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

So how did it happenSo how did it happenSo this

gets interpreted by the browser as this

So the hacker bypassed my quote stripping by encodingthe quote character in a way I didnt anticipate

Report link lta onclick=log(

Report opened + A6FE25423ampapos-alert(1)-ampapos

)gtlinkltagt

Report link lta onclick=log(

Report opened + A6FE25423-alert(1)-

)gtlinkltagt

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the AttackerTwo can play this game lets also use HTML encoding mdash

but as a defence mdash by HTML encoding the inputhttpsecure-bankcom8686mini_pres_appevent_attr

report_id=A6FE2542326apos3b-alert(1)-26apos3b

We Win In your face Mr Hacker When they attemptthe bypass their input ends up double HTML encoded

so the attack fails

Report link lta onclick=log(

Report opened + A6FE25423ampampapos-alert(1)-ampampapos

)gtlinkltagt

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Im Riding on a High Mr HackerIm Riding on a High Mr HackerI have drunk deeply from the fountain of XSS

knowledge and now you must stand aside My Friend

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote charactersand then I HTML entity encoded all inputWHAT MORE CAN I DO

WHAT MORE CAN I DO

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThere is another part of my application that reflects

input like thishttpsecure-bankcom8686mini_pres_apphref_attrreport_id=A6FE25423

lth2gtViewing Reportlth2gt

Report ID A6FE25423

lta href=A6FE25423editgtEdit reportltagt

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThe hacker found this then did thishttpsecure-bankcom8686mini_pres_apphref_attr

report_id=javascriptalert(1)

We Lose What is THAT thing Their rather elegantpayload did not rely on any quote breakout and was

unaffected by HTML encoding etc

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=javascriptalert(1)editgtEdit reportltagt

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenSo it turns out that attributes which take URLs can

actually run scripts when prefixed with a specialscheme

javascript will run JavaScriptvbscript will run visual basic (MS browsers)livescript will run a prehistoric relic of JavaScript(old old Netscape mdash Do not worry about this)

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Right lets block anyting that starts with one of thosescript schemesAnd we are not going to fall for the exact-case trickagain we will block these whatever the case

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)So when the hacker tries this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=jaVascRiPtalert(1)

We Win The script prefix is blocked whatever the case

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 1)Thwarting the Attacker (Take 1)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Lose Doh The swines just added a space in front ofjavascript so my match-at-the-start-of-the-input

would fail Browsers are unhelpfully lenient in parsingthings like this

lth2gtViewing Reportlth2gt

Report ID javascriptalert(1)

lta href=_javascriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Right lets block anything that simply contains one ofthose script schemes whatever the case

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVascRiPtalert(1)

We Win Their space trick is now also blocked by ourstuff

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 2)Thwarting the Attacker (Take 2)Then the hacker does this

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=java0ascriptalert(1)

We Lose Youve got to be kidding me They bypassedmy check for javascript by sticking a line-break

character in the middle which the browser completelyignores and executes regardless

lth2gtViewing Reportlth2gt

Report ID java

scriptalert(1)

lta href=java lt-- sneaky line break

scriptalert(1)editgtEdit reportltagt

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)Okay we have to be really careful when using inputwith URL attributes like this especially if they reflect the start of the URL likehere

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the Attacker (Take 3)Thwarting the Attacker (Take 3)In this case I am going to opt for a strong whitelisting of only the alpha-numeric characters that arerequired for a report ID

httpsecure-bankcom8686mini_pres_apphref_attrreport_id=20jaVa0AscRiPtalert(1)

We Win This blocks any of that weird stuff yet willallow the application to work as intended

lth2gtViewing Reportlth2gt

Report ID BLOCKED

lta href=BLOCKEDeditgtEdit reportltagt

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

So Long and thanks for all theSo Long and thanks for all theFish Mr HackerFish Mr Hacker

The hour is late and my job here is done

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

But I ensured all input was trapped between quotesI stripped out quote characters

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

News just inNews just inA HACKER HAS JUST EXPLOITED XSS ON OUR

APPLICATION

and I HTML entity encoded all inputand I locked down the harder places with whitelistsThis cannot be happening to me mdash it must be astress-induced nightmareYes Im going to wake up soon please PLEASE

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThe hacker did this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

We Lose But how This is not possible because wehave already added appropriate protection for thiscrude script kiddie payload mdash it was one of the first

things we tackled

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenThe page just exploited looks like this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423

Hmmmm But I dont see the input value A6FE25423reflected anywhere in this page

lth2gtViewing Reportlth2gt

Report ID ltspan id=report_idgtltspangt

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenAh wait a minute I see what this code is doing it is a

different beast altogetherltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happen

So my web server does not return a page with thevalue reflected in it but it does return a script that runs when the pageloads

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happen

and this script then reads the URL parameterURLSearchParams(windowlocationsearch)get(report_i

then updates a placeholder element with the value$(report_id)html(report_id)

ltscriptgt

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

ltscriptgt

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

How did it happenHow did it happenSo there is a reflection of the user input but ithappens only on the client-side (ie in the browser)The web server never touches the value so here allmy efforts of server-side validation are completelybypassed

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Introducing DOM-based XSSIntroducing DOM-based XSSSo just as we have seen lots of examples so far ofbuilding unsafe HTML on the server it is possible also for a script to dynamicallyupdate the page you are viewing in an unsafe way

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Introducing DOM-based XSSIntroducing DOM-based XSSThis vector is known as DOM-based XSS since itoccurs during execution of a script which directly updates the Document Object Model(DOM) (ie the parsed HTML page that you view aera page loads)

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the AttackerThe main problem here is that we use an unsafemethod html() to change the DOM such that raw HTML supplied by the hacker will beparsed and rendered as HTML

It is common to see stuff like this when thepossibility of malicious input has been completelyoverlooked

var report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)html(report_id)

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the AttackerClearly it was not intended for a report ID to everexpress a piece of HTMLLets instead use text() which sets the contentsof the placeholder tag as text just plain textvar report_id = new URLSearchParams(windowlocationsearch)get(report_id)

$(report_id)text(report_id)

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Thwarting the AttackerThwarting the AttackerSo now when the hacker does this

httpsecure-bankcom8686mini_pres_appdom_query_paramreport_id=A6FE25423ltscriptgtalert(evil)ltscriptgt

Their payload is simply (and shamefully) displayedand does not execute

We Win

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

So XSS is Hard RightSo XSS is Hard Right and deadly

No wonder it gets everywhere

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

So XSS is Hard RightSo XSS is Hard Right

It is worth trying to understand the intricacies of XSS asmuch as possible if you do not want to fall victim to it

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Some general guidelinesSome general guidelinesIn whatever context trap input between quotes anduse context-appropriate escapingencoding stop apayload breaking out of those quotesOtherwise employ very tight valuecharacter whitelisting

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Some general guidelinesSome general guidelinesRemember Tag attribute values are HTML decoded asthe browser parses the HTML so hackers will try touse this fact to bypass validation

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Some general guidelinesSome general guidelinesBe really careful when using input in URL contextsespecially to avoid attacks which use the javascriptscheme

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Some general guidelinesSome general guidelinesDont neglect client side handling of input to makesure it cannot be written to the DOM in an unsafeway

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Some general guidelinesSome general guidelinesThink twice before mixing user input with rawJavaScript evaluation functions such as eval()setTimeout() etc

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Mitigation (and its Limitations)Mitigation (and its Limitations)There are three main mitigation approaches for XSS

(Though avoidance of XSS is the best mitigation)

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Mitigation (and its Limitations)Mitigation (and its Limitations)HttpOnlyHttpOnly Cookies Cookies

These stop a direct hijack of a users session byhiding session tokens (in cookies) from scriptsThough making it harder an attacker will usuallyfind an application specific way to steal the usersaccount via XSS such as by changing their emailaddress then triggering a password resetIt is a good general rule to set any sensitive cookiesas HttpOnly unless the application requires it to beotherwise

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Mitigation (and its Limitations)Mitigation (and its Limitations)Browser XSS BlockingBrowser XSS Blocking

Any scripts observed to be reflected from the requestare blocked from executing by the browserHowever it is an ongoing game of catch-up betweenthe blocking algorithm and new techniques whichbypass itIt is effective only for Reflected XSS so not Persistentor DOM-based XSSIn fact Microso has announced it will soon retireXSS blocking in Edge given the little gain it brings

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Mitigation (and its Limitations)Mitigation (and its Limitations)Content Security Policy (CSP)Content Security Policy (CSP)

Supporting browsers allow the web application tolock down sources of JavaScript executionSo a CSP policy might allow scripts to execute onlyfrom a reduced set of source URLsAnd may block the use of inline javascript such asevent handlers and script tagsIn practice this may require a substantial re-writingof application code since violations may stop theapplication from functioning correctly

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Further ReadingFurther Reading(for Techs or to pass on to your Techs)(for Techs or to pass on to your Techs)

Must read

Great collection of security related browser quirks

httpswwwowasporgindexphpXSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

httpscodegooglecomarchivepbrowsersecwikisMainwiki

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

We Looked atWe Looked atXSSXSSMore XSSThen a little more XSS still

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions

Dr Nick Blundell mdash AppCheck NG

Thank YouThank YouI hope that was useful

Questions