nexpose 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3061d4a5d9569fa5aa6e9e71b59b8747688e3423
4
- data.tar.gz: 0b79ee953da25edc842e37b8ac19b9ba9f4bbb7c
3
+ metadata.gz: 65b6a8c8e5b9a22b4554cad85898b6e54a4f9c87
4
+ data.tar.gz: 4f75da07def218ce18d31559225d7598ceb07828
5
5
  SHA512:
6
- metadata.gz: 31e606472483067a65ef775690c7f3f92ef26f50c9ce18d4840565d7cc3c3031d6b713ba379112ebe1677ef0d1e54e79cc17e33b44cc227570a91ae45f1555ce
7
- data.tar.gz: a71ed17da304eb8c08a9fd5f5e8fad4b7efc019d0f172a12b0b215b0d342da31eb5da70dad4245d57f13c405bda2a44a53835f677c005a2b18ae7dc65e619694
6
+ metadata.gz: 7dff42cc42cc00d08077832ecb896db3fa13b12ae97c2fe2e93b3165d8a0e5597c0ae56d4dd83a00b60dcd3a4083dfd04dc6f80eab9765437b6afc6df6d5e760
7
+ data.tar.gz: b6564635a8abf0ee9a3231809e938c2b9d189099ad96360a36b59ac690e7e171803caf819235a3b2d0a875fd40a2b520c59497fe3effb6d226f1f55330fea42b
@@ -0,0 +1,91 @@
1
+ # Contributing to nexpose-client
2
+
3
+ The users and maintainers of nexpose-client would greatly appreciate any contributions
4
+ you can make to the project. These contributions typically come in the form of
5
+ filed bugs/issues or pull requests (PRs). These contributions routinely result
6
+ in new versions of the [nexpose-client
7
+ gem](https://rubygems.org/gems/nexpose-client) and the
8
+ [nexpose-client release](https://github.com/rapid7/nexpose-client/releases) to be released. The
9
+ process for each is outlined below.
10
+
11
+ ## Contributing Issues / Bug Reports
12
+
13
+ If you encounter any bugs or problems with nexpose-client, please file them
14
+ [here](https://github.com/rapid7/nexpose-client/issues/new), providing as much detail as
15
+ possible. If the bug is straight-forward enough and you understand the fix for
16
+ the bug well enough, you may take the simpler, less-paperwork route and simply
17
+ file a PR with the fix and the necessary details.
18
+
19
+ ## Contributing Code
20
+
21
+ nexpose-client uses a model nearly identical to that of
22
+ [Metasploit](https://github.com/rapid7/metasploit-framework) as outlined
23
+ [here](https://github.com/rapid7/metasploit-framework/wiki/Setting-Up-a-Metasploit-Development-Environment),
24
+ at least from a ```git``` perspective. If you've been through that process
25
+ (or, even better, you've been through it many times with many people), you can
26
+ do exactly what you did for Metasploit but with nexpose-client and ignore the rest of
27
+ this document.
28
+
29
+ On the other hand, if you haven't, read on!
30
+
31
+ ### Fork and Clone
32
+
33
+ Generally, this should only need to be done once, or if you need to start over.
34
+
35
+ 1. Fork nexpose-client: Visit https://github.com/rapid7/nexpose-client and click Fork,
36
+ selecting your github account if prompted
37
+ 2. Clone ```git@github.com:<your-github-username>/nexpose-client.git```, replacing
38
+ ```<your-github-username>``` with, you guessed it, your Github username.
39
+ 3. Add the master nexpose-client repository as your upstream:
40
+ ```
41
+ git remote add upstream git://github.com/rapid7/nexpose-client.git
42
+ git fetch --all
43
+ ```
44
+
45
+ ### Branch and Improve
46
+
47
+ If you have a contribution to make, first create a branch to contain your
48
+ work. The name is yours to choose, however generally it should roughly
49
+ describe what you are doing. In this example, and from here on out, the
50
+ branch will be wow, but you should change this.
51
+
52
+ ```
53
+ git fetch --all
54
+ git checkout master
55
+ git rebase upstream/master
56
+ git checkout -b wow
57
+ ```
58
+
59
+ Now, make your changes, committing as necessary, using useful commit messages:
60
+
61
+ ```
62
+ vim CONTRIBUTING.md
63
+ git add CONTRIBUTING.md
64
+ git commit -m "Adds a document on how to contribute to nexpose-client." -a
65
+ ```
66
+
67
+ Please note that changes to [lib/nexpose/version.rb](https://github.com/rapid7/nexpose-client/blob/master/lib/nexpose/version.rb) in PRs are almost never necessary.
68
+
69
+ Now push your changes to your fork:
70
+
71
+ ```
72
+ git push origin wow
73
+ ```
74
+
75
+ Finally, submit the PR. Navigate to ```https://github.com/<your-github-username>/nexpose-client/compare/wow```, fill in the details, and submit.
76
+
77
+ ## Releasing New Versions
78
+
79
+ Typically this process is reserved for contributors with push permissions to
80
+ nexpose-client:
81
+
82
+ ### Release New Gem
83
+
84
+ 1. Get an account on [Rubygems](https://rubygems.org)
85
+ 2. Contact one of the nexpose-client project contributors and have them add you to the nexpose-client gem
86
+ 3. Edit [lib/nexpose/version.rb](https://github.com/rapid7/nexpose-client/blob/master/lib/nexpose/version.rb) and increment ```VERSION```. Commit and push to origin/upstream master.
87
+ 4. Run ```rake release```
88
+
89
+ ### Github Release
90
+
91
+ Some users may prefer to consume nexpose-client in a manner other than using git itself. For that reason, Github offers [Releases](https://github.com/blog/1547-release-your-software). Whenever a new version of the software is to be released, be kind and also create a new [Release](https://github.com/rapid7/nexpose-client/releases), using a versioning scheme identical to that used for the gem.
@@ -11,7 +11,7 @@ This gem is heavily used for internal, automated testing of the Nexpose product.
11
11
 
12
12
  ## Contributions
13
13
 
14
- We welcome contributions to this package.
14
+ We welcome contributions to this package. Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
15
15
 
16
16
  Our coding standards include:
17
17
 
@@ -50,7 +50,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50
50
  require 'date'
51
51
  require 'time'
52
52
  require 'rexml/document'
53
- require 'nokogiri'
54
53
  require 'net/https'
55
54
  require 'net/http'
56
55
  require 'uri'
@@ -64,6 +63,7 @@ require 'nexpose/alert'
64
63
  require 'nexpose/ajax'
65
64
  require 'nexpose/api'
66
65
  require 'nexpose/api_request'
66
+ require 'nexpose/asset'
67
67
  require 'nexpose/common'
68
68
  require 'nexpose/console'
69
69
  require 'nexpose/credential'
@@ -74,6 +74,7 @@ require 'nexpose/device'
74
74
  require 'nexpose/discovery'
75
75
  require 'nexpose/discovery/filter'
76
76
  require 'nexpose/engine'
77
+ require 'nexpose/external'
77
78
  require 'nexpose/filter'
78
79
  require 'nexpose/global_settings'
79
80
  require 'nexpose/group'
@@ -94,6 +95,7 @@ require 'nexpose/tag/criteria'
94
95
  require 'nexpose/ticket'
95
96
  require 'nexpose/user'
96
97
  require 'nexpose/vuln'
98
+ require 'nexpose/vuln_def'
97
99
  require 'nexpose/vuln_exception'
98
100
  require 'nexpose/connection'
99
101
  require 'nexpose/maint'
@@ -0,0 +1,276 @@
1
+ module Nexpose
2
+ # Asset object as return from the 2.1 API.
3
+ #
4
+ class Asset < APIObject
5
+ # Unique identifier of the asset on the Nexpose console.
6
+ attr_reader :id
7
+ # Primary IP address of the asset.
8
+ attr_reader :ip
9
+ # MAC address of the asset.
10
+ attr_reader :mac
11
+ # Known host names found for the asset.
12
+ attr_reader :host_names
13
+ # Operating system name.
14
+ attr_reader :os_name
15
+ # The CPE for the asset's operating system.
16
+ attr_reader :os_cpe
17
+ # The host type of the asset. One of: GUEST, HYPERVISOR, PHYSICAL, MOBILE.
18
+ attr_reader :host_type
19
+
20
+ # Assessment summary of the asset, including most recent scan info. [Lazy]
21
+ attr_reader :assessment
22
+ # Service endpoints enumerated on the asset. [Lazy]
23
+ attr_reader :services
24
+ # Software enumerated on the asset. [Lazy]
25
+ attr_reader :software
26
+ # Vulnerabilities detected on the asset. [Lazy]
27
+ attr_reader :vulnerabilities
28
+ # Vulnerability instances detected on the asset. [Lazy]
29
+ attr_reader :vulnerability_instances
30
+ # Vulnerability exploits to which this asset is susceptible. [Lazy]
31
+ attr_reader :exploits
32
+ # Malware kits to which this asset is susceptible. [Lazy]
33
+ attr_reader :malware_kits
34
+
35
+ # User accounts enumerated on the asset. [Lazy]
36
+ attr_reader :user_accounts
37
+ # Group accounts enumerated on the asset. [Lazy]
38
+ attr_reader :group_accounts
39
+ # Files and directories that have been enumerated on the asset. [Lazy]
40
+ attr_reader :files
41
+
42
+ def initialize
43
+ @addresses = []
44
+ @host_names = []
45
+ end
46
+
47
+ # Load an asset from the provided console.
48
+ #
49
+ # @param [Connection] nsc Active connection to a Nexpose console.
50
+ # @param [String] id Unique identifier of an asset.
51
+ # @return [Asset] The requested asset, if found.
52
+ #
53
+ def self.load(nsc, id)
54
+ uri = "/api/2.1/assets/#{id}"
55
+ resp = AJAX.get(nsc, uri, AJAX::CONTENT_TYPE::JSON)
56
+ hash = JSON.parse(resp, symbolize_names: true)
57
+ new.object_from_hash(nsc, hash)
58
+ end
59
+ end
60
+
61
+ # Software found on an asset.
62
+ #
63
+ class Software < APIObject
64
+ # The software product name.
65
+ attr_reader :product
66
+ # The version of software detected.
67
+ attr_reader :version
68
+ # Name of the vendor publishing the software.
69
+ attr_reader :vendor
70
+ # The family of software.
71
+ attr_reader :family
72
+ # Type of software.
73
+ attr_reader :type
74
+ end
75
+
76
+ # A service endpoint on an asset.
77
+ #
78
+ class Service < APIObject
79
+ # Name of the service. [Optional]
80
+ attr_reader :name
81
+ # Port on which the service is running.
82
+ attr_reader :port
83
+ # Protocol used to communicate to the port. @see Service::Protocol.
84
+ attr_reader :protocol
85
+
86
+ def initialize(port = 0, protocol = Protocol::RAW, name = nil)
87
+ @port, @protocol, @name = port, protocol, name
88
+ end
89
+
90
+ def to_h
91
+ { name: name,
92
+ port: port,
93
+ protocol: protocol }
94
+ end
95
+
96
+ def <=>(other)
97
+ c = port <=> other.port
98
+ return c unless c == 0
99
+ c = protocol <=> other.protocol
100
+ return c unless c == 0
101
+ name <=> other.name
102
+ end
103
+
104
+ def ==(other)
105
+ eql?(other)
106
+ end
107
+
108
+ def eql?(other)
109
+ port.eql?(other.port) && protocol.eql?(other.protocol) && name.eql?(other.name)
110
+ end
111
+
112
+ # Valid protocol values for a service endpoint.
113
+ module Protocol
114
+ # Internet Protocol
115
+ IP = 'IP'
116
+ # Internet Control Message Protocol
117
+ ICMP = 'ICMP'
118
+ # Internet Group Management Protocol
119
+ IGMP = 'IGMP'
120
+ # Gateway-to-Gateway Protocol
121
+ GGP = 'GGP'
122
+ # Transmission Control Protocol
123
+ TCP = 'TCP'
124
+ # PARC Universal Protocol
125
+ PUP = 'PUP'
126
+ # User Datagram Protocol
127
+ UDP = 'UDP'
128
+ # Internet Datagram Protocol
129
+ IDP = 'IDP'
130
+ # Encapsulating Security Payload
131
+ ESP = 'ESP'
132
+ # Network Disk Protocol
133
+ ND = 'ND'
134
+ # Raw Packet (or unknown)
135
+ RAW = 'RAW'
136
+ end
137
+ end
138
+
139
+ # User accounts on an asset.
140
+ #
141
+ class UserAccount < APIObject
142
+ # User account name.
143
+ attr_reader :name
144
+ # Unique identifier of the user as determined by the asset (not Nexpose).
145
+ attr_reader :id
146
+ # Full name of the user.
147
+ attr_reader :full_name
148
+ # Account attributes.
149
+ attr_reader :attributes
150
+
151
+ def initialize(name = nil, id = 0, full_name = nil, attributes = [])
152
+ @id, @name, @full_name, @attributes = id, name, full_name, attributes
153
+ end
154
+
155
+ def to_h
156
+ { name: name,
157
+ id: id,
158
+ full_name: full_name,
159
+ attributes: Attributes.to_hash(attributes) }
160
+ end
161
+
162
+ def <=>(other)
163
+ c = name <=> other.name
164
+ return c unless c == 0
165
+ c = id <=> other.id
166
+ return c unless c == 0
167
+ c = full_name <=> other.full_name
168
+ return c unless c == 0
169
+ attributes <=> other.attributes
170
+ end
171
+
172
+ def ==(other)
173
+ eql?(other)
174
+ end
175
+
176
+ def eql?(other)
177
+ name.eql?(other.name) && id.eql?(other.id) && full_name.eql?(other.full_name) && attributes.eql?(other.attributes)
178
+ end
179
+ end
180
+
181
+ # Group accounts on an asset.
182
+ #
183
+ class GroupAccount < APIObject
184
+ # Group account name.
185
+ attr_reader :name
186
+ # Unique identifier of the group as determined by the asset (not Nexpose).
187
+ attr_reader :id
188
+ # Group attributes.
189
+ attr_reader :attributes
190
+
191
+ def initialize(name = nil, id = 0, attributes = [])
192
+ @name = name
193
+ @id = id
194
+ @attributes = attributes
195
+ end
196
+
197
+ def to_h
198
+ { name: name,
199
+ id: id,
200
+ attributes: Attributes.to_hash(attributes) }
201
+ end
202
+
203
+ def <=>(other)
204
+ c = name <=> other.name
205
+ return c unless c == 0
206
+ c = id <=> other.id
207
+ return c unless c == 0
208
+ attributes <=> other.attributes
209
+ end
210
+
211
+ def ==(other)
212
+ eql?(other)
213
+ end
214
+
215
+ def eql?(other)
216
+ name.eql?(other.name) && id.eql?(other.id) && attributes.eql?(other.attributes)
217
+ end
218
+ end
219
+
220
+ # File or directory on an asset.
221
+ #
222
+ class File < APIObject
223
+ # Name of the file.
224
+ attr_reader :name
225
+ # Size of the file.
226
+ attr_reader :size
227
+ # File attributes.
228
+ attr_reader :attributes
229
+ # Whether the file is a directory.
230
+ attr_reader :directory
231
+
232
+ def initialize(name = nil, size = 0, directory = false, attributes = [])
233
+ @name, @size, @directory, @attributes = name, size, directory, attributes
234
+ end
235
+
236
+ def directory?
237
+ directory
238
+ end
239
+
240
+ def to_h
241
+ { name: name,
242
+ size: size,
243
+ directory: directory,
244
+ attributes: Attributes.to_hash(attributes) }
245
+ end
246
+
247
+ def <=>(other)
248
+ c = name <=> other.name
249
+ return c unless c == 0
250
+ c = size <=> other.size
251
+ return c unless c == 0
252
+ c = directory <=> other.directory
253
+ return c unless c == 0
254
+ attributes <=> other.attributes
255
+ end
256
+
257
+ def ==(other)
258
+ eql?(other)
259
+ end
260
+
261
+ def eql?(other)
262
+ name.eql?(other.name) && size.eql?(other.size) && directory.eql?(other.directory) && attributes.eql?(other.attributes)
263
+ end
264
+ end
265
+
266
+ # Assessment statistics for an asset.
267
+ #
268
+ class Assessment < APIObject
269
+ # The date an asset was last scanned.
270
+ attr_reader :last_scan_date
271
+ # The ID of the scan which last assessed the asset.
272
+ attr_reader :last_scan_id
273
+ # The current risk score of the asset.
274
+ attr_reader :risk_score
275
+ end
276
+ end
@@ -110,6 +110,18 @@ module Nexpose
110
110
  attr_accessor :incremental
111
111
  attr_accessor :repeater_type
112
112
 
113
+ # Extended attributes added with the new scheduler implementation
114
+ attr_accessor :is_extended
115
+ attr_accessor :hour
116
+ attr_accessor :minute
117
+ attr_accessor :date
118
+ attr_accessor :day
119
+ attr_accessor :occurrence
120
+ attr_accessor :start_month
121
+ attr_accessor :timezone
122
+ attr_accessor :next_run_time
123
+ attr_accessor :template
124
+
113
125
  def initialize(type, interval, start, enabled = true)
114
126
  @type = type
115
127
  @interval = interval
@@ -117,16 +129,33 @@ module Nexpose
117
129
  @enabled = enabled
118
130
  end
119
131
 
132
+ def self.from_hash(hash)
133
+ schedule = new(hash[:type], hash[:interval], hash[:start])
134
+ hash.each do |k, v|
135
+ schedule.instance_variable_set("@#{k}", v)
136
+ end
137
+ schedule
138
+ end
139
+
120
140
  def as_xml
121
141
  xml = REXML::Element.new('Schedule')
122
142
  xml.attributes['enabled'] = @enabled ? 1 : 0
123
143
  xml.attributes['type'] = @type
124
144
  xml.attributes['interval'] = @interval
125
- xml.attributes['start'] = @start
145
+ xml.attributes['start'] = @start if @start
126
146
  xml.attributes['maxDuration'] = @max_duration if @max_duration
127
147
  xml.attributes['notValidAfter'] = @not_valid_after if @not_valid_after
128
148
  xml.attributes['incremental'] = @incremental ? 1 : 0 if @incremental
129
149
  xml.attributes['repeaterType'] = @repeater_type if @repeater_type
150
+ xml.attributes['is_extended'] = @is_extended if @is_extended
151
+ xml.attributes['hour'] = @hour if @hour
152
+ xml.attributes['minute'] = @minute if @minute
153
+ xml.attributes['date'] = @date if @date
154
+ xml.attributes['day'] = @day if @day
155
+ xml.attributes['occurrence'] = @occurrence if @occurrence
156
+ xml.attributes['start_month'] = @start_month if @start_month
157
+ xml.attributes['timezone'] = @timezone if @timezone
158
+ xml.attributes['template'] = @template if @template
130
159
  xml
131
160
  end
132
161
 
@@ -145,6 +174,16 @@ module Nexpose
145
174
  schedule.not_valid_after = xml.attributes['notValidAfter'] if xml.attributes['notValidAfter']
146
175
  schedule.incremental = (xml.attributes['incremental'] && xml.attributes['incremental'] == '1')
147
176
  schedule.repeater_type = xml.attributes['repeaterType'] if xml.attributes['repeaterType']
177
+ schedule.is_extended = xml.attributes['is_extended'] if xml.attributes['is_extended']
178
+ schedule.hour = xml.attributes['hour'] if xml.attributes['hour']
179
+ schedule.minute = xml.attributes['minute'] if xml.attributes['minute']
180
+ schedule.date = xml.attributes['date'] if xml.attributes['date']
181
+ schedule.day = xml.attributes['day'] if xml.attributes['day']
182
+ schedule.occurrence = xml.attributes['occurrence'] if xml.attributes['occurrence']
183
+ schedule.start_month = xml.attributes['start_month'] if xml.attributes['start_month']
184
+ schedule.timezone = xml.attributes['timezone'] if xml.attributes['timezone']
185
+ schedule.next_run_time = xml.attributes['next_run_time'] if xml.attributes['next_run_time']
186
+ schedule.template = xml.attributes['template'] if xml.attributes['template']
148
187
  schedule
149
188
  end
150
189
 
@@ -0,0 +1,205 @@
1
+ module Nexpose
2
+ class Connection
3
+ # Import external assets into a Nexpose console.
4
+ #
5
+ # This method will synchronously import a collection of assets into the
6
+ # console. Each call to this method will be treated as a single event.
7
+ #
8
+ # This method should only be used against "static" sites, i.e., those not
9
+ # tied to a dynamic population service like vSphere, AWS, etc.
10
+ #
11
+ # If a paused scan exists on the site at the time of import, the newly
12
+ # imported assets will not be included in the scan when it resumes.
13
+ #
14
+ # @param [Fixnum] site_id Existing site to import assets into.
15
+ # @param [Array[External::Asset]] assets External assets to import.
16
+ # @return [Array[ImportResult]] collection of import results.
17
+ #
18
+ def import_assets(site_id, assets)
19
+ json = JSON.generate(Array(assets).map(&:to_h))
20
+ import_assets_from_json(site_id, json)
21
+ end
22
+
23
+ # Import external assets into a Nexpose console.
24
+ #
25
+ # @param [Fixnum] site_id Existing site to import assets into.
26
+ # @param [String] json JSON representation of assets to import.
27
+ # @return [Array[ImportResult]] collection of import results.
28
+ #
29
+ def import_assets_from_json(site_id, json)
30
+ uri = "/api/2.1/sites/#{site_id}/assets"
31
+ # Wait up to 5 minutes for a response.
32
+ resp = AJAX.post(self, uri, json, AJAX::CONTENT_TYPE::JSON, 300)
33
+ arr = JSON.parse(resp, symbolize_names: true)
34
+ arr.map { |e| External::ImportResult.new.object_from_hash(self, e) }
35
+ end
36
+ end
37
+
38
+ # Namespace for functionality around importing external assets into Nexpose.
39
+ #
40
+ module External
41
+ # Object for importing assets from external sources into a Nexpose console.
42
+ # This exists primarily as a convenience for marshalling the data into the
43
+ # proper JSON format.
44
+ #
45
+ # In order to successfully import an asset, it must contain at least one
46
+ # scannable identifier: IP address, fully qualified domain name, or NetBIOS
47
+ # name. This ensures that once an asset is imported to the console, it can
48
+ # be scanned.
49
+ #
50
+ # Besides a scannable identifier, all other fields are optional.
51
+ #
52
+ class Asset
53
+ # IPv4 or IPv6 that is the primary identifier of the asset.
54
+ attr_accessor :ip
55
+ # A fully qualified domain name of the asset.
56
+ attr_accessor :fqdn
57
+ # A NetBIOS name of the asset.
58
+ attr_accessor :net_bios
59
+ # The MAC address of the asset.
60
+ attr_accessor :mac
61
+ # The host type of the asset. One of: GUEST, HYPERVISOR, PHYSICAL, MOBILE.
62
+ attr_accessor :host_type
63
+ # A list of alternate identifiers of the asset. This can include additional
64
+ # IP addresses and host names.
65
+ attr_accessor :aliases
66
+ # The date the asset was scanned. If left blank, the current time will be
67
+ # used by the console. Use the ISO 8601 basic date-time format.
68
+ # For example: 20141211T100614.526Z
69
+ attr_accessor :scan_date
70
+ # The CPE for the operating system on the asset.
71
+ attr_accessor :os
72
+ # A list of CPEs identifying software installed on the asset.
73
+ attr_accessor :software
74
+ # A list of service endpoints on the asset.
75
+ attr_accessor :services
76
+ # A list of user accounts on the asset.
77
+ attr_accessor :users
78
+ # A list of group accounts on the asset.
79
+ attr_accessor :groups
80
+ # Files and directories on the asset.
81
+ attr_accessor :files
82
+ # A list of key-value attributes associated with the asset.
83
+ attr_accessor :attributes
84
+ # Asset-level vulnerabilities.
85
+ attr_accessor :vulnerabilities
86
+
87
+ def initialize
88
+ @aliases = []
89
+ @software = []
90
+ @services = []
91
+ @attributes = []
92
+ @users = []
93
+ @groups = []
94
+ @files = []
95
+ @vulnerabilities = []
96
+ end
97
+
98
+ def to_json
99
+ JSON.generate(to_h)
100
+ end
101
+
102
+ def to_h
103
+ { ip: ip,
104
+ fqdn: fqdn,
105
+ net_bios: net_bios,
106
+ mac: mac,
107
+ host_type: host_type,
108
+ aliases: aliases,
109
+ scan_date: scan_date,
110
+ os: os,
111
+ software: software,
112
+ services: services.map(&:to_h),
113
+ users: users.map(&:to_h),
114
+ groups: groups.map(&:to_h),
115
+ files: files.map(&:to_h),
116
+ vulnerabilities: vulnerabilities.map(&:to_h),
117
+ attributes: Attributes.to_hash(attributes) }
118
+ end
119
+
120
+ # Valid host types for an asset.
121
+ module HostType
122
+ GUEST = 'GUEST'
123
+ HYPERVISOR = 'HYPERVISOR'
124
+ PHYSICAL = 'PHYSICAL'
125
+ MOBILE = 'MOBILE'
126
+ end
127
+ end
128
+
129
+ # A service endpoint on an asset.
130
+ #
131
+ class Service
132
+ # Name of the service. [Optional]
133
+ attr_accessor :name
134
+ # Port on which the service is running.
135
+ attr_accessor :port
136
+ # Protocol used to communicate to the port. @see Service::Protocol.
137
+ attr_accessor :protocol
138
+ # Vulnerabilities specific to this service endpoint.
139
+ attr_accessor :vulnerabilities
140
+
141
+ def initialize(port, protocol = Protocol::RAW, name = nil)
142
+ @port, @protocol, @name = port, protocol, name
143
+ @vulnerabilities = []
144
+ end
145
+
146
+ def to_h
147
+ { name: name,
148
+ port: port,
149
+ protocol: protocol,
150
+ vulnerabilities: vulnerabilities.map(&:to_h) }
151
+ end
152
+ end
153
+
154
+ # Vulnerability check object for importing vulnerabilities into Nexpose.
155
+ #
156
+ class VulnerabilityCheck
157
+ # Unique identifier of a vulnerability in Nexpose.
158
+ attr_accessor :vuln_id
159
+ # Status of the vulnerability. @see VulnerabilityCheck::Status
160
+ attr_accessor :status
161
+ # Unique identifier of a vulnerability instance, typically used for spider
162
+ # vulns or when multiple instances of a vuln exist on the same service.
163
+ attr_accessor :key
164
+ # Explanation of what proves that an asset or service is vulnerable.
165
+ attr_accessor :proof
166
+
167
+ def initialize(vuln_id, status = Status::EXPLOITED, proof = nil, key = nil)
168
+ @vuln_id, @status, @proof, @key = vuln_id, status, proof, key
169
+ end
170
+
171
+ def to_h
172
+ { vuln_id: vuln_id,
173
+ status: status,
174
+ key: key,
175
+ proof: proof }
176
+ end
177
+
178
+ # Valid vulnerability status for import into Nexpose.
179
+ module Status
180
+ # Vulnerability verified by exploit.
181
+ EXPLOITED = 'vulnerable-exploited'
182
+ # Vulnerable because the service or software version is associated with
183
+ # a known vulnerability.
184
+ VERSION = 'vulnerable-version'
185
+ # A potential vulnerability.
186
+ POTENTIAL = 'potential'
187
+ end
188
+ end
189
+
190
+ # Result object returned from an import_assets call, used to correlate the
191
+ # supplied scannable identifier with the resulting asset ID or any error
192
+ # messages from a problematic import.
193
+ #
194
+ class ImportResult < APIObject
195
+ # IP or hostname provided during import.
196
+ attr_reader :name
197
+ # Resulting asset ID of the created asset, if any.
198
+ attr_reader :asset_id
199
+ # The asset created by the import. [Lazy]
200
+ attr_reader :asset
201
+ # Any error messages associated with the import of the asset.
202
+ attr_reader :error_message
203
+ end
204
+ end
205
+ end
@@ -127,7 +127,7 @@ module Nexpose
127
127
  desc = xml.add_element('Description').add_text(@description)
128
128
 
129
129
  services = xml.add_element('Services')
130
- service = services.add_element('Service').add_attribute('type', @type)
130
+ service = services.add_element('Service').add_attribute('type', @service)
131
131
 
132
132
  (account = xml.add_element('Account')).add_attribute('type', 'nexpose')
133
133
  account.add_element('Field', { 'name' => 'database' }).add_text(@database)
@@ -257,9 +257,14 @@ module Nexpose
257
257
  # @param [Fixnum] id Site ID of an existing site.
258
258
  # @return [Site] Site configuration loaded from a Nexpose console.
259
259
  #
260
- def self.load(connection, id)
261
- r = APIRequest.execute(connection.url,
262
- %(<SiteConfigRequest session-id="#{connection.session_id}" site-id="#{id}"/>))
260
+ def self.load(connection, id, is_extended = false)
261
+ if is_extended
262
+ r = APIRequest.execute(connection.url,
263
+ %(<SiteConfigRequest session-id="#{connection.session_id}" site-id="#{id}" is_extended="true"/>))
264
+ else
265
+ r = APIRequest.execute(connection.url,
266
+ %(<SiteConfigRequest session-id="#{connection.session_id}" site-id="#{id}"/>))
267
+ end
263
268
  site = parse(r.res)
264
269
  site.load_dynamic_attributes(connection) if site.dynamic?
265
270
  site
@@ -1,4 +1,4 @@
1
1
  module Nexpose
2
2
  # The latest version of the Nexpose gem
3
- VERSION = '0.9.3'
3
+ VERSION = '0.9.4'
4
4
  end
@@ -45,12 +45,11 @@ module Nexpose
45
45
  #
46
46
  # @return [Array[String]] Array of currently valid check types.
47
47
  #
48
- def list_vuln_types
49
- data = DataTable._get_dyn_table(self, '/ajax/vulnck_cat_synopsis.txml')
48
+ def vuln_types
49
+ data = DataTable._get_dyn_table(self, '/data/vulnerability/checktypes/dyntable.xml?tableID=VulnCheckCategorySynopsis')
50
50
  data.map { |c| c['Category'] }
51
51
  end
52
-
53
- alias_method :vuln_types, :list_vuln_types
52
+ alias_method :list_vuln_types, :vuln_types
54
53
 
55
54
  # Retrieve details for a vulnerability.
56
55
  #
@@ -92,7 +91,7 @@ module Nexpose
92
91
  # Nexpose between the provided dates.
93
92
  #
94
93
  def find_vulns_by_date(from, to = nil)
95
- uri = "/ajax/vuln_synopsis.txml?addedMin=#{from}"
94
+ uri = "/data/vulnerability/synopsis/dyntable.xml?addedMin=#{from}"
96
95
  uri += "&addedMax=#{to}" if to
97
96
  DataTable._get_dyn_table(self, uri).map { |v| VulnSynopsis.new(v) }
98
97
  end
@@ -285,4 +284,46 @@ module Nexpose
285
284
  @malware = hash['MalwareSource'] == 'true'
286
285
  end
287
286
  end
287
+
288
+ # A vulnerability discovered on an asset.
289
+ #
290
+ class Vulnerability < APIObject
291
+ # Unique identifier of the vulnerability.
292
+ attr_reader :id
293
+ # Vulnerability title.
294
+ attr_reader :title
295
+ # Full vulnerability definition. [Lazy]
296
+ attr_reader :vulnerability_definition
297
+ end
298
+
299
+ # An instance of a vulnerability discovered on an asset.
300
+ #
301
+ class VulnerabilityInstance < APIObject
302
+ # ID of the asset where the vulnerability instance was detected.
303
+ attr_reader :asset_id
304
+ # IP Address of the asset where the vulnerability instance was detected.
305
+ attr_reader :asset_ip_address
306
+ # ID of the scan where the vulnerability instance was detected.
307
+ attr_reader :scan_id
308
+ # The ID (natural key) of the vulnerability.
309
+ attr_reader :vulnerability_id
310
+ # The time at which the vulnerability test was performed.
311
+ attr_reader :date
312
+ # The vulnerable status of the vulnerability.
313
+ attr_reader :status
314
+ # The proof which explains why the vulnerability is present on the asset.
315
+ # The value is often HTML-formatted text.
316
+ attr_reader :proof
317
+ # Key that can distinguish the instances of the same type on the system.
318
+ # For spider vulnerabilities, this is typically the relative URI where the
319
+ # vuln was discovered.
320
+ attr_reader :key
321
+ # The service that the vulnerability test was performed against.
322
+ attr_reader :service
323
+ # The port on which the service was running if the vulnerability was found
324
+ # through a network service, -1 if not defined.
325
+ attr_reader :port
326
+ # Protocol the service was providing on which the vulnerability was found.
327
+ attr_reader :protocol
328
+ end
288
329
  end
@@ -0,0 +1,174 @@
1
+ module Nexpose
2
+ class Connection
3
+ # Retrieve all vulnerability definitions currently in a Nexpose console.
4
+ #
5
+ # Note, this can easily take 30 seconds to complete and will load over
6
+ # 55,000 vulnerability definitions.
7
+ #
8
+ # @return [Array[VulnerabilityDefinition]] Collection of vulnerability definitions.
9
+ #
10
+ def all_vulns
11
+ uri = '/api/2.0/vulnerability_definitions'
12
+ resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, per_page: 2_147_483_647)
13
+ json = JSON.parse(resp, symbolize_names: true)
14
+ json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) }
15
+ end
16
+
17
+ # Search for any vulnerability definitions which refer to a given CVE.
18
+ #
19
+ # @param [String] cve A valid CVE.
20
+ # @return [Array[VulnerabilityDefinition]] A list of vuln definitions which check the CVE.
21
+ #
22
+ def find_vulns_by_cve(cve)
23
+ uri = '/api/2.0/vulnerability_definitions'
24
+ resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, cve: cve)
25
+ json = JSON.parse(resp, symbolize_names: true)
26
+ json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) }
27
+ end
28
+
29
+ # Search for any vulnerability definitions which refer to a given reference
30
+ # ID.
31
+ #
32
+ # Examples:
33
+ # find_vulns_by_ref('oval', 'OVAL10476')
34
+ # find_vulns_by_ref('bid', 35067)
35
+ # find_vulns_by_ref('secunia', 35188)
36
+ #
37
+ # @param [String] source External vulnerability reference source.
38
+ # @param [String] id Unique vulnerability reference ID.
39
+ # @return [Array[VulnerabilityDefinition]] A list of vuln definitions which
40
+ # check the vulnerability.
41
+ #
42
+ def find_vulns_by_ref(source, id)
43
+ uri = '/api/2.0/vulnerability_definitions'
44
+ resp = AJAX.get(self,
45
+ uri,
46
+ AJAX::CONTENT_TYPE::JSON,
47
+ source: source, id: id)
48
+ json = JSON.parse(resp, symbolize_names: true)
49
+ json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) }
50
+ end
51
+
52
+ # Search for any vulnerability definitions which refer to a given title.
53
+ #
54
+ # Note: This method will return a maximum of 500 results. If the search
55
+ # yields a high number of results, consider add more specific words to
56
+ # the title.
57
+ #
58
+ # @param [String] title A (partial) title to search for.
59
+ # @param [Boolean] all_words Whether to include all words from the search
60
+ # phrase in the search.
61
+ # @return [Array[VulnerabilityDefinition]] A list of vuln definitions with titles matching
62
+ # the provided value.
63
+ #
64
+ def find_vulns_by_title(title, all_words = true)
65
+ uri = '/api/2.0/vulnerability_definitions'
66
+ params = { title: title, all_words: all_words }
67
+ resp = AJAX.get(self, uri, AJAX::CONTENT_TYPE::JSON, params)
68
+ json = JSON.parse(resp, symbolize_names: true)
69
+ json[:resources].map { |e| VulnerabilityDefinition.new.object_from_hash(self, e) }
70
+ end
71
+ end
72
+
73
+ # Vulnerability definition object. Represents a known vulnerability on a given
74
+ # Nexpose console.
75
+ #
76
+ class VulnerabilityDefinition < APIObject
77
+ # Unique identifier of a vulnerability definition.
78
+ attr_reader :id
79
+ # Vulnerability title.
80
+ attr_reader :title
81
+ # Vulnerability description, usually formated in HTML.
82
+ attr_reader :description
83
+ # The CVEs for the vulnerability.
84
+ attr_reader :cves
85
+ # Date the vulnerability was publicized by the third-party, vendor, or another
86
+ # authoring source.
87
+ attr_reader :date_published
88
+ # Date the vulnerability was first checked by Nexpose.
89
+ attr_reader :date_added
90
+ # Severity category. One of: Critical, Severe, Moderate.
91
+ attr_reader :severity
92
+ # Severity score, in the range of 0.0 to 10.0.
93
+ attr_reader :severity_score
94
+ # Risk score associated with vulnerability.
95
+ attr_reader :riskscore
96
+
97
+ # Whether the presence of the vulnerability can cause PCI failure.
98
+ # One of: Pass, Fail.
99
+ attr_reader :pci_status
100
+ # PCI severity score of the vulnerability, measured on a scale of 1 to 5.
101
+ attr_reader :pci_severity_score
102
+
103
+ # CVSS score of the vulnerability. Value between 0.0 and 10.0.
104
+ attr_reader :cvss_score
105
+ # Full CVSS vector in CVSS Version 2.0 notation.
106
+ attr_reader :cvss_vector
107
+ # Base score for the exploitability of a vulnerability that is used to compute
108
+ # the overall CVSS score.
109
+ attr_reader :cvss_exploit_score
110
+ # Base score for the impact of a vulnerability that is used to compute the
111
+ # overall CVSS score.
112
+ attr_reader :cvss_impact_score
113
+
114
+ # Whether the vulnerability is classified as a denial-of-service vuln.
115
+ attr_reader :denial_of_service
116
+
117
+ # Load a vulnerability definition from the provided console.
118
+ #
119
+ # @param [Connection] nsc Active connection to a Nexpose console.
120
+ # @param [String] id Unique identifier of a vulnerability definition.
121
+ # @return [VulnerabilityDefinition] The requested vulnerability definition, if found.
122
+ #
123
+ def self.load(nsc, id)
124
+ uri = "/api/2.0/vulnerability_definitions/#{id}"
125
+ resp = AJAX.get(nsc, uri, AJAX::CONTENT_TYPE::JSON)
126
+ hash = JSON.parse(resp, symbolize_names: true)
127
+ new.object_from_hash(nsc, hash)
128
+ end
129
+ end
130
+
131
+ # Known malware kits that can target a vulnerability.
132
+ #
133
+ class MalwareKit < APIObject
134
+ # Internal Nexpose identifier of the malware kit.
135
+ attr_reader :id
136
+ # Malware kit name.
137
+ attr_reader :name
138
+ # Malware kit description, if available.
139
+ attr_reader :description
140
+ # Popularity of the malware kit, which identifies how common or accessible
141
+ # it is. Values include: rare, uncommon, common, popular, occasional.
142
+ attr_reader :popularity
143
+ end
144
+
145
+ # Known exploits of a vulnerability.
146
+ #
147
+ class Exploit < APIObject
148
+ # Internal Nexpose identifier of the exploit.
149
+ attr_reader :id
150
+ # Exploit title.
151
+ attr_reader :title
152
+ # A description of the exploit, if available.
153
+ attr_reader :description
154
+ # Skill level required to use the exploit. One of: Expert, Intermediate,
155
+ # Novice.
156
+ attr_reader :skill_level
157
+ # Source which defined and published the exploit, such as Metasploit or
158
+ # Exploit Database.
159
+ attr_reader :source
160
+ # Reference key used by the publishing source to identify the exploit.
161
+ attr_reader :source_key
162
+ end
163
+
164
+ # External vulnerability reference.
165
+ #
166
+ class Reference < APIObject
167
+ # Internal Nexpose identifier of the reference.
168
+ attr_reader :id
169
+ # Reference value, such as the full CVE identifier.
170
+ attr_reader :reference
171
+ # Reference source, such as CVE, MS, RedHat, etc.
172
+ attr_reader :source
173
+ end
174
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nexpose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - HD Moore
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-01-05 00:00:00.000000000 Z
14
+ date: 2015-01-28 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rex
@@ -59,6 +59,7 @@ extensions: []
59
59
  extra_rdoc_files:
60
60
  - README.markdown
61
61
  files:
62
+ - CONTRIBUTING.md
62
63
  - COPYING
63
64
  - Gemfile
64
65
  - README.markdown
@@ -69,6 +70,7 @@ files:
69
70
  - lib/nexpose/alert.rb
70
71
  - lib/nexpose/api.rb
71
72
  - lib/nexpose/api_request.rb
73
+ - lib/nexpose/asset.rb
72
74
  - lib/nexpose/common.rb
73
75
  - lib/nexpose/connection.rb
74
76
  - lib/nexpose/console.rb
@@ -80,6 +82,7 @@ files:
80
82
  - lib/nexpose/discovery/filter.rb
81
83
  - lib/nexpose/engine.rb
82
84
  - lib/nexpose/error.rb
85
+ - lib/nexpose/external.rb
83
86
  - lib/nexpose/filter.rb
84
87
  - lib/nexpose/global_settings.rb
85
88
  - lib/nexpose/group.rb
@@ -104,6 +107,7 @@ files:
104
107
  - lib/nexpose/util.rb
105
108
  - lib/nexpose/version.rb
106
109
  - lib/nexpose/vuln.rb
110
+ - lib/nexpose/vuln_def.rb
107
111
  - lib/nexpose/vuln_exception.rb
108
112
  - nexpose.gemspec
109
113
  homepage: https://github.com/rapid7/nexpose-client