Giant update from tebo, fixing up SunRPC and the nsfmount module
git-svn-id: file:///home/svn/framework3/trunk@7961 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
556b28e6ca
commit
b13e185943
|
@ -8,12 +8,21 @@ module Msf
|
|||
# a remote machine. These methods may generally be useful in the context of
|
||||
# exploitation. This mixin extends the Tcp exploit mixin. Only one SunRPC
|
||||
# service can be accessed at a time using this class.
|
||||
#
|
||||
# http://www.ietf.org/rfc/rfc1057.txt
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::SunRPC
|
||||
include Exploit::Remote::Tcp
|
||||
|
||||
XDR = Rex::Proto::SunRPC::XDR
|
||||
XDR = Rex::Encoder::XDR
|
||||
|
||||
MSG_ACCEPTED = 0
|
||||
SUCCESS = 0 # RPC executed successfully
|
||||
PROG_UMAVAIL = 1 # Remote hasn't exported program
|
||||
PROG_MISMATCH = 2 # Remote can't support version #
|
||||
PROC_UNAVAIL = 3 # Program can't support procedure
|
||||
GARBAGE_ARGS = 4 # Procedure can't decode params'
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
@ -27,7 +36,7 @@ module Exploit::Remote::SunRPC
|
|||
|
||||
register_advanced_options(
|
||||
[
|
||||
# XXX: Use portmapper to do call
|
||||
# XXX: Use portmapper to do call - Direct portmap to make the request to the program portmap_req
|
||||
], Msf::Exploit::Remote::SunRPC)
|
||||
|
||||
register_options(
|
||||
|
@ -45,15 +54,38 @@ module Exploit::Remote::SunRPC
|
|||
self.rpcobj.should_fragment = 1
|
||||
end
|
||||
|
||||
# if datastore['XPORT']
|
||||
# rpcobj.pport = datastore['XPORT']
|
||||
# else
|
||||
rpcobj.create
|
||||
# end
|
||||
ret = rpcobj.create
|
||||
return print_error("#{rhost} - No response to SunRPC PortMap request") unless ret
|
||||
|
||||
arr = XDR.decode!(ret, Integer, Integer, Integer, String, Integer, Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
||||
err = "#{rhost} - SunRPC PortMap request failed: "
|
||||
err << 'Message not accepted' if arr[1] != MSG_ACCEPTED
|
||||
err << 'RPC did not execute' if arr[4] != SUCCESS
|
||||
err << 'Program not available' if arr[5] == 0
|
||||
print_error(err)
|
||||
end
|
||||
|
||||
rpcobj.pport = arr[5]
|
||||
end
|
||||
|
||||
def sunrpc_call(proc, buf, timeout=60)
|
||||
rpcobj.call(proc, buf, timeout)
|
||||
ret = rpcobj.call(proc, buf, timeout)
|
||||
return print_error("#{rhost} - No response to SunRPC call for procedure: #{proc}") unless ret
|
||||
|
||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS
|
||||
err = "SunRPC call for program #{program} [#{progresolv(program)}], procedure #{procedure}, failed: "
|
||||
case arr[4]
|
||||
when PROG_UMAVAIL then err << "Program Unavailable"
|
||||
when PROG_MISMATCH then err << "Program Version Mismatch"
|
||||
when PROC_UNAVAIL then err << "Procedure Unavailable"
|
||||
when GARBAGE_ARGS then err << "Garbage Arguments"
|
||||
else err << "Unknown Error"
|
||||
end
|
||||
print_error("#{rhost} - #{err}")
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def sunrpc_destroy
|
||||
|
@ -68,6 +100,38 @@ module Exploit::Remote::SunRPC
|
|||
def sunrpc_authunix(*args)
|
||||
rpcobj.authunix_create(*args)
|
||||
end
|
||||
|
||||
# XXX: Incomplete. Just moved from Rex::Proto::SunRPC::Client
|
||||
def portmap_qry()
|
||||
ret = portmap_req()
|
||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
||||
err = "SunRPC call for program #{program} [#{progresolv(program)}], procedure #{procedure}, failed: "
|
||||
case arr[4]
|
||||
when PROG_UMAVAIL then err << "Program Unavailable"
|
||||
when PROG_MISMATCH then err << "Program Version Mismatch"
|
||||
when PROC_UNAVAIL then err << "Procedure Unavailable"
|
||||
when GARBAGE_ARGS then err << "Garbage Arguments"
|
||||
else err << "Unknown Error"
|
||||
end
|
||||
print_error("#{rhost} - #{err}")
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
def progresolv(number)
|
||||
names = File.join(Msf::Config.install_root, "data", "wordlists", "rpc_names.txt")
|
||||
File.open(names,"r").each_line do |line|
|
||||
next if line.empty? || line =~ /^\s*#/
|
||||
|
||||
if line =~ /^(\S+?)\s+(\d+)/ && number == $2.to_i
|
||||
return $1
|
||||
end
|
||||
end
|
||||
|
||||
return "UNKNOWN-#{number}"
|
||||
end
|
||||
|
||||
# Used to track the last SunRPC context
|
||||
attr_accessor :rpcobj
|
||||
|
|
|
@ -15,7 +15,8 @@ module XDR
|
|||
end
|
||||
|
||||
def XDR.decode_int!(data)
|
||||
return data.slice!(0..3).unpack('N')[0]
|
||||
return data.slice!(0..3).unpack('N')[0] if data
|
||||
data = 0
|
||||
end
|
||||
|
||||
def XDR.encode_lchar(char)
|
||||
|
@ -102,4 +103,4 @@ module XDR
|
|||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,2 +1 @@
|
|||
require 'rex/proto/sunrpc/client'
|
||||
require 'rex/proto/sunrpc/xdr'
|
|
@ -26,14 +26,6 @@ class Client
|
|||
|
||||
CALL = 0
|
||||
|
||||
MSG_ACCEPTED = 0
|
||||
|
||||
SUCCESS = 0 # RPC executed successfully
|
||||
PROG_UMAVAIL = 1 # Remote hasn't exported program
|
||||
PROG_MISMATCH = 2 # Remote can't support version #
|
||||
PROC_UNAVAIL = 3 # Program can't support procedure
|
||||
GARBAGE_ARGS = 4 # Procedure can't decode params
|
||||
|
||||
attr_reader :rhost, :rport, :proto, :program, :version
|
||||
attr_accessor :pport
|
||||
|
||||
|
@ -72,54 +64,23 @@ class Client
|
|||
sock = make_rpc(@proto, @rhost, @rport)
|
||||
send_rpc(sock, buf)
|
||||
ret = recv_rpc(sock)
|
||||
raise ::Rex::RuntimeError, "No response to SunRPC PortMap request" if ! ret
|
||||
close_rpc(sock)
|
||||
|
||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer,
|
||||
Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
||||
# Check PRO[CG]_*/GARBAGE_ARGS
|
||||
err = "SunRPC PortMap request to #{@rhost}:#{rport} failed: "
|
||||
err << 'Message not accepted' if arr[1] != MSG_ACCEPTED
|
||||
err << 'RPC did not execute' if arr[4] != SUCCESS
|
||||
err << 'Program not available' if arr[5] == 0
|
||||
raise ::Rex::RuntimeError, err
|
||||
end
|
||||
|
||||
@pport = arr[5]
|
||||
return ret
|
||||
end
|
||||
|
||||
def call(procedure, buffer, timeout=60)
|
||||
buf =
|
||||
Rex::Encoder::XDR.encode(CALL, 2, @program, @version, procedure,
|
||||
@auth_type, [@auth_data, 400], AUTH_NULL, '')+
|
||||
buffer
|
||||
buffer
|
||||
|
||||
if !@call_sock
|
||||
@call_sock = make_rpc(@proto, @rhost, @pport)
|
||||
end
|
||||
|
||||
send_rpc(@call_sock, buf)
|
||||
ret = recv_rpc(@call_sock, timeout)
|
||||
|
||||
if ret
|
||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS
|
||||
err = "SunRPC call for program #{program}, procedure #{procedure}, failed: "
|
||||
case arr[4]
|
||||
when PROG_UMAVAIL then err << "Program Unavailable"
|
||||
when PROG_MISMATCH then err << "Program Version Mismatch"
|
||||
when PROC_UNAVAIL then err << "Procedure Unavailable"
|
||||
when GARBAGE_ARGS then err << "Garbage Arguments"
|
||||
else err << "Unknown Error"
|
||||
end
|
||||
raise ::Rex::RuntimeError, err
|
||||
end
|
||||
else
|
||||
raise RPCTimeout, "No response to SunRPC call for procedure #{procedure}"
|
||||
end
|
||||
|
||||
return ret
|
||||
recv_rpc(@call_sock, timeout)
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -150,38 +111,11 @@ class Client
|
|||
sock = make_rpc('tcp', host, port)
|
||||
send_rpc(sock, buf)
|
||||
ret = recv_rpc(sock)
|
||||
raise ::Rex::RuntimeError, "No response to SunRPC request" if ! ret
|
||||
close_rpc(sock)
|
||||
|
||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
||||
err = "SunRPC call for program #{program}, procedure #{procedure}, failed: "
|
||||
case arr[4]
|
||||
when PROG_UMAVAIL then err << "Program Unavailable"
|
||||
when PROG_MISMATCH then err << "Program Version Mismatch"
|
||||
when PROC_UNAVAIL then err << "Procedure Unavailable"
|
||||
when GARBAGE_ARGS then err << "Garbage Arguments"
|
||||
else err << "Unknown Error"
|
||||
end
|
||||
raise ::Rex::RuntimeError, err
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
# Msf::Config.data_directory
|
||||
# def Client.program2name(number)
|
||||
# File.foreach('data/rpc_names') { |line|
|
||||
# next if line.empty? || line =~ /^\s*#/
|
||||
#
|
||||
# if line =~ /^(\S+?)\s+(\d+)/ && number == $2.to_i
|
||||
# return $1
|
||||
# end
|
||||
# }
|
||||
#
|
||||
# return "UNKNOWN-#{number}"
|
||||
# end
|
||||
|
||||
|
||||
private
|
||||
def make_rpc(proto, host, port)
|
||||
Rex::Socket.create(
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
module Rex
|
||||
module Proto
|
||||
module SunRPC
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements basic XDR encoding.
|
||||
#
|
||||
###
|
||||
module XDR
|
||||
MAX_ARG = 0xffffffff
|
||||
|
||||
# Also: unsigned int, bool, enum
|
||||
def XDR.encode_int(int)
|
||||
return [int].pack('N')
|
||||
end
|
||||
|
||||
def XDR.decode_int!(data)
|
||||
return data.slice!(0..3).unpack('N')[0]
|
||||
end
|
||||
|
||||
|
||||
def XDR.encode_lchar(char)
|
||||
char |= 0xffffff00 if char & 0x80 != 0
|
||||
return encode_int(char)
|
||||
end
|
||||
|
||||
def XDR.decode_lchar!(data)
|
||||
return (decode_int!(data) & 0xff).chr
|
||||
end
|
||||
|
||||
|
||||
# Also: Variable length opaque
|
||||
def XDR.encode_string(str, max=MAX_ARG)
|
||||
raise ArgumentError, 'XDR: String too long' if str.length > max
|
||||
len = str.length
|
||||
str += "\x00" * ((4 - (len & 3)) & 3)
|
||||
return encode_int(len) + str
|
||||
end
|
||||
|
||||
def XDR.decode_string!(data)
|
||||
real_len = decode_int!(data)
|
||||
return "" if real_len == 0
|
||||
align_len = (real_len + 3) & ~3
|
||||
return data.slice!(0..align_len-1).slice(0..real_len-1)
|
||||
end
|
||||
|
||||
|
||||
def XDR.encode_varray(arr, max=MAX_ARG, &block)
|
||||
raise ArgumentError, 'XDR: Too many array elements' if arr.length > max
|
||||
return encode_int(arr.length) + arr.collect(&block).join(nil)
|
||||
end
|
||||
|
||||
def XDR.decode_varray!(data)
|
||||
buf = []
|
||||
1.upto(decode_int!(data)) { buf.push(yield(data)) }
|
||||
return buf
|
||||
end
|
||||
|
||||
|
||||
|
||||
# encode(0, [0, 1], "foo", ["bar", 4]) does:
|
||||
# encode_int(0) + encode_varray([0, 1]) { |i| XDR.encode_int(i) } +
|
||||
# encode_string("foo") + encode_string("bar, 4)
|
||||
def XDR.encode(*data)
|
||||
data.collect do |var|
|
||||
if var.kind_of?(String)
|
||||
encode_string(var)
|
||||
elsif var.kind_of?(Integer)
|
||||
encode_int(var)
|
||||
elsif var.kind_of?(Array) && var[0].kind_of?(String)
|
||||
raise ArgumentError, 'XDR: Incorrect string array arguments' if var.length != 2
|
||||
encode_string(var[0], var[1])
|
||||
elsif var.kind_of?(Array) && var[0].kind_of?(Integer)
|
||||
encode_varray(var) { |i| XDR.encode_int(i) }
|
||||
# 0 means an empty array index in the case of Integer and an empty string in
|
||||
# the case of String so we get the best of both worlds
|
||||
elsif var.kind_of?(Array) && var[0].nil?
|
||||
encode_int(0)
|
||||
else
|
||||
type = var.class
|
||||
type = var[0].class if var.kind_of?(Array)
|
||||
raise TypeError, "XDR: encode does not support #{type}"
|
||||
end
|
||||
end.join(nil)
|
||||
end
|
||||
|
||||
# decode(buf, Integer, String, [Integer], [String]) does:
|
||||
# [decode_int!(buf), decode_string!(buf),
|
||||
# decode_varray!(buf) { |i| XDR.decode_int!(i) },
|
||||
# decode_varray!(buf) { |s| XDR.decode_string(s) }]
|
||||
def XDR.decode!(buf, *data)
|
||||
return *data.collect do |var|
|
||||
if data.length == 0
|
||||
elsif var.kind_of?(Array) && var[0] == String
|
||||
decode_varray!(buf) { |s| XDR.decode_string!(s) }
|
||||
elsif var.kind_of?(Array) && var[0] == Integer
|
||||
decode_varray!(buf) { |i| XDR.decode_int!(i) }
|
||||
elsif var == String
|
||||
decode_string!(buf)
|
||||
elsif var == Integer
|
||||
decode_int!(buf)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -23,7 +23,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
'Description' => %q{
|
||||
This module scans NFS mounts and their permissions.
|
||||
},
|
||||
'Author' => ['tebo <tebo [at] attackresearch.com>'],
|
||||
'Author' => ['<tebo [at] attackresearch.com>'],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://www.ietf.org/rfc/rfc1094.txt'],
|
||||
|
@ -47,26 +47,28 @@ class Metasploit3 < Msf::Auxiliary
|
|||
progver = 1
|
||||
procedure = 1
|
||||
|
||||
pport = sunrpc_create('udp', program, progver)
|
||||
sunrpc_create('udp', program, progver)
|
||||
sunrpc_authunix(hostname, datastore['UID'], datastore['GID'], [])
|
||||
resp = sunrpc_call(5, "")
|
||||
|
||||
if (resp[3,1].unpack('C')[0] == 0x01)
|
||||
print_status("#{ip} Exports found")
|
||||
|
||||
exports = resp[3,1].unpack('C')[0]
|
||||
if (exports == 0x01)
|
||||
print_good("#{ip} - Exports found")
|
||||
while XDR.decode_int!(resp) == 1 do
|
||||
dir = XDR.decode_string!(resp)
|
||||
grp = []
|
||||
while XDR.decode_int!(resp) == 1 do
|
||||
grp = XDR.decode_string!(resp)
|
||||
grp << XDR.decode_string!(resp)
|
||||
end
|
||||
print_line("#{ip}\t#{dir}\t[#{grp}]")
|
||||
print_line("\t#{dir}\t[#{grp.join(", ")}]")
|
||||
end
|
||||
else
|
||||
print_status("#{ip} has no exports")
|
||||
elsif(exports == 0x00)
|
||||
print_status("#{ip} - No exports")
|
||||
end
|
||||
|
||||
|
||||
sunrpc_destroy
|
||||
rescue ::Rex::Proto::SunRPC::RPCTimeout
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue