Reflected XSS with AngularJS sandbox escape and CSP

Description

The website in this lab uses CSP and AngularJS.

Since Chrome 109 the path property has been removed. The workaround is to use composedPath() instead. That goes for Firefox as well.

According to MDN, all major browsers support composedPath as of January 2023. Chrome (and other Chromium-based browsers) supported both path (it was a Chrome innovation) and composedPath until v109 when path was removed. (The obsolete browsers IE11 and Legacy Edge didn’t support either of them).

Reproduction and proof of concept

  1. Go to the exploit server and paste the following code, replacing 0a5a009b039d93c0c0374b3300ea00c7 with your lab ID:

<script>
location='https://0a5a009b039d93c0c0374b3300ea00c7.web-security-academy.net/?search=<input id=x ng-focus=$event.composedPath()|orderBy:'(z=alert)(document.cookie)'>#x';
</script>

URL encoded:

<script>
location='https://0a5a009b039d93c0c0374b3300ea00c7.web-security-academy.net/?search=%3Cinput%20id=x%20ng-focus=$event.composedPath()|orderBy:%27(z=alert)(document.cookie)%27%3E#x';
</script>
  1. Click Store and Deliver exploit to victim.

The exploit uses the ng-focus event in AngularJS to create a focus event that bypasses CSP. It also uses $event, which is an AngularJS variable that references the event object. The path property is specific to Chrome and contains an array of elements that triggered the event. The last element in the array contains the window object.

Normally, | is a bitwise or operation in JavaScript, but in AngularJS it indicates a filter operation, in this case the orderBy filter. The colon signifies an argument that is being sent to the filter. In the argument, instead of calling the alert function directly, we assign it to the variable z. The function will only be called when the orderBy operation reaches the window object in the $event.composedPath() array. This means it can be called in the scope of the window without an explicit reference to the window object, effectively bypassing AngularJS’s window check.