Safeguard
Vulnerability Analysis

GitLab ExifTool RCE (CVE-2021-22205) Explained

An unauthenticated attacker could run code on a GitLab server just by uploading an image. The bug was not in GitLab at all — it was in ExifTool. Here is the full story.

Marcus Chen
Security Researcher
5 min read

CVE-2021-22205 is a textbook example of how a dependency you did not write can hand an attacker complete control of your application. The vulnerability let an unauthenticated attacker execute arbitrary commands on a self-managed GitLab server simply by uploading a specially crafted image. The root cause was not in GitLab's own code but in ExifTool, a metadata-parsing tool GitLab called under the hood. It was initially rated CVSS 9.9, then revised to a perfect 10.0 once it was confirmed exploitable without any authentication.

Timeline and impact

GitLab patched the issue on April 14, 2021, in versions 13.8.8, 13.9.6, and 13.10.3. On September 21, 2021, GitLab revised the severity to 10.0 to reflect that the flaw was reachable pre-authentication. Then, on November 1, 2021, Rapid7 reported active exploitation in the wild, and researchers found that roughly half of internet-facing GitLab instances remained unpatched more than six months after the fix. The gap between patch availability and patch adoption is the real lesson of this CVE: the fix existed for over half a year while tens of thousands of servers stayed exploitable, many of them ending up in botnets and used to launch further attacks.

Root cause

When a user uploaded an image, GitLab Workhorse passed files with certain extensions through ExifTool to strip non-allowlisted metadata tags. The critical mistake is that ExifTool does not trust file extensions; it sniffs the actual file content to decide how to parse it. So an attacker could upload a file with an innocent .jpg name whose bytes were actually a DjVu document, and ExifTool would happily hand it to its DjVu parser.

That DjVu parser contained its own vulnerability, tracked as CVE-2021-22204, in how it handled annotation metadata. A crafted DjVu file could smuggle a string that ExifTool's code evaluated in a Perl eval context, turning attacker-controlled metadata into attacker-controlled commands. Because GitLab routed uploads through ExifTool automatically, and image upload on many endpoints does not require login, the whole chain was reachable by an anonymous attacker.

The conceptual chain looked like this:

attacker uploads "photo.jpg" (bytes are really a crafted DjVu)
  -> GitLab Workhorse hands it to ExifTool
  -> ExifTool ignores ".jpg", detects DjVu, invokes DjVu parser
  -> crafted annotation reaches an eval context (CVE-2021-22204)
  -> attacker command runs as the GitLab service user

This is a supply chain lesson wearing an application-security costume: GitLab's own code was fine, but the trust it placed in a bundled parser inherited that parser's worst behavior.

Detection

If you ran a vulnerable GitLab version, indicators of exploitation include:

  • Unexpected users, especially new administrator accounts, appearing in GitLab.
  • The git or gitlab-workhorse service user spawning shells, perl, curl, or wget processes.
  • Outbound connections from the GitLab host to unfamiliar IP addresses shortly after image-upload requests.
  • Web logs showing POST requests to upload endpoints followed by anomalous CPU or process activity.
  • Cron jobs or systemd units you did not create, a common persistence mechanism seen in the wild.

Checking your running version against the patched releases is the fastest triage; anything below the fixed versions should be treated as potentially compromised if it was internet-facing.

Remediation and patched versions

Upgrade to GitLab CE/EE 13.8.8, 13.9.6, 13.10.3, or any later release. All versions from 11.9 onward were affected, so most older installs needed to move. Because active exploitation was widespread, patching alone is not sufficient for a server that was exposed: assume compromise, rotate secrets and runner tokens, review for rogue accounts and persistence, and rebuild if you cannot confidently establish integrity. Updating the underlying ExifTool package (fixed in ExifTool 12.24) hardens the specific parser, but upgrading GitLab is the authoritative remediation.

How Safeguard helps

The honest framing: no scanner would have flagged CVE-2021-22205 the instant it was disclosed as a novel bug, and if your server was already exposed and exploited, remediation is an incident-response exercise, not just a version bump. Where a platform like Safeguard earns its keep is the part this CVE punished hardest, the six-month patch gap driven by nobody having a reliable inventory of what they ran and what it depended on.

Safeguard's software composition analysis tracks GitLab and its bundled components, including ExifTool, as first-class entries in your software inventory, so when a fixed version lands you can immediately see every instance still below the threshold rather than discovering it from an attacker. Because many teams run GitLab and its runners as containers, container image scanning confirms whether a vulnerable build shipped in an image. Griffin AI reads the advisory and generates a prioritized remediation plan, elevating an internet-facing, actively-exploited finding above routine noise, and automated fix pull requests help move self-managed deployments onto patched releases faster. If you are comparing how quickly tools surface a bundled-dependency RCE like this, our Snyk comparison covers the differences.

Frequently Asked Questions

Was the bug actually in GitLab's code? No. GitLab's handling was the delivery path, but the executable flaw lived in ExifTool's DjVu parser (CVE-2021-22204). GitLab inherited the risk because it automatically passed uploaded images through ExifTool. This is why dependency inventory matters as much as reviewing your own code.

Why was the severity raised from 9.9 to 10.0? The initial assessment assumed some level of authentication was needed. Once it was confirmed that image upload was reachable by an unauthenticated attacker, the confidentiality, integrity, and availability impact combined with no privileges required pushed the score to a maximum 10.0.

If I patched, am I safe? You are safe from future exploitation of this bug, but if your server was internet-facing and unpatched during the exploitation window, patching does not undo a prior compromise. Treat an exposed, previously-vulnerable instance as suspect: hunt for rogue admin accounts, unexpected processes, and persistence, and rotate credentials.

Which versions fix it? GitLab CE and EE 13.8.8, 13.9.6, and 13.10.3, released April 14, 2021, and every later version. Everything from 11.9 up to those releases was vulnerable.

Get started at app.safeguard.sh/register, and find integration guides at docs.safeguard.sh.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.