Skip to main content

Exploit addEventListener

Although the content script is isolated, there is an important connection between Isolated and Main Worlds - these are events. Let's imagine that the extension contains the following code in the content script:

const button = document.createElement("button");
button.innerText="APPROVE";
button.id = "approve"
button.addEventListener("click",(event)=>{
console.log("Clicked!");
// ...
})
document.body.appendChild(button);

In fact, the drawback of this approach is that we can dispatch an event from the Main World of our site on this button:

document.getElementById("approve").dispatchEvent(new Event("click"))

Running such code will show you that the registered listener has been executed.

window events

It is important to understand that this also applies to window and document events (except for error events, which are not passed between contexts in Chrome). For example, the extension can register an action in the content script when a key combination is pressed:

    document.addEventListener('keydown', function(event) {
if (event.ctrlKey && event.key === 'o') {
event.preventDefault();
console.log('Ctrl + O was pressed');
//...
}
});

In this case, you can simply run:

const event = new KeyboardEvent("keydown",{key: 'o',ctrlKey: true})
document.dispatchEvent(event)

It is also important to understand that many text values in events are not filtered, meaning we can safely trigger a key press with the name __proto__

new KeyboardEvent("keydown",{key: '__proto__',ctrlKey: true})

How to Fix

If you are not familiar with client-side security, you might think that fixing this bug is quite complex. However, to fix it, you just need to use isTrusted:

The isTrusted read-only property of the Event interface is a boolean value that is true when the event was generated by the user agent (including via user actions and programmatic methods such as HTMLElement.focus())

So a valid check should look like this:

    document.addEventListener('keydown', function(event) {
if (event.isTrusted && event.ctrlKey && event.key === 'o') {
event.preventDefault();
console.log('Ctrl + O was pressed');
//...
}
});