mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[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:
parent
0d237ea1c0
commit
aec761efbe
13 changed files with 187 additions and 10 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue