Reflected DOM XSS at ServiceNow - CVE-2022-39048

Hey everyone, I hope you all are doing good. This is a blog post of my another finding.

I started doing more research on ServiceNow after finding CVE-2022-38463. After spending some time at recon, I found an endpoint - ‘/assessment_redirect.do’ which has a parameter ‘sysparm_survey_url’ and value was reflecting in response in JavaScript.

image

We have this JavaScript code:

<script eval="true">

// For security issue, we need to control the domain and redirect to "assessment_take2.do" ui page. If url is trying to redirect to external domain, we will force to redirect to "assessment_list.do" page.
var unwrapped_url = "VALUE-OF-sysparm_survey_url";
var urlWithoutProtocol = unwrapped_url.replace(/(^\w+:|^)\/\//, '');// remove url protocols like "https://, http://"
var redirect_page = extractHostname(urlWithoutProtocol);
if (redirect_page === 'assessment_take2.do')
	document.location.href = urlWithoutProtocol;
else
	document.location.href = "assessment_list.do";

</script>

In above code snippet, value of ‘sysparm_survey_url’ parameter is reflecting in variable : unwrapped_url and then it is removing the ‘http://’ or ‘https://’ using regex:

unwrapped_url.replace(/(^\w+:|^)\/\//, '')

Then it is using a function ‘extractHostname()’ and this will split the value of unwrapped_url at ‘/’ and then return the value.

function extractHostname(url) {
    var hostname;
    //find & remove protocol (http, ftp, etc.) and get hostname

    if (url.indexOf("//") > -1)
        hostname = url.split('/')[2];
    else
        hostname = url.split('/')[0];

    //find & remove port number
    hostname = hostname.split(':')[0];
    //find & remove "?"
    hostname = hostname.split('?')[0];

    return hostname;
}

here this function is checking if url has ‘//’ then split the url and return value at 2nd position starting from 0 else it will split and return the value at 0 position.

For example: If url is ‘aman//rawat’ then it will return ‘rawat’ and if url is ‘aman/rawat’ then it will return ‘aman’

then it is checking the value of ‘redirect_page’ variable. If value of ‘redirect_page’ is equal to ‘assessment_take2.do’ then it will redirect user to value of ‘sysparm_survey_url’

var redirect_page = extractHostname(urlWithoutProtocol);
if (redirect_page === 'assessment_take2.do')
	document.location.href = urlWithoutProtocol;
else
	document.location.href = "assessment_list.do";

redirecting to ‘urlWithoutProtocol’ using document.location.href makes this vulnerable to XSS because we created a payload to bypass the check in if statement and then we can get the XSS.

Payload: 'javascript:alert()//assessment_take2.do'

So, we give the above payload in parameter: ‘sysparm_survey_url’ and then it will bypass the check where ‘redirect_page’ variable must be equal to ‘assessment_take2.do’ because of extractHostname() function, value of ‘redirect_page’ will be ‘assessment_take2.do’.

And then it is redirecting to ‘urlWithoutProtocol’ using ‘document.location.href’ and value of ‘urlWithoutProtocol’ is ‘javascript:alert()//assessment_take2.do’ where ‘assessment_take2.do’ will be treated as comment and JavaScript will be executed.

image

Update ServiceNow instance to version - Tokyo Patch 1a, Patch 2, San Diego Patch 7b, Patch 9, Rome Patch 10 HotFix 2b, Quebec Patch 10 HotFix 10b.