Last Updated: 2024-06-22


Browsershot is a popular PHP library developers use to take screenshots of websites or convert webpages to PDFs. It's used in thousands of web projects around the world. But in late 2022, security researchers discovered a serious vulnerability—CVE-2022-41706—that lets hackers steal any file your web server can access. Let's break it down in simple English, show how it works, and give some example code.

What Is Browsershot?

Browsershot (GitHub repo) is a tool that uses Headless Chrome behind the scenes. Web apps feed a URL to Browsershot, it loads the page in Chrome, then makes an image or PDF.

Example usage

use Spatie\Browsershot\Browsershot;

Browsershot::url('https://example.com';)->save('example.png');

The Root Problem

Browsershot expects a URL (http:// or https://), but doesn't check the protocol in the supplied URL. Attackers can use other protocols like file://.

Browsershot Version Affected: 3.57.2  
Vulnerability Type: Local File Disclosure (Improper Input Validation)

How Does It Work?

If a web app uses Browsershot and lets users supply a URL (maybe via a form or API), a hacker can submit file:///etc/passwd (on Linux) or file:///C:/Windows/win.ini (on Windows). Browsershot loads this *local file* in the browser and tries to save a screenshot or PDF. The attacker can then retrieve that output—getting a copy of your server's sensitive files.

Basic Attack Flow

1. Attacker submits a malicious URL using a file:// protocol.

Browsershot loads the *local file* instead of a web page.

3. Output (image, PDF, or even raw HTML) may be saved, returned, or exposed to attacker via web application logic.

Imagine this (dangerous!) PHP code

// Suppose this is your "screenshot API" controller
$url = $_GET['url']; // e.g. user submits <yourdomain>?url=https://example.com

Browsershot::url($url)->save('/tmp/screenshot.png');

header('Content-Type: image/png');
readfile('/tmp/screenshot.png');

An attacker simply visits:  
https://yourwebsite.com/screenshot?url=file:///etc/passwd

Result:
Browsershot opens /etc/passwd locally, makes a "screenshot" of the file (which will look like unformatted text), and your API returns the image containing sensitive file contents.

Let's clarify with a PoC (proof of concept)

use Spatie\Browsershot\Browsershot;

// '$malicious_url' supplied by an attacker:
$malicious_url = 'file:///etc/hosts'; // could be any readable file on your server

Browsershot::url($malicious_url)
    ->save('output.png'); // Attacker gets 'output.png'

// Or, grab the raw HTML (for text files)
$html = Browsershot::url($malicious_url)->bodyHtml();
echo $html;

What Could Be Stolen?

- Linux: /etc/passwd, /var/www/.env (environment configs), database connection info, API keys.

Why This Matters

Browsershot isn't supposed to work with file:// URLs in web-facing apps. This bug lets anyone probe your server and grab files that should always stay private—*just by sending a different protocol*.

How To Protect Your Application

If you're using Browsershot v3.57.2 or earlier:

Update to the latest version (always)

Get the patched version here

Always validate user input

Only allow http:// and https:// URLs.

Sample filter code

function is_valid_http_url($url) {
    $parsed = parse_url($url);
    return in_array($parsed['scheme'], ['http', 'https']);
}

if (!is_valid_http_url($url)) {
    die('Invalid URL');
}

Learning More

- Original advisory (Huntr.io): https://huntr.dev/bounties/290b4f83-cd43-4c44-ac67-15017317a89c/
- NVD entry: https://nvd.nist.gov/vuln/detail/CVE-2022-41706
- Browsershot GitHub Issue: https://github.com/spatie/browsershot/issues/678

Stay updated: Patch vulnerabilities as soon as they're found.

If you use Browsershot in any project, make sure you review and patch your code right now. Seemingly small oversights can open the door for serious data breaches!


_This post was written exclusively for readers looking to understand CVE-2022-41706 in clear, practical terms. Stay safe, code smart!_

Timeline

Published on: 11/25/2022 18:15:00 UTC
Last modified on: 12/01/2022 16:46:00 UTC