“MyFlaw” — Cross Platform 0-Day RCE Vulnerability Discovered in Opera’s Browsers

“MyFlaw” — Cross Platform 0-Day RCE Vulnerability Discovered in Opera’s Browser

By Oleg Zaytsev (Guardio Labs)

The Guardio Labs research team uncovered a critical zero-day vulnerability in the popular Opera web browser family. This vulnerability allowed attackers to execute malicious files on Windows or MacOS systems using a specially crafted browser extension. This discovery not only highlights the vulnerability within Opera but also reflects a broader challenge in modern browser security.

Our proactive disclosure to Opera’s team and their swift response exemplifies the critical collaboration between security researchers and browser developers in protecting users. This write-up aims to shed light on the intricate details of the research process and discovered vulnerability as well as the ongoing efforts to safeguard digital experiences against evolving cyber threats.

Vulnerability ID Card
Exploit POC Extension — From installation to code execution (<1 sec)

From Opera’s My-Flow To The RCE Flaw

Opera’s My Flow feature stands out for its seamless notes and file sharing between your desktop and mobile devices, all through the Opera browser. Simply scan a QR code with Opera’s mobile app, and you’re greeted with a chat-like interface for exchanging messages and files.

Typical My-Flow activity taking notes and opening the attached file sent from Mobile

However, from a cybersecurity perspective, one aspect is notably concerning. The chat-like interface adds an “OPEN” link to any message with an attached file, allowing users to immediately execute the file from the web interface. This indicates that the webpage context can somehow interact with a system API and execute a file from the file system, outside the browser’s usual confines, with no sandbox, no limits.

This feature, though convenient, reveals high potential security risks, leading our team to investigate further. In our vulnerability research, we identify high-risk vectors, like the above, and thoroughly examine the architecture, development, and security protocols involved, aiming to pinpoint security gaps or logic errors that could be exploited — and indeed we discovered a significant vulnerability.

The Hidden Built-In Extension

Like many of today’s popular browsers, Opera is built on the Chromium open-source project. It shares much of its core code, capabilities, and design with Chromium. To differentiate itself and offer unique features, Opera taps into Chromium’s built-in customization options, one of which includes the concept of built-in browser extensions.

Similar to extensions you install from browser stores, these built-in ones enhance functionality and add new features. However, a key difference is that built-in extensions are pre-installed in the browser, can’t be disabled or controlled, and can possess broader capabilities and permissions.

For the curious, a glimpse into these extensions is possible through the browser’s dev tools, where you can inspect the inner workings of your browser — just browse to opera://inspect and select “Extensions”:

The built-in “Opera Touch Background” extension in the inspect window

The special My Flow feature is made possible using the Opera Touch Background extension, which is in charge of all the inner workings.

As with any other extension, it introduces a manifest file that declares all permissions and capabilities. In it, we should specifically note the externally_connectable declaration:

"externally_connectable": {
"matches": [
"https://*.flow.opera.com/*",
"https://*.flow.op-test.net/*"
]
}

The above means that only web resources under the declared domains can communicate with this extension. This is done via the chrome.runtime.connect API, giving the webpage access to all declared handlers in that powerful extension.

Checking the listeners on the extension code itself reveals some of the special capabilities My Flow can access:

port.onMessage.addListener(data => {
switch (data.type) {
case 'GET_PAIRING_TOKEN':
wrapResponse(this.getPairingToken(data.value), data);
break;

case 'GET_DEVICES':
wrapResponse(this.getConnectedDevices(true), data);
break;

...

case 'OPEN_FILE':
wrapResponse(this.openFile(data.localFileName), data);
break;
case 'SEND_FILE':
wrapResponse(
this.sendFile(
data.name, data.content, data.file_type, data.preview,
data.messageId, data),
data);
break;
case 'DOWNLOAD_FILE':
wrapResponse(
this.downloadFile(
data.url, data.name, data.iv, data.messageId, data),
data);
break;
...
}
});

