Archived article from 11 November 2012. Its content reflects the state of things at the time. Reconstructed from the web archive of secalert.net - view the original.
Summary. SAP's main website used a client-side tracking script, trackinghelper.js, that read values from window.location and wrote them into the page without escaping. A server-side filter blocked the obvious payloads, but the single quote was the only character encoded on the reflected path, so a crafted value broke out of the onclick string context and executed on every navigation link, including the login button. Reported to SAP in late 2012, fixed, and acknowledged as part of their responsible-disclosure process.
Background
A few weeks ago I took a closer look at the JavaScript files used on the main SAP.com website. While doing so, I noticed that SAP tracked the referrer and click behaviour of its visitors through a script called trackinghelper.js. I reported the full details to the SAP security team, who fixed the issue and, as with two earlier reports, sent a gift as thanks for the responsible disclosure.
The file contained a number of functions that were invoked depending on where the visitor clicked, for example when switching the language, using the navigation, or following SAP on Twitter, Facebook or YouTube. Each of these cases triggered a different function.
Finding the sink
Let's take a look at the relevant part of the file.
...
Line 13: function TrackLink(targetURL, targetLabel, contextVals) {
Line 14: ShowEventInteraction(window.location.href, "CLICK", targetLabel, targetURL, contextVals);
Line 15: }
...
Line 21: function TrackFBRec(targetURL) {
Line 22: ShowEventInteraction(window.location.href, "SHARE", "Facebook Recommend", targetURL, "");
Line 23: ShowEventInteraction(targetURL, "SHARE", "", "Facebook Recommend", "");
Line 24: }
...As you can see, the script read user-supplied values from window.location and inserted them into the generated HTML without any visible escaping.
Probing the filter
My first step was a quick check with a few common XSS patterns.
http://www.sap.com/index.epx?"><script>alert(1234)</script><"This did not work at all. Instead, I was redirected to http://www.sap.com/errorpage.epx and the payload was discarded.
So I tried a simpler payload without any quotes.
http://www.sap.com/index.epx?alert(1234)This time I stayed on index.epx and was not redirected to the error page, which suggested that SAP was applying some kind of filter here.
Let's look at the rendered HTML.
...
Line 180: <li><a href="/country-selector.epx" id="utilitynav-locationselect" title="Change Country/Language" onclick="TrackLink('http://www.sap.com/index.epx?alert(1234)','Select Country','ClickArea=header')">United States<span>(change Country/Language)</span></a></li>
...
Line 184:
<li class="login"><a href="javascript:start_login_session('/profile/slogin.epx?pmelayer=true');" title="Log In To SAP.com" onclick="TrackLink('http://www.sap.com/index.epx?alert(1234)','SAP Login','ClickArea=header');TryLoadZAPtracker('sapcom_login-button'); ">Login</a></li>
...This is promising. The payload became part of the rendered HTML, but it still did not execute, even when a link was clicked, because it was treated as an encapsulated string.
Breaking out of the string
So the next step was to turn the payload into something that would actually execute.
http://www.sap.com/index.epx?');alert(1234);//Looking at the rendered HTML again, the single quote was encoded to ', but that did not matter: the closing parenthesis was not encoded at all.
Line 186: ...onclick="TrackLink('http://www.sap.com/index.epx?');alert(1234);// ...Every navigation link now carried the payload, including the "Login" button, which is a particularly likely target for a victim to click.
Impact and remediation
Because the reflected value landed in an onclick handler on every navigation link, any visitor who followed one of those links, the login button in particular, would run attacker-supplied script in the context of sap.com. The correct fix is to context-encode user-supplied values for the exact sink they land in. A value placed inside a single-quoted JavaScript string must have every character that can terminate or alter that context escaped, not just the quote. Better still, request-derived values should never be concatenated into executable handlers at all.
Timeline
October 26th 2012: analysed the static JavaScript files on the sap.com website
October 27th 2012: reported the findings to secure@sap.com
November 3rd 2012: reported the findings again
November 3rd 2012: SAP security team acknowledged the findings
November 11th 2012: published this write-up after SAP fixed the issue
February 2nd 2013: received a gift from SAP for the responsible disclosure