nexpose_sccm 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 05d535cae6251874a19e668e8f19fead29309377
4
+ data.tar.gz: fd671a1b752549f983b6c3e85d47e8f57920a15f
5
+ SHA512:
6
+ metadata.gz: d833ae2f1444db55e6daae56a5b4bccc10feeb2d93ced13a1e4d8a8a3de1ad67d1210f142257559555b63f0fbe9bf0958aac4cb838208fa152fe60a94074e382
7
+ data.tar.gz: 1bb2ee97958b404fbad16459389c966046cd241675c487e877e381e36b84ddb01f1ed3c1c88d80190bdfe8246c469fffd3476f83655a230968af191896f27eb1
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'winrm'
4
+
5
+ group :nexpose, optional: true do
6
+ gem 'nexpose'
7
+ end
8
+
9
+ group :dwh, optional: true do
10
+ ## May need to install postgres drivers before the gem
11
+ gem 'pg'
12
+ end
13
+
14
+ # Specify your gem's dependencies in nexpose_servicenow.gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Adam Robinson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,212 @@
1
+ # Nexpose SCCM Integration
2
+
3
+ This is the offical gem package for the Ruby Nexpose SCCM integration.
4
+
5
+ For assistance with using the gem, please contact the Rapid7 Integrations support team using the [Rapid7 Support Portal](https://rapid7support.force.com/customers/login)
6
+
7
+ ## About
8
+
9
+ The `nexpose_sccm` integration is designed to pull vulnerability and solution data from Nexpose scan results, to then generate SCCM Software Update Groups and Collections based upon the data. These groups can then be used to deploy software updates to assets, enabling partial automation of the process.
10
+
11
+ Example queries to retrieve data from Nexpose have been provided, with the option for the user to supply different variations instead.
12
+ The integration supports retrieving data from the Nexpose Console or via a separate DataWarehouse installation.
13
+
14
+ The integration provides options to create Deployment Packages on the SCCM server, as well as generate a CSV report of assets found in Nexpose, but currently unknown or not managed by SCCM.
15
+
16
+ NOTE: Requires SCCM 1702 or later for Deployment Package functionality. It is possible to gather the version of SCCM by
17
+ running the following command in Powershell:
18
+
19
+ ```powershell
20
+ Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\SMS\\Setup
21
+ ```
22
+
23
+ `Ruby Version [>= 2.3.1, < 2.5.0]`
24
+
25
+ For more information please refer to the integration documentation which can be requested from the Rapid7 support team.
26
+
27
+ ## Installation
28
+
29
+ ### RubyGems (preferred)
30
+ With the correct Ruby version available, run the following command from the command line:
31
+
32
+ ```bash
33
+ gem install nexpose_sccm
34
+ ```
35
+ This will also download and install the required dependencies.
36
+
37
+
38
+ ### Bundler
39
+ Using the Bundler gem, the integration can be installed or packaged to be installed on a system with no Internet access.
40
+
41
+ Choose between using the Nexpose Console or a Data Warehouse for data export.
42
+
43
+ ##### Online
44
+ ```bash
45
+ bundle install --with nexpose dwh
46
+ ```
47
+
48
+ ##### Offline
49
+ On a system with internet access and a similar setup to the final install location, run the following command from within the gem folder:
50
+
51
+ ```bash
52
+ bundle package
53
+ ```
54
+ Then copy the directory to the offline system and install dependencies followed by bundle install:
55
+
56
+ ```bash
57
+ apt install ruby ruby-dev ruby-bundler libpq-dev build-essential
58
+ bundle install --local --with nexpose dwh
59
+ ```
60
+ The required gems are located under `nexpose_sccm/vendor/cache`
61
+
62
+ ### <Pending Windows Instructions>
63
+
64
+ ## Configuration
65
+ Configuration files for the nexpose\_sccm integration can be found under `nexpose_sccm/conf/`
66
+
67
+ There are several files located here that need populated:
68
+
69
+ - `logging.yml`
70
+ This file contains logging settings for the integration
71
+ - `nexpose.yml`
72
+ This file contains the Nexpose connection settings
73
+ - `postgres.yml`
74
+ This file contains the Data Warehouse connection settings
75
+ - `queries.yml`
76
+ This file contains the queries used by the integration. Please see below for more information
77
+ - `sccm.yml`
78
+ This file contains settings for the operation of the integration, as well as the SCCM connection settings
79
+ - `secret.yml`
80
+ This file contains settings for the in-built encryption functionality (optional)
81
+
82
+
83
+ #### Windows and SCCM Permissions
84
+ The user account running the integration should preferably be a Windows Domain account.
85
+ It needs to be added to the Remote Management Users group on the SCCM server.
86
+ It also needs to be added as an "Administrative User" in the SCCM Console.
87
+ The Security role for this user should be a custom role based on the pre-existing "Read-Only Analyst", with the following additional permissions:
88
+
89
+ |Permission Group |Permissions |
90
+ |-----------------------|--------------------|
91
+ |Collection |Create, Delete Resource, Modify, Modify Resource, Read, Read Resource, Remote Control, View Collected File|
92
+ |Software Update Group |Create, Modify, Read|
93
+ |Software Update Package|Create, Modify, Read|
94
+
95
+ This user may also need to log into the SCCM Console to activate the account.
96
+
97
+ For WinRM access from the host machine to the SCCM server, ports 5985 (HTTP) or 5986 (HTTPS) will need to be opened in the Firewall.
98
+ For HTTPS communication between the host machine and the SCCM server, a HTTPS listener will need to be created. The SSL Peer Fingerprint can then be retrieved by running the command in the `sccm.yaml` file.
99
+
100
+
101
+ #### Nexpose SCCM settings
102
+ These settings are found at the bottom of the `sccm.yml` file. A brief explanation of each can be found below:
103
+
104
+ |Setting |Description|
105
+ |---------------------------|-----------|
106
+ |create\_deployment_package | Set to _true_ to create Deployment Packages based on the generated Software Update Groups. This leverages staging configuration for where software updates are downloaded |
107
+ |download_updates | Set to _true_ to have SCCM download updates for the deployment package in scope. This setting will not be used if create\_deployment_package is false |
108
+ |integration_prefix | The prefix used for all Software Update Groups, Collections and Deployment Package names |
109
+ |generate\_unknown\_device_report| Set to true to generate a CSV report of devices found in Nexpose, but unknown to SCCM |
110
+ |report_location | Absolute or relative location of directory where reports should be saved |
111
+ |scope | The query to run from `queries.yml` - must match the name in the file |
112
+ |action | Can be set to either _update_ or _create_ for generating objects in SCCM. Recommended to leave as update to avoid creating large numbers of objects |
113
+ |data_source | Defines where vulnerability and solution data is pulled from - set _nsc_ for the Nexpose Console, _dwh_ for a Data Warehouse |
114
+ |software\_update\_group_key| The name of the SQL query column used for grouping and generating Software Update Groups |
115
+ |collection_key | The name of the SQL query column used for grouping and generating Collections |
116
+
117
+
118
+ #### Nexpose Queries
119
+ The `queries.yml` file can be modified to include customised queries. These should be tested in the Reports section of the Nexpose Console or against the Data Warehouse before use.
120
+ The query should be named following the convention shown for existing queries. It should also be under `:nsc:` or `:dwh:` depending on which Data Source it is to run against, following the YAML indentation rules.
121
+
122
+ Values can be inserted into queries at runtime - either from a file or a user prompt. The `asset_best_solution_by_site` query shows an example of this for a single value, whereas the `vuln_by_id` query demonstrates this for two lists. See the vuln_ids.csv file for an example of passing values in by file.
123
+
124
+ Any column can be used in the query as the key for either Software Update Groups or Collections - they do not need to be the same column, nor marked with the "key" identifier.
125
+ Below is a list of required or recommended columns for creating a query:
126
+
127
+ |Column|Required|Description|
128
+ |------|--------|-----------|
129
+ |key |Yes| This column makes an easy identifier for creating Software Update Groups or Collections. It is the default value listed in the `sccm.yml` file. This can be any column the user wishes to group on e.g. when running the integration, you could group Collections by site_id, combining all assets in a site into the same Collection. However, they could then use a different column to group the Software Update Groups e.g. IP Address |
130
+ |ip_address|Yes| The IP address is used for finding matching SCCM managed devices by IP Address |
131
+ |host_name|Yes| The host name is used for finding matching SCCM managed devices by HostName |
132
+ |nexpose_id|Yes| The solution's nexpose_id is used for extracting the solution, for each vulnerability, per asset in scope. This is required for retrieving the software patches from SCCM |
133
+
134
+
135
+ #### Encryption Settings
136
+ In order to asymmetrically encrypt sensitive data in the configuration files, perform the following:
137
+ - Identify the values to be encrypted and prefix them with `(enc)` e.g. `:user: '(enc)nxuser'`
138
+ - Ensure the `:secret` section is also defined in `secret.yml` with the location to store the encrypted key directory and encrypted settings file. It is important these values do not change as the integration will use them to identify how to decrypt the encrypted settings.
139
+
140
+ Once all fields expected to be encrypted have been updated, then the encrypt_settings.rb script can be used for the encryption process. Once run, the configuration files will be updated. This informs the integration to retrieve the credentials from the encrypted file:
141
+
142
+ ```ruby
143
+ ruby encrypt_settings.rb
144
+ ```
145
+ It is important to remember to re-encrypt **ALL** configuration files if any encrypted values are updated.
146
+ To disable the use of encrypted settings, either remove or comment out the entire `:secret` section within `secret.yml`
147
+
148
+ Like the main integration, the configuration file location can be passed using the `-s` flag.
149
+
150
+
151
+ ## Usage
152
+ The executable used for initiating the integration can be found in the `bin/` directory of the installed nexpose_sccm gem.
153
+ With the gem installed, the integration can be called using the following command:
154
+
155
+ ```bash
156
+ nexpose_sccm [options]
157
+ ```
158
+ The following options can be used with the integration:
159
+
160
+ |Option |Description|
161
+ |--------------|-----------|
162
+ |--version | Shows version information about the nexpose\_sccm gem |
163
+ |-h | Displays the information listed here |
164
+ |-s SETTINGS | The path to the settings directory containing the necessary yml files. Only needed if files are stored in a chosen location |
165
+ |-i INPUT\_FILE| The path to the input file leveraged by the query. This should be in CSV format with a column for each set of input data and the headers should match the required field names. See vuln_ids.csv for an example |
166
+
167
+ ## Validation
168
+ There are two ways to validate the successful completion of the integration.
169
+ First, verify the output of the log file or STDOUT if configured. During normal usage, the log level should be set to INFO which means only informational or error messages will be populated within the log. If any error messages are found, please review them to identify the reason for the error. A successful run should only contain INFO and/or DEBUG messages.
170
+ The log file can be found under `<gem_install_location>/nexpose_sccm/lib/nexpose_sccm/logs/`
171
+
172
+ In addition, once the integration has run it is possible to check the Software Update Groups, Collections, and Deployment Packages that are generated within SCCM. Once logged into the SCCM console, view each section to ensure everything is properly generated.
173
+ If `download_updates` is set to _true_ then downloaded updates will also be found at the `staging` location set in the `sccm.yml` file.
174
+
175
+ ## Troubleshooting
176
+ ### Logs
177
+ Please check the log files located at `<gem_install_location>/nexpose_sccm/lib/nexpose_sccm/logs/`
178
+ These will output errors occurring during the normal run of the integration. Log messages will also appear here if there is an issue connecting to the SCCM server via WinRM.
179
+
180
+ ### Windows Issues
181
+ The integration uses WinRM and WMI to communicate with the SCCM Console, as well as to run WQL and Powershell commands. If an error occurs with this process it will be when doing the following:
182
+
183
+ - Getting Device, Collection, Software Update Group, Software Update or Deployment Package information
184
+ - Creating Collections, Software Update Groups or Deployment Packages
185
+
186
+ Please ensure your user has the correct permissions for these tasks.
187
+ Please ensure any local users have the correct permissions for running tasks on the SCCM console.
188
+
189
+ Information on these issues can be found on the [Microsoft Technet Website](https://blogs.technet.microsoft.com)
190
+
191
+ Useful logs can be found in the "Event Viewer" application on the SCCM server:
192
+
193
+ - `Windows Logs -> System`
194
+ - `Application and Services -> Microsoft -> Windows Remote Management`
195
+
196
+ ### Support
197
+ For further issues, please contact the Rapid7 support team at support@rapid7.com
198
+
199
+ ## Contribution
200
+
201
+ ## Changelog
202
+
203
+ #### 0.4.0
204
+ - Adaptation from existing script into a Ruby gem
205
+ - Restructuring file hierarcy
206
+ - Implementing internal logger
207
+ - Adding licensing
208
+ - Adding a gemspec
209
+ - Removing Bundler
210
+ - Adding extra example queries
211
+ - Updating README
212
+ - Published to the RubyGems repository
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'openssl'
5
+ require 'io/console'
6
+ require 'base64'
7
+ require 'fileutils'
8
+ require 'yaml'
9
+ require 'nexpose_sccm/utilities/utility_config'
10
+
11
+ SETTINGS_LOCATIONS = [File.join(File.dirname(__FILE__), '../conf/'),
12
+ File.join(File.dirname(__FILE__), '/'),
13
+ '/opt/rapid7/nexpose/scripts/',
14
+ 'C:\\program files\\rapid7\\nexpose\\scripts\\']
15
+
16
+ options = {}
17
+ ops = OptionParser.new do |opts|
18
+ opts.banner = "Usage: #{$0} [options]\n"
19
+
20
+ opts.on('-s SETTINGS', '--settings SETTINGS', 'Path to settings directory with yml files') do |config|
21
+ options[:config_location] = config
22
+ end
23
+
24
+ opts.on('-h', '--help', 'Show this message') do
25
+ puts opts.help
26
+ exit
27
+ end
28
+ end
29
+
30
+ # Prompt function
31
+ def prompt(pwd, default, *args)
32
+ begin
33
+ print(*args)
34
+ result = ""
35
+ if pwd
36
+ result = STDIN.noecho(&:gets).strip
37
+ print("\n")
38
+ else
39
+ result = gets.strip
40
+ end
41
+ if (result.empty? || result.nil?) && (default.nil? || default.empty?)
42
+ raise
43
+ end
44
+ rescue
45
+ retry
46
+ end
47
+ return result.empty? ? default : result
48
+ end
49
+
50
+ # Create directory function - careful as this will attempt to create
51
+ # parent directories that do not exist as well.
52
+ def create_dir(path, mode=nil)
53
+ FileUtils.mkdir_p(path) unless File.exist?(path)
54
+ FileUtils.chmod(mode, path) unless mode.nil?
55
+ end
56
+
57
+ begin
58
+ ops.parse! ARGV
59
+ rescue OptionParser::InvalidOption => error
60
+ puts "Error encountered. Details: #{error}\n\n#{ops}"
61
+ exit 1
62
+ end
63
+
64
+ settings = UtilityConfig.load_config(SETTINGS_LOCATIONS,
65
+ options[:config_location])
66
+
67
+ unless settings.key?(:secret)
68
+ puts "The secret.yml file in the chosen / default location must be populated."
69
+ exit 0
70
+ end
71
+
72
+ to_encrypt = {}
73
+
74
+ # Read in settings.yml to identify what to encrypt; will begin with (enc)
75
+ settings.each do |key, value|
76
+ to_encrypt[key] = {} # unless key.to_s.eql?('secret')
77
+ value.each do |k, v|
78
+ if v.to_s.start_with?('(enc)')
79
+ to_encrypt[key][k] = v.sub('(enc)', '')
80
+ settings[key][k] = 'secret'
81
+ elsif v.class == Hash
82
+ v.each do |k2, v2|
83
+ if v2.to_s.start_with?('(enc)')
84
+ to_encrypt[key][k] = {} if to_encrypt[key][k].nil?
85
+ settings[key][k] = {} if settings[key][k].nil?
86
+
87
+ to_encrypt[key][k][k2] = v2.sub('(enc)', '')
88
+ settings[key][k][k2] = 'secret'
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ if settings.key?(:secret)
96
+ if File.exist?(settings[:secret][:encrypt_key_dir]) && File.exist?(settings[:secret][:encrypt_cred_file])
97
+ priv_key = OpenSSL::PKey::RSA.new(File.read(File.join(settings[:secret][:encrypt_key_dir], 'rsa_key')))
98
+ dec_buf = priv_key.private_decrypt(Base64.decode64(File.read(settings[:secret][:encrypt_cred_file])))
99
+ current_settings = YAML::load(dec_buf)
100
+ current_settings.each do |k, v|
101
+ if to_encrypt.key?(k)
102
+ if to_encrypt[k].nil? || to_encrypt[k].empty?
103
+ to_encrypt[k] = v
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ # Assign directories. Make sure nxkeys is 700 permissions
111
+ # and the tree is owned by either root or the owner of the key file.
112
+ priv_dir = prompt(false, settings[:secret][:encrypt_key_dir], "Keys directory (default - #{settings[:secret][:encrypt_key_dir]}): ")
113
+ secret_file = prompt(false, settings[:secret][:encrypt_cred_file], "Encrypted settings file (default - #{settings[:secret][:encrypt_cred_file]}): ")
114
+
115
+ create_dir(priv_dir, 0700)
116
+
117
+ # Update and save settings.yml
118
+ UtilityConfig.save_encrypt(SETTINGS_LOCATIONS, options[:config_location])
119
+
120
+ # Protected private key file
121
+ priv_file = File.join(priv_dir, 'rsa_key')
122
+
123
+ # Generate private key and write to file.
124
+ rsa_key = OpenSSL::PKey::RSA.new(4096)
125
+ File.open(priv_file, 'w') {|file| file.write(rsa_key)}
126
+ FileUtils.chmod(0600, priv_file)
127
+ puts "Private key created."
128
+ # Generate public key from private key used to encrypt the credential YAML.
129
+ pub_key = rsa_key.public_key
130
+
131
+ # Encrypt then encode necessary settings
132
+ enc_creds = Base64.encode64(pub_key.public_encrypt(to_encrypt.to_yaml))
133
+ File.open(secret_file, 'w') {|file| file.write(enc_creds)}
134
+ FileUtils.chmod(0750, secret_file)
135
+ puts "Created encrypted credentials at #{secret_file}"
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ require 'csv'
3
+ require 'base64'
4
+ require 'openssl'
5
+ require 'optparse'
6
+ require 'yaml'
7
+ require 'nexpose_sccm'
8
+
9
+ # module WinRM
10
+ # module HTTP
11
+ # # A generic HTTP transport that utilized HTTPClient to send messages back and forth.
12
+ # # This backend will maintain state for every WinRMWebService instance that is instantiated so it
13
+ # # is possible to use GSSAPI with Keep-Alive.
14
+ # class HttpTransport
15
+ # def verify_ssl_fingerprint(cert)
16
+ # return unless @ssl_peer_fingerprint
17
+ # conn_fingerprint = OpenSSL::Digest::SHA1.new(cert.to_der).to_s
18
+ # puts @ssl_peer_fingerprint
19
+ # puts conn_fingerprint
20
+ # return unless @ssl_peer_fingerprint.casecmp(conn_fingerprint) != 0
21
+ # raise "ssl fingerprint mismatch!!!!\n"
22
+ # end
23
+ # end
24
+ # end
25
+ # end
26
+
27
+ # Allow for override of options; -s leverage for settings directory location
28
+ @options = {}
29
+
30
+ ops = OptionParser.new do |opts|
31
+ opts.banner = 'Usage: nexpose_sccm [options]'
32
+ opts.on('-s SETTINGS', '--settings SETTINGS', 'Path to settings directory with yml files') do |config|
33
+ @options[:config_location] = config
34
+ end
35
+
36
+ opts.on('-i INPUT_FILE', '--input INPUT_FILE', 'Path to input file leveraged by query') do |config|
37
+ @options[:input_file_location] = config
38
+ end
39
+
40
+ opts.on("-h", "--help", "Show this message") do
41
+ puts opts
42
+ exit 0
43
+ end
44
+
45
+ opts.on_tail("--version", "Show version") do
46
+ puts "\nnexpose_sccm v#{NexposeSCCM::VERSION}"
47
+ exit
48
+ end
49
+ end
50
+
51
+ begin
52
+ ops.parse! ARGV
53
+ rescue OptionParser::InvalidOption => error
54
+ puts "Error encountered. Details: #{error}\n\n#{ops}"
55
+ exit 1
56
+ end
57
+
58
+ SETTINGS_LOCATIONS = [File.join(File.dirname(__FILE__), '../conf/'),
59
+ File.join(File.dirname(__FILE__), '/'),
60
+ '/opt/rapid7/nexpose/scripts/',
61
+ 'C:\\program files\\rapid7\\nexpose\\scripts\\']
62
+
63
+ # Load settings from -s option or parse known locations for configuration file
64
+ settings = UtilityConfig.load_config(SETTINGS_LOCATIONS,
65
+ @options[:config_location])
66
+ settings = UtilityConfig.load_encrypt(settings)
67
+
68
+ NexposeSCCM.setup(settings)
69
+ NexposeSCCM.generate_updates(settings, @options[:input_file_location])