Looking deeper into the OPEN_FILE code we see it eventually access a native private API under the core object of the browser: opr.operaTouchPrivate.openFile(String filename).
The same goes for the DOWNLOAD_FILE that creates a file in a specific target on the local OS under ~/Downloads/MyFlow/.

Architecture — From Touch app to My-Flow web scripts and embedded high-permissions extension

Think about the possibilities — if we find a way to call those handlers, we can eventually download any kind of payload and execute it without user intervention on our targeted system. This is a powerful attack vector with dramatic malicious potential!

To do so, we first need to find a way to run our own controlled code from the context of those declared domains under opera.com.

Exploiting Opera-Controlled Domain Permissions

OK, so only resources under the Opera-controlled domains can access the DOWNLOAD_FILE or OPEN_FILE handlers we are targeting here. This is indeed an important security measure.

The first thing that comes to our heads is XSS (Cross Site Scripting) —Injecting arbitrary javascript code to a webpage loaded from a relevant domain, by manipulating different inputs like URI params or POST data that might trigger a code vulnerability. In case we find something like this, we just need to craft the relevant URL and make the victim click on it to have it run our own crafted code under the opera domain. Indeed, a similar flow to this was once already detected and disclosed to Opera more than 2 years ago. So, we must assume the page is now well-coded and immune to these kinds of vulnerabilities.

Injecting Code Via Extension Manipulation

Another, more straightforward way to inject code, is using Extensions. Imagine a normal extension with generic permissions, just like any other Ad-Blocker and similar tools millions of users install every day. Once installed on the browser, the extension can inject code in several methods into targeted URLs — in this case, any page loaded from flow.opera.com.

The first option to try is the extension API call chrome.tabs.executeScript which injects and executes a script into the main webpage context. This is prevented in this domain due to a specific security policy introduced by Opera directly in their browser’s code. This is similar to Google’s Chrome preventing extensions from executing code on Chrome Store pages. So far — good job, Opera.

Another option is the WebRequest/DeclerativeNetRequest APIs that are allowed on our targeted domain. With these permissions, commonly used by AdBlocker Extension, one can alter a request the page makes for a specific resource and have it fetch a different one. In our case, the page flow.opera.com requests a javascript file from /ext/v1/scripts.js, so we can switch it to fetch our own crafted file with a simple rule.

Well, this time we meet our next obstacle in the form of CSP:

The error log printed due to CSP blocking the injected script from loading

CSP (Content Security Policy) is a web security standard used to prevent XSS, clickjacking, and other code injection attacks by specifying which content sources are trusted. In this case, our script is not served by an approved source, thus blocked from execution! This can be seen in the meta tag stating this policy on the original Opera-controlled webapp page:

<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://flow-dev.operacdn.com https://flow.operacdn.com">

Adding to the above, the script tag itself includes an extra level of security in the form of SRI (Sub-Resource Integrity):

<script src="https://flow.operacdn.com/ext/v1/scripts-1673951285900.js" defer="" integrity="sha256-0vAferkk3jK3H8s/xAEmiM1WNl6rUWIr+bEExaTCcAA=" crossorigin="anonymous"></script>

The above integrity attribute makes sure the loaded script has the stated hash value. There is no way to bypass this check unless we just happen to have a quantum computer lying around in the basement…
So even if we managed to change the script content, it would have failed to load and execute on the browser side due to having a different hash value. Touché Opera!

Overcoming CSP/SRI In a Surprising Way

Well, there must be another way…

Domains in the *.flow.opera.com family are used as production apps for several of Opera’s products, different versions, and possibly even beta/dev versions. Might these give us some more exploit opportunities?

Doing a quick search for historical scans under this domain family with urlscan.io gave us interesting results! urlscan.io is a security tool that analyzes and provides detailed reports about the content and safety of URLs by scanning and inspecting web pages. As such, it also gives us a glimpse into the history of each domain's usage. In this case — some long-forgotten random HTML pages sitting under those domains and are still available even years after!

