Skip to content
New Report: Get your copy of The 2024 State of the Internet Report! | Download Today
Blogs

CVE-2021-22205: It Was A GitLab Smash

Hosts with potentially vulnerable GitLab instances.

Introduction

GitLab is an open-source code repository system for software development, primarily used by large organizations to manage DevOps and other related software projects. GitLab’s primary offering is managed and hosted by the GitLab company itself; they also offer a self-hosted version of their product for both communities and enterprises with varying levels of support.

On May 07, 2021, a researcher named “vakzz” (William Bowling) reported a nasty bug that targeted the GitLab enterprise and community software via the HackerOne bug bounty program. GitLab engineers promptly fixed the issue, and the vulnerability seemed to have gone under the radar for a few months. That is until a user on Twitter reported that a botnet of thousands of compromised GitLab instances started a massive DDoS campaign, generating over one terabit of data per second (that’s 75,000 ZD/m (Zip Disks per minute)).

What is the Issue?

According to the original report, images uploaded to the GitLab server get passed through a Perl tool called ExifTool to strip out useless metadata. Unfortunately, ExifTool also supports a DjVu file format, which will process user-defined S-Expressions, a LISP-like evaluation language found in specific sections of the DjVu file. A related bug, CVE-2021-22204, was announced back in April 2021, wherein the same researcher showed that the ExifTool DjVu did not properly sanitize inputs before calling the Perl function eval(), allowing for user-controlled, arbitrary Perl code execution (Remote Code Execution). William Bowling wrote an excellent rundown on his blog with more details on the research that led to these findings.

At the time of writing, Censys found 20,524 internet-facing hosts running 20,565 services identified as a vulnerable version of the GitLab server software. Most of the hosts ran version 11.11 with over 1,400 occurrences, closely followed by version 13.1 and 12.1 with around 1,300 hosts.

While the GitLab server did not have any feature that allowed us to determine the specific version of the running software, we came up with an effective method of identification and fingerprinting using (literal) artifacts of GitLab’s build pipeline.

GitLab will automatically kick off a set of jobs that compiles and tests the GitLab software whenever a code commit is tagged. One part of this build process is a job called “compile-production-assets” found in the directory “.gitlab/ci/frontend.gitlab-ci.yml” whose goal is to aggregate and compile the product’s public assets (images and CSS files) into an output folder. Since these files are accessible to the public without authentication and referenced in an exposed GitLab website index (thus searchable by Censys), they made perfect candidates for a bit of ad-hoc fingerprinting.

Many of these generated filenames hardly ever change (such as the logo), but some do. For example, “public/assets/application.css” seemed to have a different generated filename for every minor release. Additionally, the names of these files will include a 64-byte ASCII-HEX encoded string appended to the original filename, giving us the ability to map these filenames back to the build job and software version that generated it. So our goal was to download every tagged build (with a vulnerable version) of the GitLab software, then note the name of the generated filename with the prefix of “public/assets/application-” and the extension of “css”, and associate that filename with the tag of the build. Not perfect, but it will do.

After downloading every vulnerable image from docker.io (all 247 major, minor, and release candidates), we ran a small shell script that generated a table of tag-names to unique file-names that resulted in 44 unique filename hashes:

#!/usr/bin/env bash
 
assetdir="/opt/gitlab/embedded/service/gitlab-rails/public/assets"
tags=$(cat ./vulnerable_tags.txt)
 
for tag in $tags; do
    filename=$(docker run --rm -it --entrypoint "" gitlab/gitlab-ce:$tag ls $assetdir|egrep '^application-.*\.css' | grep -v \.gz)
    echo $tag,$filename
done

After a bit of manual data pruning, we started the search off by generating a BigQuery SQL statement to find all running GitLab services along with the specific filename we were looking for. The results of which were stored in a temporary database table for further analysis. (A feature which Censys enterprise customers have access to):

