Sometime ago in the past, while fuzzing Zendesk randomly, I came across a very odd kind of sanitization technique in a JSON endpoint and even though this was a black-box approach, I would assume this was what was happening in their back-end server, can you spot the bug?
$sanitized = [];
foreach ($_GET as $k => $v) {
$sanitized[$k] = json_encode(intval($v));
}
$queryarray = array_map(function ($k, $v) {
return $k . ‘=’ . $v;
}, array_keys($sanitized), array_values($sanitized));
$query = implode(‘&’, $queryarray);
echo $query;
I don’t know if you spotted the vulnerability above but it only sanitizes the the parameter value, not the parameter name, which looks like a common mistake to make for developers looking at similar sanitization loops on github. But also very tricky to exploit none the less because of odd json behaviors.
What was more interesting was how the application doesn’t return any Content-Type or X-Content-Options headers, so browsers like Chrome sniff content and that obviously makes cross site scripting attacks possible when they parse HTML tags.
Since Zendesk usually runs on subdomain’s of sensitive domains like support.domain.com, my initial exploitation scenario for this is to escalate this into a host xss.
forexample ?errorCod<scipt>domain=x; will be “errorCod<script>domain”:”x. so you see the problem and why it is tricky to craft a payload out of.
Exploitation
I couldn’t assign anything other than trigger an alert. Since assigning usually required “=” and if zendesk finds one, they will change it to “:” so after closing the script tag, I begun my payload with <svg> because of its XML-ish nature. This means that once we use entities inside an SVG’s <script> element (or any other CDATA element), they will be parsed as if they were used in canonical representation. Therefore, to bypass the filter, or to call document.domain=0 with the equal sign char = encoded, i.e. = or even shorter D;.
Then I used 3 equal signs, one will be stripped, one will be replace by : and one will be left for us. so we came as far as </script><svg><script>/xz==/document.domain by using comments to bypass 3 of the filters. even though the values after = gets entitied, it doesn’t matter since we are in <svg><script> tag and this would mean anything in it will act like mXSS (mutation xss) even when its entitied.
After that, the next payload will look something like:
</script><svg><script>/*xz":"=*/document.domain='hackerone.com'//
the comment in the end is there because we can’t use semicollon ; to finish the document.domain line and anything following it will be commented out.
Then all whats left to do is create another json parameter name with values error_titlez</script> and close our tag. then we shall have “hackerone.com” as our document.domain. and we can request/recieve to the main domain.
Final payload =
</script><svg><script>/*xz":"=*/document.domain='hackerone.com'//","error_title":"","error_titlez</script><script>alert(document.domain)</script>
and we get our domain set to the main host, and we can do a lot more attacks starting there.
P.S: Jack Whitton did a nice post on how he turned a subdomain XSS on https://photos.facebook.com to one on https://www.facebook.com — its a good read.