Among those found, we see many different versions of that same My Flow landing page — including this specific 2+ years old one:

A quick search for relevant page scans from the target host with URLscan — urlscan.io

The page itself looks quite the same as the current one in production, but changes lie under the hood: Not only that it lack the CSP meta tag, but it also holds a script tag calling for a javascript file without any integrity check. No CSP, no SRI — looks too good to be true:

<head>
<!-- disable Service Worker <link rel="manifest" href="manifest.json"> -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content=".....">
<!-- NO CSP tag! -->
<link rel="icon" href="https://flow.operacdn.com/ext/v1/img/....">
<title>My Flow</title>
<link rel="stylesheet" href="https://flow.operacdn.com/ext/v1/....">
</head>
..
..
<!-- NO SRI attribute! -->
<script src="https://flow.operacdn.com/ext/v1/scripts-1633701575733.js" defer=""></script>

This is exactly what an attacker needs — an unsafe, forgotten, vulnerable to code injection asset, and most importantly — has access to (very) high permission native browser API!

Simulating “My Flow” to Send Malicious Payload

Long story short, we can now create a simple extension proof of concept that, with some simple steps, will download a file to the victim’s computer and execute it.

There is only one missing building block here, which is the payload itself. How do we send a file to the infected browser? Well, this time it’s a case of pure reversing and automation. We realized that the browser itself (e.g. our Extension code) can simulate the same activity of the My Flow application as they both use the same endpoint under flow.opera.com.

First, the extension creates a new device instance by calling flow.opera.com/v1/devices with some fake mobile device details as well as a public key that will be used to encrypt the file payload. In return, we get a DEVICE ID as well as a TOKEN. Next, we request the pairing token to the browser using the GET_PAIRING_TOKEN handler. This returns the QR code value that is scanned by the application on your mobile phone to pair it with the browser.

The QR Code and its value used to pair the Opera Touch app with the Browser

Now we send the above data to https://flow.opera.com/v1/connect-devices to get the fake device connected and paired with our browser.

The next obvious step would have been to simulate a file transfer from the fake mobile device to the browser, initiate its download, and file open operation. Opera uses encryption on files we send between our devices (as those are stored on their servers to allow this activity) thus we should first encrypt our malicious payload using the relevant keys exchanged earlier.

Even though, we realized a better way to go. The SEND_FILE handler used by the browser to send files to the mobile device has one interesting side effect — it saves a copy of the sent file under the same folder to which MyFlow also downloads files. This handler can also get the file content as a blob — thus we just found another quick way to generate any malicious file directly from our extension to the host filesystem!

Exploiting SEND_FILE command to generate any local file to later be executed

Now that we have our file in the relevant folder on the system, we can trigger the OPEN_FILE call and we are done. The file is executed from the local storage of the infected browser’s OS.

One Final Catch — From Zero to One Click

Now we meet another last obstacle in the form of a permissions block to the FILE_OPEN call. Seems like the call to this type of action must come from a specific context, as we get this error:

The error code presented when calling OPEN_FILE directly

In our exploration of the My Flow API, we observed that triggering the OPEN_FILE operation requires a click event, and indeed, this approach was successful. However, this shifts the attack dynamics from a zero-click to a one-click scenario. While a one-click attack is less potent than a zero-click, it’s surprisingly simple to engineer.

We just need the user to click anywhere on the screen. But hey, we already had the user install an extension (under the guise of it being an exceptional ad blocker or similar enticement), and with any new extensions installed, there is the “Thank you for installing” page we are all so used to:

Dynamically changing the original page to mimic an Extension install thank you page

We already injected code to this new tab, that abuses the forgotten asset from the flow.opera.com domain. With that, we can quickly inject some simple code to also dynamically deface this page to resemble a simple Thank you page — simply prompting the user to click anywhere to begin. It’s a straightforward yet effective method.

Full Scope Exploit Extension POC

