[Security Solution] Add Credential Hardening policy option in Attack Surface Reduction card (#136454)

* [Security Solution] Add Credential Dumping policy option
This commit is contained in:
Kevin Logan 2022-07-21 09:29:38 -04:00 committed by GitHub
parent 0d237ea1c0
commit aec761efbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 187 additions and 10 deletions

View file

@ -12,7 +12,7 @@ import type { PackagePolicy } from '../../../../common';
import { migratePackagePolicyToV840 as migration } from './to_v8_4_0';
describe('8.4.0 Endpoint Package Policy migration', () => {
const policyDoc = ({ linuxAdvanced = {} }) => {
const policyDoc = ({ linuxAdvanced = {}, windowsOptions = {} }) => {
return {
id: 'mock-saved-object-id',
attributes: {
@ -40,7 +40,9 @@ describe('8.4.0 Endpoint Package Policy migration', () => {
config: {
policy: {
value: {
windows: {},
windows: {
...windowsOptions,
},
mac: {},
linux: {
...linuxAdvanced,
@ -55,17 +57,18 @@ describe('8.4.0 Endpoint Package Policy migration', () => {
};
};
it('adds advanced file monitoring defaulted to false', () => {
it('adds advanced file monitoring defaulted to false and ensures credential hardening is added and false.', () => {
const initialDoc = policyDoc({});
const migratedDoc = policyDoc({
linuxAdvanced: { advanced: { fanotify: { ignore_unknown_filesystems: false } } },
windowsOptions: { attack_surface_reduction: { credential_hardening: { enabled: false } } },
});
expect(migration(initialDoc, {} as SavedObjectMigrationContext)).toEqual(migratedDoc);
});
it('adds advanced file monitoring defaulted to false and preserves existing advanced fields', () => {
it('adds advanced file monitoring defaulted to false and preserves existing advanced fields and ensures credential hardening is added and false.', () => {
const initialDoc = policyDoc({
linuxAdvanced: { advanced: { existingAdvanced: true } },
});
@ -74,6 +77,7 @@ describe('8.4.0 Endpoint Package Policy migration', () => {
linuxAdvanced: {
advanced: { fanotify: { ignore_unknown_filesystems: false }, existingAdvanced: true },
},
windowsOptions: { attack_surface_reduction: { credential_hardening: { enabled: false } } },
});
expect(migration(initialDoc, {} as SavedObjectMigrationContext)).toEqual(migratedDoc);

View file

@ -25,11 +25,14 @@ export const migratePackagePolicyToV840: SavedObjectMigrationFn<PackagePolicy, P
if (input && input.config) {
const policy = input.config.policy.value;
const migratedPolicy = { fanotify: { ignore_unknown_filesystems: false } };
const migratedAdvancedPolicy = { fanotify: { ignore_unknown_filesystems: false } };
const migratedAttackSurfaceReductionPolicy = { credential_hardening: { enabled: false } };
policy.linux.advanced = policy.linux.advanced
? { ...policy.linux.advanced, ...migratedPolicy }
: { ...migratedPolicy };
? { ...policy.linux.advanced, ...migratedAdvancedPolicy }
: { ...migratedAdvancedPolicy };
policy.windows.attack_surface_reduction = migratedAttackSurfaceReductionPolicy;
}
return updatedPackagePolicyDoc;

View file

@ -63,6 +63,11 @@ export const policyFactory = (): PolicyConfig => {
antivirus_registration: {
enabled: false,
},
attack_surface_reduction: {
credential_hardening: {
enabled: true,
},
},
},
mac: {
events: {
@ -169,6 +174,11 @@ export const policyFactoryWithoutPaidFeatures = (
mode: ProtectionModes.off,
supported: false,
},
attack_surface_reduction: {
credential_hardening: {
enabled: false,
},
},
popup: {
...policy.windows.popup,
malware: {

View file

@ -952,6 +952,11 @@ export interface PolicyConfig {
antivirus_registration: {
enabled: boolean;
};
attack_surface_reduction: {
credential_hardening: {
enabled: boolean;
};
};
};
mac: {
advanced?: {};
@ -1029,6 +1034,7 @@ export interface UIPolicyConfig {
| 'advanced'
| 'memory_protection'
| 'behavior_protection'
| 'attack_surface_reduction'
>;
/**
* Mac-specific policy configuration that is supported via the UI

View file

@ -140,6 +140,23 @@ describe('policy_config and licenses', () => {
expect(valid).toBeFalsy();
});
it('allows credential hardening option when Platinum', () => {
const policy = policyFactory();
policy.windows.attack_surface_reduction.credential_hardening.enabled = true; // make policy change
const valid = isEndpointPolicyValidForLicense(policy, Platinum);
expect(valid).toBeTruthy();
});
it('blocks credential hardening option when below Platinum', () => {
const policy = policyFactory();
policy.windows.attack_surface_reduction.credential_hardening.enabled = true; // make policy change
let valid = isEndpointPolicyValidForLicense(policy, Gold);
expect(valid).toBeFalsy();
valid = isEndpointPolicyValidForLicense(policy, Basic);
expect(valid).toBeFalsy();
});
describe('ransomware protection checks', () => {
it('blocks ransomware to be turned on for Gold and below licenses', () => {
const policy = policyFactoryWithoutPaidFeatures();

View file

@ -202,6 +202,28 @@ function isEndpointBehaviorPolicyValidForLicense(policy: PolicyConfig, license:
return true;
}
function isEndpointCredentialDumpingPolicyValidForLicense(
policy: PolicyConfig,
license: ILicense | null
) {
if (isAtLeast(license, 'platinum')) {
// platinum allows all advanced features
return true;
}
const defaults = policyFactoryWithoutPaidFeatures();
// only platinum or higher may use credential hardening
if (
policy.windows.attack_surface_reduction.credential_hardening.enabled !==
defaults.windows.attack_surface_reduction.credential_hardening.enabled
) {
return false;
}
return true;
}
function isEndpointAdvancedPolicyValidForLicense(policy: PolicyConfig, license: ILicense | null) {
if (isAtLeast(license, 'platinum')) {
// platinum allows all advanced features
@ -231,7 +253,8 @@ export const isEndpointPolicyValidForLicense = (
isEndpointRansomwarePolicyValidForLicense(policy, license) &&
isEndpointMemoryPolicyValidForLicense(policy, license) &&
isEndpointBehaviorPolicyValidForLicense(policy, license) &&
isEndpointAdvancedPolicyValidForLicense(policy, license)
isEndpointAdvancedPolicyValidForLicense(policy, license) &&
isEndpointCredentialDumpingPolicyValidForLicense(policy, license)
);
};

View file

@ -40,6 +40,13 @@ export interface UserChangedAntivirusRegistration {
};
}
export interface UserChangedCredentialHardening {
type: 'userChangedCredentialHardening';
payload: {
enabled: boolean;
};
}
export interface ServerReturnedPolicyDetailsAgentSummaryData {
type: 'serverReturnedPolicyDetailsAgentSummaryData';
payload: {
@ -78,4 +85,5 @@ export type PolicySettingsAction =
| ServerFailedToReturnPolicyDetailsData
| UserChangedPolicyConfig
| UserChangedAntivirusRegistration
| UserChangedCredentialHardening
| LicenseChanged;

View file

@ -285,6 +285,11 @@ describe('policy details: ', () => {
memory_protection: { mode: 'off', supported: false },
behavior_protection: { mode: 'off', supported: false },
ransomware: { mode: 'off', supported: false },
attack_surface_reduction: {
credential_hardening: {
enabled: false,
},
},
popup: {
malware: {
enabled: true,

View file

@ -174,5 +174,28 @@ export const policySettingsReducer: ImmutableReducer<PolicyDetailsState, AppActi
}
}
if (action.type === 'userChangedCredentialHardening') {
if (state.policyItem) {
const policyConfig = fullPolicy(state);
return {
...state,
policyItem: updatePolicyConfigInPolicyData(state.policyItem, {
...policyConfig,
windows: {
...policyConfig.windows,
attack_surface_reduction: {
credential_hardening: {
enabled: action.payload.enabled,
},
},
},
}),
};
} else {
return state;
}
}
return state;
};

View file

@ -164,6 +164,7 @@ export const policyConfig: (s: PolicyDetailsState) => UIPolicyConfig = createSel
behavior_protection: windows.behavior_protection,
popup: windows.popup,
antivirus_registration: windows.antivirus_registration,
attack_surface_reduction: windows.attack_surface_reduction,
},
mac: {
advanced: mac.advanced,
@ -189,6 +190,10 @@ export const isAntivirusRegistrationEnabled = createSelector(policyConfig, (uiPo
return uiPolicyConfig.windows.antivirus_registration.enabled;
});
export const isCredentialHardeningEnabled = createSelector(policyConfig, (uiPolicyConfig) => {
return uiPolicyConfig.windows.attack_surface_reduction.credential_hardening.enabled;
});
/** Returns the total number of possible windows eventing configurations */
export const totalWindowsEvents = (state: PolicyDetailsState): number => {
const config = policyConfig(state);

View file

@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { memo, useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { EuiSwitch } from '@elastic/eui';
import { OperatingSystem } from '@kbn/securitysolution-utils';
import { isCredentialHardeningEnabled } from '../../../store/policy_details/selectors';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { ConfigForm } from '../config_form';
const TRANSLATIONS: Readonly<{ [K in 'title' | 'label']: string }> = {
title: i18n.translate(
'xpack.securitySolution.endpoint.policy.details.attackSurfaceReduction.type',
{
defaultMessage: 'Attack surface reduction',
}
),
label: i18n.translate(
'xpack.securitySolution.endpoint.policy.details.credentialHardening.toggle',
{
defaultMessage: 'Credential hardening',
}
),
};
export const AttackSurfaceReductionForm = memo(() => {
const credentialHardeningEnabled = usePolicyDetailsSelector(isCredentialHardeningEnabled);
const dispatch = useDispatch();
const handleSwitchChange = useCallback(
(event) =>
dispatch({
type: 'userChangedCredentialHardening',
payload: {
enabled: event.target.checked,
},
}),
[dispatch]
);
return (
<ConfigForm type={TRANSLATIONS.title} supportedOss={[OperatingSystem.WINDOWS]}>
<EuiSwitch
label={TRANSLATIONS.label}
checked={credentialHardeningEnabled}
onChange={handleSwitchChange}
/>
</ConfigForm>
);
});
AttackSurfaceReductionForm.displayName = 'AttackSurfaceReductionForm';

View file

@ -15,6 +15,7 @@ import { BehaviorProtection } from './policy_forms/protections/behavior';
import { LinuxEvents, MacEvents, WindowsEvents } from './policy_forms/events';
import { AdvancedPolicyForms } from './policy_advanced';
import { AntivirusRegistrationForm } from './components/antivirus_registration_form';
import { AttackSurfaceReductionForm } from './components/attack_surface_reduction_form';
import { Ransomware } from './policy_forms/protections/ransomware';
import { LockedPolicyCard } from './policy_forms/locked_card';
import { useLicense } from '../../../../common/hooks/use_license';
@ -40,6 +41,13 @@ const LOCKED_CARD_BEHAVIOR_TITLE = i18n.translate(
}
);
const LOCKED_CARD_ATTACK_SURFACE_REDUCTION = i18n.translate(
'xpack.securitySolution.endpoint.policy.details.attack_surface_reduction',
{
defaultMessage: 'Attack Surface Reduction',
}
);
export const PolicyDetailsForm = memo(() => {
const [showAdvancedPolicy, setShowAdvancedPolicy] = useState<boolean>(false);
const handleAdvancedPolicyClick = useCallback(() => {
@ -75,6 +83,12 @@ export const PolicyDetailsForm = memo(() => {
<LockedPolicyCard title={LOCKED_CARD_BEHAVIOR_TITLE} />
)}
<EuiSpacer size="l" />
{isPlatinumPlus ? (
<AttackSurfaceReductionForm />
) : (
<LockedPolicyCard title={LOCKED_CARD_ATTACK_SURFACE_REDUCTION} />
)}
<EuiSpacer size="l" />
<EuiText size="xs" color="subdued">
<h4>

View file

@ -320,9 +320,9 @@ describe('Policy Form Layout', () => {
expect(ransomware).toHaveLength(0);
});
it('shows the locked card in place of 1 paid feature', () => {
it('shows the locked card in place of paid features', () => {
const lockedCard = policyFormLayoutView.find('EuiCard[data-test-subj="lockedPolicyCard"]');
expect(lockedCard).toHaveLength(3);
expect(lockedCard).toHaveLength(4);
});
});
});