Skip to main content

Unsafe storage to UXSS

Sometimes developers forget that chrome.storage.local is the same for all sites where the content script is loaded.

window.addEventListener("message", (event) => {
if (event.source !== window) return;

if (event.data.action === "setUsername") {
chrome.storage.local.set({ ["Username"]: event.data.value }, () => {
console.log("Storage set successfully");
});
}
});

document.addEventListener("DOMContentLoaded", (event) => {
chrome.storage.local.get("Username", (data) => {
if (data.Username) {
var host = document.createElement('div');
host.innerHTML = `<h1>${data.Username}</h1>`
document.body.appendChild(host);
}
});
});

This is a fairly simple example, but it shows a very important thing. You need to sanitize data inserted into chrome.storage. Because data received from one origin will be applied across all domains. In fact, this allows for achieving UXSS.

Namespace confusion

Usually, extensions are injected into HTML pages, but they can also be injected into SVG files, which can help you achieve UXSS. A simplified real example:

window.addEventListener("message", (event) => {
if (event.source !== window) return;

if (event.data.action === "insertBlogPost") {
chrome.storage.local.set({ ["theme"]: sanitizeHTML(event.data.theme)}, () => {
console.log("Storage set successfully");
});
}
if(event.data.action === "styleActive"){
chrome.storage.local.get("theme", (data) => {
if (data.theme) {
document.activeElement.appendChild(data.theme);
}
});
}
});

In this example, we can sanitize tags in the context of HTML and then switch to an SVG page. Where the tag will be inserted differently:

<style><img src="x" onerror="alert()"></img></style>

After that, the attacker can direct the user to an SVG document on a third-party site, for example, the company's logo. In the SVG context, the <img> tag inside the <style> tag will be applied, and we will get XSS.

You also need to understand that SVG documents, in general, have a number of differences from HTML documents - for example, there is no document.head and document.body, and so on. Perhaps this can help you.