To demonstrate a complete Proof of Concept (POC) attack flow, let’s consider how an attacker could exploit this newfound vulnerability in Opera, potentially installing malicious payloads on numerous users’ computers worldwide.

The attack begins with a browser extension, cunningly disguised as an AdBlocker. This guise is not only appealing for widespread daily installation but also grants the necessary permissions for exploitation — specifically, the DeclerativeNetRequest. This permission allows us to substitute the original script request with our payload, camouflaging it among a multitude of other rules that perform standard ad-blocking functions.

Once the user installs this extension, the OnInstalled handler immediately opens a vulnerable page from the flow.opera.com domain in a new tab. This action initiates the malicious phase of the exploit:

The Exploit — installing a malicious extension that auto-triggers the attack chain to code execution

Our crafted JavaScript code is then injected into this page, subtly altering its appearance and enabling interaction with the Opera Touch Extension. This interaction is designed to simulate a mobile device pairing with the browser, transferring a malicious file, and executing it, completing the attack flow in less than a second.

Exploit POC Extension — From installation to code execution (<1 sec)

As demonstrated in the POC run, the exploit can execute a file on the target operating system, whether it’s Windows or MacOS, in just a second. This rapid execution underscores the exploit’s alarming potential for malicious use.

Disclosure And Working With Opera

Immediately after discovering this vulnerability, we reached out to Opera’s team to fully disclose the issue and shared all our findings. At the time, there was no evidence of active exploitation of this vulnerability in the wild, but we couldn’t be certain. Therefore, our highest priority was to fully inform Opera and assist in any way possible to rectify the issue.

The response from Opera’s engineering team was swift and effective. Within just five days of our disclosure, they implemented the most critical part of the fix by removing problematic and insecure assets from their servers.

Remediation And Final Thoughts

While there are currently no known vulnerable assets on Opera’s production servers, the potential for such issues to reappear in the future due to human error or new code updates susceptible to XSS remains. This highlights the need for further internal design changes at Opera, as well as the Chromium infrastructure in general. As an example, generally disabling 3rd party extension permissions on dedicated production domains – just like being done on Chrome’s web store.

It should be noted that Opera responded quickly and efficiently cooperated throughout the process. Following is Opera’s official statement:

As part of our ongoing work with external security researchers, we were alerted to this flaw by the Guardio Labs team in November 2023. Following their findings, our team worked closely with Guardio Labs and moved quickly to address the vulnerability and implement a fix on the server side in only a few days. Specifically, we were alerted on November 17th, and the fix was in place by November 22nd.

Our current structure uses an HTML standard, and is the safest option that does not break key functionality. After Guardio alerted us to this vulnerability, we removed the cause of these issues and we are making sure that similar problems will not appear in the future.

We would like to thank Guardio Labs for their work on uncovering and immediately alerting us to this vulnerability. This collaboration demonstrates how we work together with security experts and researchers around the world to complement our own efforts at maintaining and improving the security of our products and ensuring our users have a safe online experience.

This research sheds light on the vulnerabilities present in modern browsers, emphasizing the multitude of attack vectors that emerge as browsers become more feature-rich and complex. It particularly highlights how extensions, despite operating in sandboxed environments, can still be potent tools for hackers. These extensions can be easily propagated to steal information and, as shown, can even breach the boundaries of the browser itself.

This underscores the ongoing challenge of balancing new functionalities with the imperative of maintaining robust security protocols. At Guardio Labs, our research team remains committed to this endeavor. Alongside the broader cybersecurity community, we are dedicated to identifying such threats proactively, striving to stay one step ahead of malicious actors.

Note on the POC —In the interest of security, we have chosen not to publish the exploit’s Proof of Concept (POC) code. Our decision stems from a concern that the existing architecture remains at high risk for exploitation. We aim to prevent potential misuse by malicious parties, particularly if future updates to the product inadvertently reintroduce vulnerabilities for cross-site scripting (XSS) or extension abuse.

Source: Original Post