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:
HD Moore 2009-12-23 13:44:53 +00:00
parent 556b28e6ca
commit b13e185943
6 changed files with 92 additions and 202 deletions

View File

@ -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

View File

@ -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

View File

@ -1,2 +1 @@
require 'rex/proto/sunrpc/client'
require 'rex/proto/sunrpc/xdr'

View File

@ -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(

View File

@ -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

View File

@ -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