-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
12,832 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
lockfile_version: 1 | ||
depends: [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
--- | ||
name: cis-windows10-1511-level1 | ||
title: CIS Microsoft Windows 10 (1511) Benchmark Level 1 | ||
summary: CIS Microsoft Windows 10 (1511) Benchmark Level 1 translated from SCAP | ||
version: 1.1.0-6 | ||
maintainer: Chef Software, Inc. | ||
copyright: Chef Software, Inc. | ||
copyright_email: [email protected] | ||
license: Proprietary, All rights reserved | ||
supports: | ||
- platform-family: windows |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
class Plist < Inspec.resource(1) | ||
name 'plist' | ||
supports platform: 'darwin' | ||
desc 'plist files (also known as property files) store data, often user and system settings. The files may be text or binary.' | ||
example " | ||
describe plist('/System/Library/CoreServices/SystemVersion.plist') do | ||
it { should exist } | ||
its('ProductName') { should eq 'Mac OS X' } | ||
end | ||
" | ||
|
||
def initialize(path, opts = {}) | ||
@path = path | ||
@xpath = opts[:xpath] | ||
@json_data = nil | ||
@xml_data = nil | ||
end | ||
|
||
def to_s | ||
"plist #{@path}" + (@xpath ? " with xpath: #{@xpath}" : '') | ||
end | ||
|
||
def exists? | ||
if @path =~ /\$HOME/ | ||
expand_home = inspec.command('echo $HOME').stdout.strip | ||
return inspec.file(@path.gsub('$HOME', expand_home)).exist? | ||
end | ||
inspec.file(@path).exist? | ||
end | ||
|
||
def method_missing(*args) | ||
load_json | ||
required_key = args[0].is_a?(Array) ? args[0].map { |x| x.to_s } : args[0].to_s | ||
@json_data.dig(*required_key) | ||
end | ||
|
||
def xpath_value | ||
raise Inspec::Exceptions::ResourceFailed ':xpath must be specified in options hash to use xpath_value' unless @xpath | ||
load_xml | ||
result = @xml_data.xpath(@xpath) | ||
result.respond_to?(:text) ? result.text : result | ||
end | ||
|
||
private | ||
|
||
def load_json | ||
begin | ||
@json_data ||= JSON.parse(inspec.command("plutil -convert json -o - #{@path}").stdout) | ||
rescue => e | ||
raise Inspec::Exceptions::ResourceFailed, "Failed to read plist data for '#{@path}': #{e.message}" | ||
end | ||
end | ||
|
||
def load_xml | ||
begin | ||
@xml_data ||= Nokogiri::XML.parse(inspec.command("plutil -convert xml1 -o - #{@path}").stdout) | ||
rescue => e | ||
raise Inspec::Exceptions::ResourceFailed, "Failed to read plist data for '#{@path}': #{e.message}" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
class RegistrySecurityDescriptor < Inspec.resource(1) | ||
name 'registry_security_descriptor' | ||
supports platform: 'windows' | ||
desc 'Represents the security descriptor for a registry key in Windows' | ||
example " | ||
# return all user rights associated with registry key | ||
registry_rights = registry_security_descriptor('HKLM:\\System\\CurrentControlSet\\Control\\SecurePipeServers\\Winreg') | ||
permitted_sids = ['S-1-5-32-551', 'S-1-5-19'] | ||
permitted_sids.each do |trustee| | ||
describe registry_rights.permissions[trustee] do | ||
its(['Delete']) { should cmp 0 } | ||
its(['ReadControl']) { should cmp 0 } | ||
end | ||
end | ||
# return all user rights associated with registry key and verify if trustee sid(s) exist | ||
registry_rights = registry_security_descriptor('HKLM:\\System\\CurrentControlSet\\Control\\SecurePipeServers\\Winreg') | ||
describe registry_rights do | ||
its('permissions') { should include('S-1-5-32-551', 'S-1-5-19') } | ||
end | ||
# return a specific users' rights associated with a registry key | ||
describe registry_security_descriptor('HKLM:\\System\\CurrentControlSet\\Control\\SecurePipeServers\\Winreg').permissions_for_trustee('S-1-5-19') do | ||
# Assert the permission associated with the specified registry key | ||
its(['ReadControl']) { should cmp '0' } | ||
end | ||
# return a list of user sid's who have rights associated with a registry key | ||
describe registry_security_descriptor('HKLM:\\System\\CurrentControlSet\\Control\\SecurePipeServers\\Winreg') do | ||
# Assert the permission associated with the specified registry key | ||
its ('trustees_with_any_permission') { should include 'S-1-5-32-546' } | ||
end | ||
" | ||
|
||
ACCESS_RIGHTS_BINARY = { | ||
'Delete' => 0b10000000000000000, | ||
'ReadControl' => 0b100000000000000000, | ||
'WriteDac' => 0b1000000000000000000, | ||
'WriteOwner' => 0b10000000000000000000, | ||
'AccessSystemSecurity' => 0b1000000000000000000000000, | ||
'Synchronize' => 0b100000000000000000000, | ||
'KeyAllAccess' => 0b11110000000000111111, | ||
'KeyCreateLink' => 0b100000, | ||
'KeyCreateSubKey' => 0b100, | ||
'KeyEnumerateSubKeys' => 0b1000, | ||
'KeyExecute' => 0b100000000000011001, | ||
'KeyNotify' => 0b10000, | ||
'KeyQueryValue' => 0b1, | ||
'KeyRead' => 0b100000000000011001, | ||
'KeySetValue' => 0b10, | ||
'KeyWow6432Key' => 0b1000000000, | ||
'KeyWow6464Key' => 0b100000000, | ||
'KeyWow64Res' => 0b1100000000, | ||
'KeyWrite' => 0b00000000000000110 | ||
}.freeze | ||
|
||
def initialize(path, options = {}) | ||
@path = path | ||
@trustee_access_mask = nil | ||
end | ||
|
||
def permissions | ||
fetch_results | ||
return nil unless @trustee_access_mask | ||
results = {} | ||
@trustee_access_mask.each do |trusteesid, accessmask| | ||
accessrights = {} | ||
ACCESS_RIGHTS_BINARY.each do |k,v| | ||
if accessmask.to_i & v == 0 | ||
accessrights[k] = 0 | ||
else | ||
accessrights[k] = 1 | ||
end | ||
end | ||
results[trusteesid] = accessrights | ||
end | ||
results | ||
end | ||
|
||
def permissions_for_trustee(trustee) | ||
fetch_results | ||
return nil unless @trustee_access_mask || trustee | ||
results = {} | ||
accessmask = @trustee_access_mask[trustee] | ||
ACCESS_RIGHTS_BINARY.each do |k,v| | ||
results[k] = (accessmask.to_i & v == 0) ? 0 : 1 | ||
end | ||
results | ||
end | ||
|
||
def trustees_with_any_permission | ||
fetch_results | ||
return [] unless @trustee_access_mask | ||
trustees = [] | ||
@trustee_access_mask.each do |trusteesid, accessmask| | ||
trustees.push(trusteesid) | ||
end | ||
trustees | ||
end | ||
|
||
private | ||
def fetch_results | ||
return if @trustee_access_mask | ||
sddl = inspec.powershell("(Get-Acl #{@path}).SDDL").stdout.strip.gsub("\r\n","") | ||
raise "The provided Registry Key '#{@path}' does not have an SDDL associated." if sddl == "" | ||
@trustee_access_mask = {} | ||
access_details = inspec.powershell("(Invoke-CimMethod Win32_SecurityDescriptorHelper -MethodName SDDLToWin32SD -Arguments @{ SDDL = '#{sddl}' }).Descriptor.DACL | Select @{Name=\"SID\";Expression={$_.Trustee.SIDString}},AccessMask").stdout.strip.split("\r\n")[2..-1].map { |entry| entry.split } | ||
access_details.each do |access_detail| | ||
trusteesid = access_detail[0] | ||
accessmask = access_detail[1] | ||
if @trustee_access_mask.key?(trusteesid) && @trustee_access_mask[trusteesid] | ||
@trustee_access_mask[trusteesid] = (accessmask.to_i + @trustee_access_mask[trusteesid].to_i).to_s | ||
else | ||
@trustee_access_mask[trusteesid] = accessmask | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
class SecurityDescriptor < Inspec.resource(1) | ||
name 'security_descriptor' | ||
supports supports platform: 'windows' | ||
desc 'Represents the security descriptor for a file in Windows' | ||
example " | ||
describe security_descriptor('C:\\windows\\system32\\EventVwr.exe') do | ||
# Assert the set of entities with a given permission | ||
its('ReadAndExecute') { should_not include 'S-1-5-32-546' } | ||
# Assert the permissions for a given entity | ||
its('S-1-5-32-544') { should_not include 'Write' } | ||
end | ||
" | ||
|
||
def initialize(filename, options = {}) | ||
@filename = filename | ||
@results = nil | ||
end | ||
|
||
%w{Read ReadAndExecute Write Modify Sychronize FullControl}.each do |perm| | ||
define_method perm do | ||
fetch_results unless @results | ||
# Return keys that have this permission, with the domain stripped | ||
@results.select { |k,v| v.include?(perm) }.keys.map { |key| key.split("\\")[-1] } | ||
end | ||
end | ||
|
||
def method_missing(name) | ||
fetch_results unless @results | ||
# Return the results for this entity if it exists (with some domain name) in the result set | ||
entity_key = @results.keys.select { |key| key.split("\\")[-1] == name.to_s }[0] | ||
return @results[entity_key] if entity_key | ||
# Entity not in the result set is "no permissions" | ||
[] | ||
end | ||
|
||
def to_s | ||
"Security Descriptor for #{@filename}" | ||
end | ||
|
||
private | ||
|
||
def fetch_results | ||
@results = {} | ||
cmd = inspec.powershell("Get-Acl #{@filename} | select -expand access") | ||
raise cmd.stderr.strip unless cmd.stderr == '' | ||
access_details = cmd.stdout.strip.split("\r\n\r\n").map { |entry| entry.split("\r\n") } | ||
access_details.each do |access_detail| | ||
entity = access_detail.select { |a| a =~ %r{^IdentityReference} }[0].tr(' ', '').split(':')[-1] | ||
permissions = access_detail.select { |a| a =~ %r{^FileSystemRights} }[0].tr(' ', '').split(':')[-1].split(',') | ||
# Get-Acl displays entity names in its results rather than SIDs. | ||
# It is preferable to work with SIDs when testing security | ||
# Replace the entity name from Get-Acl with a SID where possible. | ||
entity_id = get_sid(entity.split("\\")[-1]) | ||
@results[entity_id] = permissions | ||
end | ||
end | ||
|
||
def get_sid(entity_name) | ||
group_sids = inspec.command("wmic group where 'Name=\"#{entity_name}\"' get Name\",\"SID /format:csv").stdout.strip.split("\r\n\r\n")[1..-1].map { |entry| entry.split(',') } | ||
return group_sids[0][2] unless group_sids.empty? | ||
user_sids = inspec.command("wmic useraccount where 'Name=\"#{entity_name}\"' get Name\",\"SID /format:csv").stdout.strip.split("\r\n\r\n")[1..-1].map { |entry| entry.split(',') } | ||
return user_sids[0][2] unless user_sids.empty? | ||
entity_name | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
class SecurityIdentifier < Inspec.resource(1) | ||
name 'security_identifier' | ||
supports platform: 'windows' | ||
desc 'Resource that returns a Security Identifier for a given entity name in Windows. Because different entities can have the same name (e.g. a user and group can both be called \'devops\') the resource requires the type of the entity (:user, :group) to be stated to avoid an ambiguous test. If the entity type is unknown (e.g. when working with SCAP content that names an entity but does not declare its type) you may give the type as :unspecified to explicitly state that you need the resource to try to determine a SID when the entity type is not known.' | ||
example " | ||
describe security_policy do | ||
its(\"SeRemoteInteractiveLogonRight\") { should_not include security_identifier(group: 'Guests') } | ||
end | ||
" | ||
|
||
def initialize(opts = {}) | ||
supported_opt_keys = [:user, :group, :unspecified] | ||
raise "Invalid security_identifier param '#{opts}'. Please pass a hash with these supported keys: #{supported_opt_keys}" unless opts.respond_to?(:keys) | ||
raise "Unsupported security_identifier options '#{opts.keys - supported_opt_keys}'. Supported keys: #[supported_opt_keys]" unless (opts.keys - supported_opt_keys).empty? | ||
raise 'Specifying more than one of :user :group or :unspecified for security_identifier is not supported' unless opts.keys and (opts.keys & supported_opt_keys).length == 1 | ||
if opts[:user] | ||
@type = :user | ||
@name = opts[:user] | ||
end | ||
if opts[:group] | ||
@type = :group | ||
@name = opts[:group] | ||
end | ||
if opts[:unspecified] | ||
@type = :unspecified | ||
@name = opts[:unspecified] | ||
end | ||
raise 'Specify one of :user :group or :unspecified for security_identifier' unless @name | ||
@sids = nil | ||
end | ||
|
||
def sid | ||
fetch_sids unless @sids | ||
@sids[@name] || @name | ||
end | ||
|
||
def fetch_sids | ||
@sids = {} | ||
case @type | ||
when :group | ||
sid_data = wmi_results_array("wmic group where 'Name=\"#{@name}\"' get Name\",\"SID /format:csv") | ||
when :user | ||
sid_data = wmi_results_array("wmic useraccount where 'Name=\"#{@name}\"' get Name\",\"SID /format:csv") | ||
when :unspecified | ||
# try group first, then user | ||
sid_data = wmi_results_array("wmic group where 'Name=\"#{@name}\"' get Name\",\"SID /format:csv") | ||
if sid_data.empty? | ||
sid_data = wmi_results_array("wmic useraccount where 'Name=\"#{@name}\"' get Name\",\"SID /format:csv") | ||
end | ||
else | ||
raise "Unhandled entity type '#{@type}'" | ||
end | ||
sid_data.each { |sid| @sids[sid[1]] = sid[2] } | ||
end | ||
|
||
def wmi_results_array(query) | ||
inspec.command(query).stdout.strip.split("\r\n\r\n")[1..-1].map { |entry| entry.split(',') } | ||
end | ||
end |