Skip to content

XSS and content injection when viewing raw XHTML files on IOS devices

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 #2473886 by joaxcar on 2024-04-22, assigned to GitLab Team:

Report | Attachments | How To Reproduce

Report

Summary

Back again to an issue that I thought was fixed. Namely, #1923293 content injection in WebKit browsers. The issue that I previously reported to you here was fixed by GitLab, but it is not sufficient; it seems as if I have now found a bypass.

First of all, I found a bypass to the WebKit CVE-2023-32445, which was the fix for my initial report to Apple. It turns out that they only fixed this issue for svg and xml files. Again, as I explained in my last report, this could be intentional as the HTML specs do allow browsers to ignore content-type. But I do believe they will fix this one as well, eventually. Still, I think fixing the bypass on the GitLab's side could be a good thing, as similar issues might pop up.

The issue

When visiting a site with content-type: text/plain on IOS any browser on IOS will sniff the file content instead of adhering to the content-type in these two scenarios

  1. The URL ends in .xhtml
  2. The URL does not end in a file type, but the Content-Disposition: inline; filename="name.xhtml"; header contains the filename with .xhtml

This behavior exists on two places on GitLab:

  1. The API, for example, https://gitlab.com/api/v4/snippets/<SNIPPET_ID>/raw that will serve the raw content with these headers (and you can also add arbitrary URL endings on this one like https://gitlab.com/api/v4/snippets/<SNIPPET_ID>/raw.xhtml)
  2. The raw pages like https://gitlab.com/-/snippets/<SNIPPET_ID>/raw/main/test.xhtml (this is where the old fix existed, which I can now bypass)

When visiting https://gitlab.com/-/snippets/<SNIPPET_ID>/raw/main/test.xhtml we get either

Content-Disposition: inline  
Content-Type: text/plain; charset=utf-8  

if the file is deemed "safe" and this if the file is deemed "unsafe" (this was the fix for the previous report)

Content-Disposition: inline; filename=blob  
Content-Type: text/plain; charset=utf-8  

Notice the filename=blob part, which will force WebKit not to sniff the `content-type' as it has no file ending.

I don't know how the check for "unsafe" works but it will mark a file containing <!DOCTYPE html> in the first line as "unsafe". Hovewer by altering this line to this

<!DOCTYPE html  
>

(adding a newline) the file will be considered "safe" again and have disposition inline.

Using this bypass together with the WebKit bypass, we can now again serve HTML from GitLab.com if the victim view a raw file on IOS. (This bypass works on both snippets and raw files in repos https://gitlab.com/GROUPNAME/PROJECTNAME/-/raw/main/test.xhtml)

What is the problem

I know that the severity got pulled down quite a bit last time because it is only working on IOS. I do, however, still believe this is a highly potent attack vector as you can easily target victims' phones by sending links in a medium where you more often open it on the phone. Working myself with GitLab, I know that you often end up viewing links from text messages or emails on the phone.

First of all, on a self hosted instance, this will lead to XSS through a payload like this

<!DOCTYPE html  
>
<x xmlns:x="http://www.w3.org/1999/xhtml">  
<x:script>alert(document.domain);</x:script>  
</x>  

but this will not bypass the CSP on gitlab.com. There is still a CSP bypass for the form-action attribute as it is defined as

form-action 'self' https: http:  

which means that we can send form content to any https domain. The most effective POC on gitlab.com thus becomes a login form injection. As we are on the gitlab.com origin we are allowed to just copy in most of the HTML (including styling) from the real login page and can create an attack that does the following

  1. Show a login prompt (this will show as trusted and on gitlab.com domain which will allow IOS autofill and biometric 2FA)
  2. Catch the credentials and redirect the user to a copy of gitlabs 2FA endpoint. Again IOS will show this as running on gitlab.com
  3. Catch the 2FA code and gain full control over the victims account, bypassing 2FA

See this video for an example

HTML_INJECTION.mov

Some details zoomin:

detail1.png

detail2.png

detail3.png

Even if this is not a full "XSS CSP bypass" I do think that it should qualify for the higher impact on confidentiality and integrity.

Remediation

I do think that Apple will fix this, but have not gotten any response yet. Last time, it took them six months to fix, and it will still leave users who have not upgraded directly vulnerable to attacks for longer than that. For this reason I think that GitLab can benefit from fixing the bypass on your side. The fix is the same as last time. Putting Content-Disposition: inline; filename=blob as the header will remove the injection.

As mentioned, this is already in place but bypassable using new line in DOCTYPE

Steps to reproduce

  1. Create a new snippet https://gitlab.com/-/snippets/new
  2. Add a file called test.xthml in the snippet and add this content
<?xml version="1.0" encoding="UTF-8"?>  
<x xmlns:x="http://www.w3.org/1999/xhtml">  
<x:script>alert(document.domain);</x:script>  
<x:iframe src="https://example.com"></x:iframe>  
</x>  
  1. Create the snippet
  2. Visit the snippet on an IOS device and click "view RAW". Or use a link like this https://gitlab.com/-/snippets/SNIPPET_ID/raw/main/test.xhtml
  3. If you are on self-hosted without CSP, you should get a popup. If on gitlab.com you only see the iframe render, but this proves HTML injection

Impact

XSS on self-hosted instances and HTML with form injection on gitlab.com. leading to account takeover (bypassing 2fa)

Examples

If you want to test the whole POC payload here are the two snippet files needed and the corresponding HTML files you need to host on the catch server:

test.xhtml

code.xhtml

login.html

code.html

What is the current bug behavior?

GitLab fail to put xhtml files in "unsafe" mode when viewed as RAW when doctype contains newline

What is the expected correct behavior?

GitLab should not allow to bypass the "unsafe" mode as it can still lead to issues

Output of checks

This bug happens on GitLab.com

Impact

XSS on self-hosted instances and HTML with form injection on gitlab.com. leading to account takeover (bypassing 2fa)

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

How To Reproduce

Please add reproducibility information to this section: