2015-09-03 18:36:46 +00:00
|
|
|
# encoding: utf-8
|
2015-10-06 16:55:44 +00:00
|
|
|
#
|
2015-04-17 13:37:17 +00:00
|
|
|
# Security Configuration and Analysis
|
|
|
|
#
|
|
|
|
# Export local security policy:
|
|
|
|
# secedit /export /cfg secpol.cfg
|
|
|
|
#
|
|
|
|
# @link http://www.microsoft.com/en-us/download/details.aspx?id=25250
|
2015-08-28 18:52:22 +00:00
|
|
|
#
|
2015-04-17 13:37:17 +00:00
|
|
|
# In Windows, some security options are managed differently that the local GPO
|
2015-08-28 18:52:22 +00:00
|
|
|
# All local GPO parameters can be examined via Registry, but not all security
|
2015-04-17 13:37:17 +00:00
|
|
|
# parameters. Therefore we need a combination of Registry and secedit output
|
|
|
|
|
2016-09-11 11:43:36 +00:00
|
|
|
require 'hashie'
|
|
|
|
|
2016-03-08 18:06:55 +00:00
|
|
|
module Inspec::Resources
|
2016-09-25 23:42:52 +00:00
|
|
|
# known and supported MS privilege rights
|
|
|
|
# @see https://technet.microsoft.com/en-us/library/dd277311.aspx
|
|
|
|
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
|
|
|
|
MS_PRIVILEGES_RIGHTS = [
|
|
|
|
'SeNetworkLogonRight',
|
|
|
|
'SeBackupPrivilege',
|
|
|
|
'SeChangeNotifyPrivilege',
|
|
|
|
'SeSystemtimePrivilege',
|
|
|
|
'SeCreatePagefilePrivilege',
|
|
|
|
'SeDebugPrivilege',
|
|
|
|
'SeRemoteShutdownPrivilege',
|
|
|
|
'SeAuditPrivilege',
|
|
|
|
'SeIncreaseQuotaPrivilege',
|
|
|
|
'SeIncreaseBasePriorityPrivilege',
|
|
|
|
'SeLoadDriverPrivilege',
|
|
|
|
'SeBatchLogonRight',
|
|
|
|
'SeServiceLogonRight',
|
|
|
|
'SeInteractiveLogonRight',
|
|
|
|
'SeSecurityPrivilege',
|
|
|
|
'SeSystemEnvironmentPrivilege',
|
|
|
|
'SeProfileSingleProcessPrivilege',
|
|
|
|
'SeSystemProfilePrivilege',
|
|
|
|
'SeAssignPrimaryTokenPrivilege',
|
|
|
|
'SeRestorePrivilege',
|
|
|
|
'SeShutdownPrivilege',
|
|
|
|
'SeTakeOwnershipPrivilege',
|
|
|
|
'SeUndockPrivilege',
|
|
|
|
'SeManageVolumePrivilege',
|
|
|
|
'SeRemoteInteractiveLogonRight',
|
|
|
|
'SeImpersonatePrivilege',
|
|
|
|
'SeCreateGlobalPrivilege',
|
|
|
|
'SeIncreaseWorking',
|
|
|
|
'SeTimeZonePrivilege',
|
|
|
|
'SeCreateSymbolicLinkPrivilege',
|
|
|
|
'SeDenyNetworkLogonRight', # Deny access to this computer from the network
|
|
|
|
'SeDenyInteractiveLogonRight', # Deny logon locally
|
|
|
|
'SeDenyBatchLogonRight', # Deny logon as a batch job
|
|
|
|
'SeDenyServiceLogonRight', # Deny logon as a service
|
|
|
|
'SeTcbPrivilege',
|
|
|
|
'SeMachineAccountPrivilege',
|
|
|
|
'SeCreateTokenPrivilege',
|
|
|
|
'SeCreatePermanentPrivilege',
|
|
|
|
'SeEnableDelegationPrivilege',
|
|
|
|
'SeLockMemoryPrivilege',
|
|
|
|
'SeSyncAgentPrivilege',
|
|
|
|
'SeUnsolicitedInputPrivilege',
|
|
|
|
'SeTrustedCredManAccessPrivilege',
|
|
|
|
'SeRelabelPrivilege', # the privilege to change a Windows integrity label (new to Windows Vista)
|
|
|
|
'SeDenyRemoteInteractiveLogonRight', # Deny logon through Terminal Services
|
|
|
|
].freeze
|
|
|
|
|
2016-03-08 18:06:55 +00:00
|
|
|
class SecurityPolicy < Inspec.resource(1)
|
|
|
|
name 'security_policy'
|
2018-02-19 14:26:49 +00:00
|
|
|
supports platform: 'windows'
|
2016-03-08 18:06:55 +00:00
|
|
|
desc 'Use the security_policy InSpec audit resource to test security policies on the Microsoft Windows platform.'
|
|
|
|
example "
|
|
|
|
describe security_policy do
|
2016-09-11 11:43:36 +00:00
|
|
|
its('SeNetworkLogonRight') { should include 'S-1-5-11' }
|
2016-03-08 18:06:55 +00:00
|
|
|
end
|
2018-01-23 20:31:57 +00:00
|
|
|
|
|
|
|
describe security_policy(translate_sid: true) do
|
|
|
|
its('SeNetworkLogonRight') { should include 'NT AUTHORITY\\Authenticated Users' }
|
|
|
|
end
|
2016-03-08 18:06:55 +00:00
|
|
|
"
|
2016-09-11 11:43:36 +00:00
|
|
|
|
2018-01-23 20:31:57 +00:00
|
|
|
def initialize(opts = {})
|
|
|
|
@translate_sid = opts[:translate_sid] || false
|
|
|
|
end
|
|
|
|
|
2016-09-11 11:43:36 +00:00
|
|
|
def content
|
|
|
|
read_content
|
|
|
|
end
|
|
|
|
|
|
|
|
def params(*opts)
|
|
|
|
opts.inject(read_params) do |res, nxt|
|
|
|
|
res.respond_to?(:key) ? res[nxt] : nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def method_missing(name)
|
|
|
|
params = read_params
|
|
|
|
return nil if params.nil?
|
|
|
|
|
|
|
|
# deep search for hash key
|
|
|
|
params.extend Hashie::Extensions::DeepFind
|
|
|
|
res = params.deep_find(name.to_s)
|
2016-09-25 23:42:52 +00:00
|
|
|
|
|
|
|
# return an empty array if configuration does not include rights configuration
|
|
|
|
return [] if res.nil? && MS_PRIVILEGES_RIGHTS.include?(name.to_s)
|
2016-09-11 11:43:36 +00:00
|
|
|
res
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
'Security Policy'
|
2015-11-27 13:02:38 +00:00
|
|
|
end
|
2015-07-26 10:30:12 +00:00
|
|
|
|
2016-09-11 11:43:36 +00:00
|
|
|
private
|
|
|
|
|
|
|
|
def read_content
|
|
|
|
return @content if defined?(@content)
|
|
|
|
|
2017-12-04 20:31:06 +00:00
|
|
|
# using process pid to prevent any race conditions with multiple runners
|
|
|
|
export_file = "win_secpol-#{Process.pid}.cfg"
|
2017-11-29 14:16:40 +00:00
|
|
|
|
2016-03-08 18:06:55 +00:00
|
|
|
# export the security policy
|
2017-11-29 14:16:40 +00:00
|
|
|
cmd = inspec.command("secedit /export /cfg #{export_file}")
|
2016-03-08 18:06:55 +00:00
|
|
|
return nil if cmd.exit_status.to_i != 0
|
2015-12-17 13:56:43 +00:00
|
|
|
|
2016-03-08 18:06:55 +00:00
|
|
|
# store file content
|
2017-11-29 14:16:40 +00:00
|
|
|
cmd = inspec.command("Get-Content #{export_file}")
|
2016-09-11 11:43:36 +00:00
|
|
|
return skip_resource "Can't read security policy" if cmd.exit_status.to_i != 0
|
2015-04-17 13:37:17 +00:00
|
|
|
|
2017-11-29 14:16:40 +00:00
|
|
|
@content = cmd.stdout
|
2016-03-08 18:06:55 +00:00
|
|
|
ensure
|
|
|
|
# delete temp file
|
2017-11-29 14:16:40 +00:00
|
|
|
inspec.command("Remove-Item #{export_file}").exit_status.to_i
|
2015-04-17 13:37:17 +00:00
|
|
|
end
|
|
|
|
|
2016-09-11 11:43:36 +00:00
|
|
|
def read_params
|
|
|
|
return @params if defined?(@params)
|
|
|
|
return @params = {} if read_content.nil?
|
2015-07-26 10:30:12 +00:00
|
|
|
|
2016-09-11 11:43:36 +00:00
|
|
|
conf = SimpleConfig.new(
|
|
|
|
@content,
|
2017-04-26 21:18:14 +00:00
|
|
|
assignment_regex: /^\s*(.*)=\s*(\S*)\s*$/,
|
2016-09-11 11:43:36 +00:00
|
|
|
)
|
|
|
|
@params = convert_hash(conf.params)
|
|
|
|
end
|
2015-04-17 13:37:17 +00:00
|
|
|
|
2016-09-11 11:43:36 +00:00
|
|
|
# extracts the values, this methods detects:
|
|
|
|
# numbers and SIDs and optimizes them for further usage
|
|
|
|
def extract_value(val)
|
|
|
|
if val =~ /^\d+$/
|
|
|
|
val.to_i
|
|
|
|
# special handling for SID array
|
2018-01-23 20:31:57 +00:00
|
|
|
elsif val =~ /[,]{0,1}\*\S/
|
|
|
|
if @translate_sid
|
|
|
|
val.split(',').map { |v|
|
|
|
|
object_name = inspec.command("(New-Object System.Security.Principal.SecurityIdentifier(\"#{v.sub('*S', 'S')}\")).Translate( [System.Security.Principal.NTAccount]).Value").stdout.to_s.strip
|
|
|
|
object_name.empty? || object_name.nil? ? v.sub('*S', 'S') : object_name
|
|
|
|
}
|
|
|
|
else
|
|
|
|
val.split(',').map { |v|
|
|
|
|
v.sub('*S', 'S')
|
|
|
|
}
|
|
|
|
end
|
2016-09-11 11:43:36 +00:00
|
|
|
# special handling for string values with "
|
|
|
|
elsif !(m = /^\"(.*)\"$/.match(val)).nil?
|
|
|
|
m[1]
|
2016-03-08 18:06:55 +00:00
|
|
|
else
|
2016-09-11 11:43:36 +00:00
|
|
|
val
|
2016-03-08 18:06:55 +00:00
|
|
|
end
|
|
|
|
end
|
2015-04-17 13:37:17 +00:00
|
|
|
|
2016-09-11 11:43:36 +00:00
|
|
|
def convert_hash(hash)
|
|
|
|
new_hash = {}
|
|
|
|
hash.each do |k, v|
|
|
|
|
v.is_a?(Hash) ? value = convert_hash(v) : value = extract_value(v)
|
|
|
|
new_hash[k.strip] = value
|
|
|
|
end
|
|
|
|
new_hash
|
2016-03-08 18:06:55 +00:00
|
|
|
end
|
2015-07-26 10:30:12 +00:00
|
|
|
end
|
|
|
|
end
|