The #7th DOJO CHALLENGE was on finding a valid WAF bypass to exploit an XSS on prototype pollution vulnerability.
WINNERS!
We are glad to announce the #7 DOJO Challenge winners list.
3 BEST WRITE-UP REPORT
Subscribe to our Twitter or Linkedin feeds to be notified of the upcoming challenges.
Read on to find the best write-up as well as the challenge author’s recommendations.
The challenge
You had an input field that you could inject, but many characters were sanitized and the WAF could detect them. The goal was to get around that WAF and find a way to exploit an XSS.
We asked you to produce a qualified write-up report explaining the logic allowing such exploitation. This write-up serves two purposes:
- Ensure no copy-paste would occur.
- Determine the contestant ability to properly describe a vulnerability and its vectors inside a professionally redacted report. This capacity gives us invaluable hints on your own, unique, talent as a bug hunter.
BEST WRITE-UP REPORT
Zomsop82‘sreport was detailed, informative, and good at explaining the WAF exploitation logic.
The others reports, notably Marcosen‘s and ivarsvids‘s were also very nice, we’re sorry can’t publish them all because that’s where you clearly witness the outstanding creativity of our community.
Thank you all for playing with us!
Zomsop82‘s Write-Up
————– START OF Zomsop82 REPORT ——————
Description
Website allow user to control a JavaScript variable named “config” which can be used to perform reflected XSS.
It is noted that a “WAF” is in place to avoid XSS but can be bypassed.
Exploitation
- Website allow user input with almost no restriction to take place in a JavaScript variable.
- User input is expected to be valid JSON which restricts a bit the allowed charset and forbid usage of JavaScript features such as functions.
- The “WAF” in place forbid characters such as ["<", ">", "'", '"', "?"], which would be enough to restrict XSS if previous checks were not hijackable.
- The main issue lie in the function isObject() which is bypassable because user can modify the JavaScript object prototype and masquerade as another type by declaring a property
__proto__ such as config = {"__proto__": {}} - Once type check bypassed, the control loop over forbidden characters was not working anymore because of missing includes function on object. This could be fixed by using Array prototype on config object such as
config = {"__proto__": []}, which has an includes function (See [1]) and is then inherited by config object allowing to bypass the loop. - Now that the config object can pass through the “WAF” without issues, we can add a payload with JavaScript code execution inside name property such as
{"name": "<script>alert(1)</script>".
Final payload is:
{"__proto__":[], "name": "<script>alert(1)</script>"}
PoC
Payload
{"__proto__":[], "name": "<script>alert(1)</script>"}
- Input following payload in $config field:
{"__proto__":[], "name": "<script>alert(1)</script>"} - Validate
- XSS triggers
Risk
- Account Hijacking
- Stealing credentials
- Expose sensitive data
Remediation
- JavaScript has a lot of quirks and will have many more, it is generally unsafe to consider that you can “control” an almost fully controlled user input variable. Though, to fix the isObject function it might be better to rely on instanceof JavaScript keyword.
- In all case, use a sanitization library such as DOMPurify before returning/interpreting user controlled input. See [3].
Proof
References
- [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
- [2] https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Operators/instanceof
- [3] https://github.com/cure53/DOMPurify
————– END OF Zomsop82 REPORT ——————