ArcGIS is a popular geographic information system (GIS) software developed by Esri. It allows users to create, manage, analyze and visualize spatial data on interactive digital maps. ArcGIS offers a suite of products including ArcGIS Desktop, ArcGIS Enterprise, ArcGIS Online and mobile apps. With over 1 million users worldwide, ArcGIS powers mapping and location intelligence across government, business and education sectors.
Given the widespread use of ArcGIS, security is crucial. Vulnerabilities like cross-site scripting can put many users at risk of account takeover, data theft and malware. As an ethical hacker, I aim to help keep products like ArcGIS secure through responsible disclosure of issues.
I discovered a serious stored cross-site scripting vulnerability in ArcGIS that allowed malicious scripts to be executed on user browsers. I responsibly reported this to ArcGIS, who fixed the issue to protect its users.
The vulnerability occurred when creating map layers with malicious titles or data. For example, creating a layer with the following title would result in a JavaScript alert box pop up on page load:
<IMG src="#" STYLE="'onerror=alert('XSS!');'">
Finding the vulnerability
Let’s go through the process of crafting an input payload that would trigger the XSS vulnerability.
As a first step to verify if a website is vulnerable to XSS we can test passing the following simple script as the name of a map layer, and then we open the map view and verify if an alert pops up in the browser:
<script>alert('XSS!')</script>
Testing the above payload with ArcGIS I noticed that no alert would come up and the whole script was deleted from the generated HTML that is sent to the client’s browser. While an hacker may see this as bad news in the sense that the basic XSS test didn’t succeed, we can also assume that some sort of input sanitization is happening on the server before storing the layer names in the database, and this actually means that there might be a chance that this sanitization process does not correctly handle all scenarios, therefore opening a potential attack vector.
So I started running some tests. The XSS filter in place was able to block some obvious attempts like:
User input | Result after sanitization |
---|---|
|
(empty) |
|
|
|
|
Let’s draw some conclusions from the tests listed above to better understand the behavior of the sanitization process:
-
Linking stylesheets results in the HTML tag to be fully removed.
-
Tables and images are allowed but some attributes are removed to prevent XSS.
Moving on with the exploration with more complex payloads. This time let’s include a new column explaining how the browser applies syntax correction techniques to convert the invalid HTML resulting from the sanitization process to valid HTML.
User input | Result after server sanitization | Browser HTML after syntax correction |
---|---|---|
|
|
|
|
|
|
What a mess 😵💫! But here are some facts that can be retained from these attempts:
-
There’s a special rule to block us from using the
javascript:
prefix in an attempt to run javascript code. In fact this wouldn’t work anyway becausejavascript:
is not a valid scheme to be used as image source, but it seems that there’s a list of hardcoded expressions that are being explicitly blocked, or in this case converted to the character#
. -
The system in place provides special treatment for single quotes when they are present inside attribute values by converting them to double quotes. For example:
<img src="foo’bar"/>
would convert to<img src="foo"bar""/>
-
Afterwards a similar behavior is applied for double quotes when they are preceded by a single quote, in which case they get converted to single quotes.
Enough experimenting! Now for the fun part, here comes the payload that allowed me to bypass the sanitization filters. To better differentiate
User input | Result after server sanitization | Browser HTML after syntax correction |
---|---|---|
|
|
|
This payload uses a well-known XSS technique where we pass a source URL with an invalid (placeholder) value, causing the onerror
handler to be executed. So whatever script we include inside the onerror
handler will be ran by the user browser when it tries to load the image. Notice that by including a single quote right before the onerror
handler we successfully trick the server sanitization logic so that it closes the style
attribute and refrains from further converting single quotes to double quotes in our actual javascript code. And in the final part of the payload we introduce double quotes that will be converted to single quotes and generate an innocuous empty string - this process causes the browser to perform syntax correction without breaking our XSS payload.
When a user clicked the "Show Table" button for a layer containing these titles, the JavaScript would execute in their browser. I confirmed the issue could also be exploited by injecting the payload into the layer’s data rows.
The exploit worked in components like the Map Viewer and Dashboards where users directly interact with data layers.
Impact
But what could be the real impact of this vulnerability? It’s hard to estimate, but let me provide an example.
In the past I have written about a few security flaws present in COVID-19 monitoring dashboards. If you combine these security flaws with the XSS vulnerability from this article you can effectively cause some damage. In fact, during the COVID-19 pandemic, these dashboards were embedded (via iframes) in a few trustable portuguese websites, such as governmental websites and Sapo news aggregator. Now imagine a malicious hacker crafts this pop-up message:
Aftermath
After disclosing this to ArcGIS, they were able to implement filters to sanitize layer titles and data before rendering. This prevented the stored XSS vulnerability from being further exploited, and so a patch to address it was released in 2022. Here is the official advisory: