nexpose 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
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