WITH
  results AS (
  SELECT
    DISTINCT host_identifier.ipv4 AS host,
    autonomous_system.asn AS asn,
    autonomous_system.name AS asn_name,
    location.country_code AS cc,
    location.coordinates.latitude AS lat,
    location.coordinates.longitude AS long,
    REGEXP_EXTRACT(SAFE_CONVERT_BYTES_TO_STRING(svc.http.response.body),
        ".*(/assets/application-[a-f0-9]{64}.css).*") AS version,
  FROM
    `censys-io.universal_internet_dataset.universal_internet_dataset`
  JOIN
    UNNEST(services) svc
  JOIN
    UNNEST(svc.http.response.html_tags) x
  WHERE
    DATE(snapshot_date) = "2021-11-03"
    AND SAFE_CONVERT_BYTES_TO_STRING(svc.http.response.body)LIKE '%/assets/application-%.css%'
    AND SAFE_CONVERT_BYTES_TO_STRING(x) LIKE '%/assets/gitlab_logo-%'
  ORDER BY
    version DESC )
SELECT
  *
FROM
  results;

The next step was to take the data generated by the above query and map the found filenames to their respective version as seen in the docker images, resulting in the following:

Filename Mapped Version Host Count
/assets/application-5440e2dd89d3c803295cc924699c93eb762e75d42178eb3fe8b42a5093075c71.css 11.11 1448
/assets/application-02aa9533ec4957bb01d206d6eaa51d762c7b7396362f0f7a3b5fb4dd6088745b.css 13.10 1416
/assets/application-450cbe5102fb0f634c533051d2631578c8a6bae2c4ef1c2e50d4bfd090ce3b54.css 12.10 1322
/assets/application-45b2cf643afd34888294a073bf55717ea00860d6a1dca3d301ded1d0040cac44.css 12.9 1277
/assets/application-d161b6e25db66456f8e0603de5132d1ff90f9388d0a0305d2d073a67fd229ddb.css 13.9 1157
/assets/application-52560ba2603619d2ff1447002a60dcb62c7c957451fb820f1894e1ce7c23821c.css 13.8 1000
/assets/application-051048a171ccf14f73419f46d3bd8204aa3ed585a72924faea0192f53d42cfce.css 13.6 985
/assets/application-a0c92bafde7d93e87af3bc2797125cba613018240a9f5305ff949be8a1b16528.css 13.7 833
/assets/application-c8d8d30d89b00098edab024579a3f3c0df2613a29ebcd57cdb9a9062675558e4.css 13.0 736
/assets/application-4abc4e078df94075056919bd59aed6e7a0f95067039a8339b8f614924d8cb160.css 13.1 700
/assets/application-def1880ada798c68ee010ba2193f53a2c65a8981871a634ae7e18ccdcd503fa3.css 12.3 699
/assets/application-969119f639d0837f445a10ced20d3a82d2ea69d682a4e74f39a48a4e7b443d5e.css 13.4 639
/assets/application-455d114267e5992b858fb725de1c1ddb83862890fe54436ffea5ff2d2f72edc8.css 13.3 605
/assets/application-3407a4fd892e9d5024f3096605eb1e25cad75a8bf847d26740a1e6a77e45b087.css 12.4 594
/assets/application-aeddf31361633b3d1196c6483f25c484855e0f243e7f7e62686a4de9e10ec03b.css 12.6 567
/assets/application-bec9544b57b8b2b515e855779735ad31c3eacf65d615b4bfbd574549735111e7.css 12.7 557
/assets/application-a9308f85e95b00007892d451fd9f6beabcd8792b4c5f8cd7524ba7e941d479c9.css 13.2 549
/assets/application-77566acc818458515231d0a82c131a42890d771ea998b9f578dc38e0eb7e517f.css 12.0 509
/assets/application-3cbf1ae156fa85f16d4ca01321e0965db8cfb9239404aaf52c3cebfc5b4493fb.css 11.9 458
/assets/application-bf1ba5d5d3395adc5bad6f17cc3cb21b3fb29d3e3471a5b260e0bc5ec7a57bc4.css 13.5.0-ce.0 436
/assets/application-d56f0577fbbbd6f159e9be00b274270cb25b60a7809871a6a572783b533f5a3c.css 12.2 409
/assets/application-38981e26a24308976f3a29d6e5e2beef57c7acda3ad0d5e7f6f149d58fd09d3d.css 11.10.5-ce.0 406
/assets/application-4a081f9e3a60a0e580cad484d66fbf5a1505ad313280e96728729069f87f856e.css 12.8.0-ce.0 371
/assets/application-dc6b3e9c0fad345e7c45a569f4c34c3e94730c33743ae8ca055aa6669ad6ac56.css 12.8 367
/assets/application-d812b9bf6957fafe35951054b9efc5be6b10c204c127aa5a048506218c34e40f.css 12.5 359
/assets/application-78812856e55613c6803ecb31cc1864b7555bf7f0126d1dfa6f37376d37d3aeab.css 12.1 325
/assets/application-93ebf32a4bd988b808c2329308847edd77e752b38becc995970079a6d586c39b.css 11.10 300
/assets/application-73a21594461cbc9a2fb00fc6f94aec1a33ccf435a7d008d764ddd0482e08fc8d.css 12.5.0-ce.0 255
/assets/application-340c31a75c5150c5e501ec143849adbed26fed0da5a5ee8c60fb928009ea3b86.css 12.1.0-ce.0 250
/assets/application-be9a23d3021354ec649bc823b23eab01ed235a4eb730fd2f4f7cdb2a6dee453a.css 13.5 233
/assets/application-34031b465d912c7d03e815c7cfaff77a3fa7a9c84671bb663026d36b1acd3f86.css 11.11.0-rc4 224
/assets/application-67ac5da9c95d82e894c9efe975335f9e8bdae64967f33652cd9a97b5449216d2.css 11.1 171
/assets/application-7f1c7b2bfaa6152740d453804e7aa380077636cad101005ed85e70990ec20ec5.css 13.3.8-ce.0 130
/assets/application-f154ef27cf0f1383ba4ca59531058312b44c84d40938bc8758827023db472812.css 13.2.0-ce.0 61
/assets/application-f9ab217549b223c55fa310f2007a8f5685f9596c579f5c5526e7dcb204ba0e11.css 13.4.0-ce.0 60
/assets/application-ec9dfedd7bd44754668b208858a31b83489d5474f7606294f6cc0128bb218c6d.css 13.1.0-ce.0 60
/assets/application-292ca64c0c109481b0855aea6b883a588bd293c6807e9493fc3af5a16f37f369.css 11.9.0 46
/assets/application-39fdbd63424a09b5b065a6cc60c9267d3f49950bf1f1a7fd276fe1ece4a35c09.css 12.7.0-ce.0 43
/assets/application-9c095c833db4364caae1659f4e4dcb78da3b5ec5e9a507154832126b0fe0f08e.css 11.11.0-rc1.ce.0 6
/assets/application-79837fd1939f90d58cc5a842a81120e8cecbc03484362e88081ebf3b7e3830e9.css 13.7.0-rc3.ce.0 1
/assets/application-2eaf7e76aa55726cc0419f604e58ee73c5578c02c9e21fdbe7ae887925ea92ae.css 11.10.0-rc7.ce.0 1

We can also construct a somewhat verbose Censys search query using the above hashes to view better the hosts matching these conditions by searching for both the gitlab_logo filename and the list of files that match.

While using these exploited hosts for DDoS is terrible by itself, there have also been discussions of other mass-exploitation attacks where random admin users were found. A bigger worry here is the potential for more advanced attacks; For example, an attacker could potentially introduce backdoors and vulnerable functionality into the source code of projects hosted by these services. If this were to happen, even the most securely written code could become an administrative nightmare.

The attack itself is straightforward, and there are readily available exploits making this a severe issue that should be dealt with immediately. Hopefully, the data presented in this post will assist engineers and administrators in identifying and patching vulnerable GitLab instances.

What do I do about it?

References

 

About the Author

Mark Ellzey
Senior Security Researcher All posts by Mark Ellzey
Mark Ellzey is a Senior Security Researcher at Censys. Before his current role, Mark has worked as both a network security engineer and software developer for several internet service providers and financial institutions for over 22 years.
Attack Surface Management Solutions
Learn more