forked from p15670423/monkey
Merge pull request #2206 from guardicore/1242-allow-custom-ransomware-extension
1242 allow custom ransomware extension
This commit is contained in:
commit
ce390e41b8
|
@ -21,6 +21,8 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- `/api/registration-status` endpoint. #2149
|
- `/api/registration-status` endpoint. #2149
|
||||||
- authentication to `/api/island/version`. #2109
|
- authentication to `/api/island/version`. #2109
|
||||||
- `/api/events` endpoint. #2155
|
- `/api/events` endpoint. #2155
|
||||||
|
- The ability to customize the file extension used by ransomware when
|
||||||
|
encrypting files. #1242
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Reset workflow. Now it's possible to delete data gathered by agents without
|
- Reset workflow. Now it's possible to delete data gathered by agents without
|
||||||
|
|
|
@ -37,15 +37,21 @@ To ensure minimum interference and easy recoverability, the ransomware
|
||||||
simulation will only encrypt files contained in a user-specified directory. If
|
simulation will only encrypt files contained in a user-specified directory. If
|
||||||
no directory is specified, no files will be encrypted.
|
no directory is specified, no files will be encrypted.
|
||||||
|
|
||||||
|
Infection Monkey appends the `.m0nk3y` file extension to files that it
|
||||||
|
encrypts. You may optionally provide a custom file extension for Infection
|
||||||
|
Monkey to use instead. You can even provide no file extension, but take
|
||||||
|
caution: you'll no longer be able to tell if the file has been encrypted based
|
||||||
|
on the filename alone!
|
||||||
|
|
||||||
![Ransomware configuration](/images/usage/scenarios/ransomware-config.png "Ransomware configuration")
|
![Ransomware configuration](/images/usage/scenarios/ransomware-config.png "Ransomware configuration")
|
||||||
|
|
||||||
### How are the files encrypted?
|
### How are the files encrypted?
|
||||||
|
|
||||||
Files are "encrypted" in place with a simple bit flip. Encrypted files are
|
Files are "encrypted" in place with a simple bit flip. Encrypted files are
|
||||||
renamed to have `.m0nk3y` appended to their names. This is a safe way to
|
renamed to have a file extension (`.m0nk3y` by default) appended to their
|
||||||
simulate encryption since it is easy to "decrypt" your files. You can simply
|
names. This is a safe way to simulate encryption since it is easy to "decrypt"
|
||||||
perform a bit flip on the files again and rename them to remove the appended
|
your files. You can simply perform a bit flip on the files again and rename
|
||||||
`.m0nk3y` extension.
|
them to remove the appended `.m0nk3y` extension.
|
||||||
|
|
||||||
Flipping a file's bits is sufficient to simulate the encryption behavior of
|
Flipping a file's bits is sufficient to simulate the encryption behavior of
|
||||||
ransomware, as the data in your files has been manipulated (leaving them
|
ransomware, as the data in your files has been manipulated (leaving them
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 436 KiB |
|
@ -35,6 +35,7 @@ CREDENTIAL_COLLECTOR_CONFIGURATION = tuple(
|
||||||
RANSOMWARE_OPTIONS = {
|
RANSOMWARE_OPTIONS = {
|
||||||
"encryption": {
|
"encryption": {
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
|
"file_extension": ".m0nk3y",
|
||||||
"directories": {"linux_target_dir": "", "windows_target_dir": ""},
|
"directories": {"linux_target_dir": "", "windows_target_dir": ""},
|
||||||
},
|
},
|
||||||
"other_behaviors": {"readme": True},
|
"other_behaviors": {"readme": True},
|
||||||
|
|
|
@ -16,7 +16,6 @@ from .ransomware import Ransomware
|
||||||
from .ransomware_options import RansomwareOptions
|
from .ransomware_options import RansomwareOptions
|
||||||
from .targeted_file_extensions import TARGETED_FILE_EXTENSIONS
|
from .targeted_file_extensions import TARGETED_FILE_EXTENSIONS
|
||||||
|
|
||||||
EXTENSION = ".m0nk3y"
|
|
||||||
CHUNK_SIZE = 4096 * 24
|
CHUNK_SIZE = 4096 * 24
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -26,8 +25,8 @@ def build_ransomware(options: dict):
|
||||||
logger.debug(f"Ransomware configuration:\n{pformat(options)}")
|
logger.debug(f"Ransomware configuration:\n{pformat(options)}")
|
||||||
ransomware_options = RansomwareOptions(options)
|
ransomware_options = RansomwareOptions(options)
|
||||||
|
|
||||||
file_encryptor = _build_file_encryptor()
|
file_encryptor = _build_file_encryptor(ransomware_options.file_extension)
|
||||||
file_selector = _build_file_selector()
|
file_selector = _build_file_selector(ransomware_options.file_extension)
|
||||||
leave_readme = _build_leave_readme()
|
leave_readme = _build_leave_readme()
|
||||||
telemetry_messenger = _build_telemetry_messenger()
|
telemetry_messenger = _build_telemetry_messenger()
|
||||||
|
|
||||||
|
@ -40,15 +39,16 @@ def build_ransomware(options: dict):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _build_file_encryptor():
|
def _build_file_encryptor(file_extension: str):
|
||||||
return InPlaceFileEncryptor(
|
return InPlaceFileEncryptor(
|
||||||
encrypt_bytes=flip_bits, new_file_extension=EXTENSION, chunk_size=CHUNK_SIZE
|
encrypt_bytes=flip_bits, new_file_extension=file_extension, chunk_size=CHUNK_SIZE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _build_file_selector():
|
def _build_file_selector(file_extension: str):
|
||||||
targeted_file_extensions = TARGETED_FILE_EXTENSIONS.copy()
|
targeted_file_extensions = TARGETED_FILE_EXTENSIONS.copy()
|
||||||
targeted_file_extensions.discard(EXTENSION)
|
if file_extension:
|
||||||
|
targeted_file_extensions.discard(file_extension)
|
||||||
|
|
||||||
return ProductionSafeTargetFileSelector(targeted_file_extensions)
|
return ProductionSafeTargetFileSelector(targeted_file_extensions)
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ logger = logging.getLogger(__name__)
|
||||||
class RansomwareOptions:
|
class RansomwareOptions:
|
||||||
def __init__(self, options: dict):
|
def __init__(self, options: dict):
|
||||||
self.encryption_enabled = options["encryption"]["enabled"]
|
self.encryption_enabled = options["encryption"]["enabled"]
|
||||||
|
self.file_extension = options["encryption"]["file_extension"]
|
||||||
self.readme_enabled = options["other_behaviors"]["readme"]
|
self.readme_enabled = options["other_behaviors"]["readme"]
|
||||||
|
|
||||||
self.target_directory = None
|
self.target_directory = None
|
||||||
|
|
|
@ -2,7 +2,7 @@ import AdvancedMultiSelect from '../ui-components/AdvancedMultiSelect';
|
||||||
import InfoBox from './InfoBox';
|
import InfoBox from './InfoBox';
|
||||||
import TextBox from './TextBox.js';
|
import TextBox from './TextBox.js';
|
||||||
import PbaInput from './PbaInput';
|
import PbaInput from './PbaInput';
|
||||||
import {API_PBA_LINUX, API_PBA_WINDOWS} from '../pages/ConfigurePage';
|
import { API_PBA_LINUX, API_PBA_WINDOWS } from '../pages/ConfigurePage';
|
||||||
import SensitiveTextInput from '../ui-components/SensitiveTextInput';
|
import SensitiveTextInput from '../ui-components/SensitiveTextInput';
|
||||||
|
|
||||||
export default function UiSchema(props) {
|
export default function UiSchema(props) {
|
||||||
|
@ -45,13 +45,13 @@ export default function UiSchema(props) {
|
||||||
'ui:widget': SensitiveTextInput
|
'ui:widget': SensitiveTextInput
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
exploit_lm_hash_list:{
|
exploit_lm_hash_list: {
|
||||||
items: {
|
items: {
|
||||||
classNames: 'config-template-no-header',
|
classNames: 'config-template-no-header',
|
||||||
'ui:widget': SensitiveTextInput
|
'ui:widget': SensitiveTextInput
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
exploit_ntlm_hash_list:{
|
exploit_ntlm_hash_list: {
|
||||||
items: {
|
items: {
|
||||||
classNames: 'config-template-no-header',
|
classNames: 'config-template-no-header',
|
||||||
'ui:widget': SensitiveTextInput
|
'ui:widget': SensitiveTextInput
|
||||||
|
@ -82,11 +82,11 @@ export default function UiSchema(props) {
|
||||||
tcp: {
|
tcp: {
|
||||||
ports: {
|
ports: {
|
||||||
items: {
|
items: {
|
||||||
classNames: 'config-template-no-header'
|
classNames: 'config-template-no-header'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fingerprinters:{
|
fingerprinters: {
|
||||||
classNames: 'config-template-no-header',
|
classNames: 'config-template-no-header',
|
||||||
'ui:widget': AdvancedMultiSelect,
|
'ui:widget': AdvancedMultiSelect,
|
||||||
fingerprinter_classes: {
|
fingerprinter_classes: {
|
||||||
|
@ -99,9 +99,12 @@ export default function UiSchema(props) {
|
||||||
payloads: {
|
payloads: {
|
||||||
classNames: 'config-template-no-header',
|
classNames: 'config-template-no-header',
|
||||||
encryption: {
|
encryption: {
|
||||||
info_box : {
|
info_box: {
|
||||||
'ui:field': InfoBox
|
'ui:field': InfoBox
|
||||||
},
|
},
|
||||||
|
file_extension: {
|
||||||
|
'ui:emptyValue': ''
|
||||||
|
},
|
||||||
directories: {
|
directories: {
|
||||||
// Directory inputs are dynamically hidden
|
// Directory inputs are dynamically hidden
|
||||||
},
|
},
|
||||||
|
@ -112,7 +115,7 @@ export default function UiSchema(props) {
|
||||||
'ui:widget': 'hidden'
|
'ui:widget': 'hidden'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
other_behaviors : {
|
other_behaviors: {
|
||||||
'ui:widget': 'hidden'
|
'ui:widget': 'hidden'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {IP, IP_RANGE, VALID_RANSOMWARE_TARGET_PATH_LINUX, VALID_RANSOMWARE_TARGET_PATH_WINDOWS} from './ValidationFormats';
|
import { IP, IP_RANGE, VALID_FILE_EXTENSION, VALID_RANSOMWARE_TARGET_PATH_LINUX, VALID_RANSOMWARE_TARGET_PATH_WINDOWS } from './ValidationFormats';
|
||||||
|
|
||||||
let invalidDirMessage = 'Invalid directory. Path should be absolute or begin with an environment variable.';
|
let invalidDirMessage = 'Invalid directory. Path should be absolute or begin with an environment variable.';
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ export default function transformErrors(errors) {
|
||||||
error.message = 'Invalid IP range, refer to description for valid examples.'
|
error.message = 'Invalid IP range, refer to description for valid examples.'
|
||||||
} else if (error.name === 'format' && error.params.format === IP) {
|
} else if (error.name === 'format' && error.params.format === IP) {
|
||||||
error.message = 'Invalid IP.'
|
error.message = 'Invalid IP.'
|
||||||
|
} else if (error.name === 'format' && error.params.format === VALID_FILE_EXTENSION) {
|
||||||
|
error.message = 'Invalid file extension.'
|
||||||
} else if (error.name === 'format' && error.params.format === VALID_RANSOMWARE_TARGET_PATH_LINUX) {
|
} else if (error.name === 'format' && error.params.format === VALID_RANSOMWARE_TARGET_PATH_LINUX) {
|
||||||
error.message = invalidDirMessage
|
error.message = invalidDirMessage
|
||||||
} else if (error.name === 'format' && error.params.format === VALID_RANSOMWARE_TARGET_PATH_WINDOWS) {
|
} else if (error.name === 'format' && error.params.format === VALID_RANSOMWARE_TARGET_PATH_WINDOWS) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ const ipRegex = '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0
|
||||||
const cidrNotationRegex = '([0-9]|1[0-9]|2[0-9]|3[0-2])'
|
const cidrNotationRegex = '([0-9]|1[0-9]|2[0-9]|3[0-2])'
|
||||||
const hostnameRegex = '^([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*.?)*([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*)$'
|
const hostnameRegex = '^([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*.?)*([A-Za-z0-9]*[A-Za-z]+[A-Za-z0-9]*)$'
|
||||||
|
|
||||||
|
const fileExtensionRegex = /^(\.[A-Za-z0-9_]+)*$/
|
||||||
|
|
||||||
const linuxAbsolutePathRegex = /^\// // path starts with `/`
|
const linuxAbsolutePathRegex = /^\// // path starts with `/`
|
||||||
const linuxPathStartsWithEnvVariableRegex = /^\$/ // path starts with `$`
|
const linuxPathStartsWithEnvVariableRegex = /^\$/ // path starts with `$`
|
||||||
|
@ -11,7 +12,7 @@ const linuxPathStartsWithTildeRegex = /^~/ // path starts with `~`
|
||||||
const windowsAbsolutePathRegex = /^([A-Za-z]:(\\|\/))/ // path starts like `C:\` OR `C:/`
|
const windowsAbsolutePathRegex = /^([A-Za-z]:(\\|\/))/ // path starts like `C:\` OR `C:/`
|
||||||
const windowsEnvVarNonNumeric = '[A-Za-z#\\$\'\\(\\)\\*\\+,\\-\\.\\?@\\[\\]_`\\{\\}~ ]'
|
const windowsEnvVarNonNumeric = '[A-Za-z#\\$\'\\(\\)\\*\\+,\\-\\.\\?@\\[\\]_`\\{\\}~ ]'
|
||||||
const windowsPathStartsWithEnvVariableRegex = new RegExp(
|
const windowsPathStartsWithEnvVariableRegex = new RegExp(
|
||||||
`^%(${windowsEnvVarNonNumeric}+(${windowsEnvVarNonNumeric}|\\d)*)%`
|
`^%(${windowsEnvVarNonNumeric}+(${windowsEnvVarNonNumeric}|\\d)*)%`
|
||||||
) // path starts like `$` OR `%abc%`
|
) // path starts like `$` OR `%abc%`
|
||||||
const windowsUncPathRegex = /^\\{2}/ // Path starts like `\\`
|
const windowsUncPathRegex = /^\\{2}/ // Path starts like `\\`
|
||||||
const emptyRegex = /^$/
|
const emptyRegex = /^$/
|
||||||
|
@ -19,32 +20,34 @@ const emptyRegex = /^$/
|
||||||
|
|
||||||
export const IP_RANGE = 'ip-range';
|
export const IP_RANGE = 'ip-range';
|
||||||
export const IP = 'ip';
|
export const IP = 'ip';
|
||||||
|
export const VALID_FILE_EXTENSION = 'valid-file-extension'
|
||||||
export const VALID_RANSOMWARE_TARGET_PATH_LINUX = 'valid-ransomware-target-path-linux'
|
export const VALID_RANSOMWARE_TARGET_PATH_LINUX = 'valid-ransomware-target-path-linux'
|
||||||
export const VALID_RANSOMWARE_TARGET_PATH_WINDOWS = 'valid-ransomware-target-path-windows'
|
export const VALID_RANSOMWARE_TARGET_PATH_WINDOWS = 'valid-ransomware-target-path-windows'
|
||||||
|
|
||||||
export const formValidationFormats = {
|
export const formValidationFormats = {
|
||||||
[IP_RANGE]: buildIpRangeRegex(),
|
[IP_RANGE]: buildIpRangeRegex(),
|
||||||
[IP]: buildIpRegex(),
|
[IP]: buildIpRegex(),
|
||||||
|
[VALID_FILE_EXTENSION]: fileExtensionRegex,
|
||||||
[VALID_RANSOMWARE_TARGET_PATH_LINUX]: buildValidRansomwarePathLinuxRegex(),
|
[VALID_RANSOMWARE_TARGET_PATH_LINUX]: buildValidRansomwarePathLinuxRegex(),
|
||||||
[VALID_RANSOMWARE_TARGET_PATH_WINDOWS]: buildValidRansomwarePathWindowsRegex()
|
[VALID_RANSOMWARE_TARGET_PATH_WINDOWS]: buildValidRansomwarePathWindowsRegex()
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildIpRangeRegex(){
|
function buildIpRangeRegex() {
|
||||||
return new RegExp([
|
return new RegExp([
|
||||||
'^'+ipRegex+'$|', // Single: IP
|
'^' + ipRegex + '$|', // Single: IP
|
||||||
'^'+ipRegex+'-'+ipRegex+'$|', // IP range: IP-IP
|
'^' + ipRegex + '-' + ipRegex + '$|', // IP range: IP-IP
|
||||||
'^'+ipRegex+'/'+cidrNotationRegex+'$|', // IP range with cidr notation: IP/cidr
|
'^' + ipRegex + '/' + cidrNotationRegex + '$|', // IP range with cidr notation: IP/cidr
|
||||||
hostnameRegex // Hostname: target.tg
|
hostnameRegex // Hostname: target.tg
|
||||||
].join(''))
|
].join(''))
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildIpRegex(){
|
function buildIpRegex() {
|
||||||
return new RegExp('^'+ipRegex+'$')
|
return new RegExp('^' + ipRegex + '$')
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildValidRansomwarePathLinuxRegex() {
|
function buildValidRansomwarePathLinuxRegex() {
|
||||||
return new RegExp([
|
return new RegExp([
|
||||||
emptyRegex.source,
|
emptyRegex.source,
|
||||||
linuxAbsolutePathRegex.source,
|
linuxAbsolutePathRegex.source,
|
||||||
linuxPathStartsWithEnvVariableRegex.source,
|
linuxPathStartsWithEnvVariableRegex.source,
|
||||||
linuxPathStartsWithTildeRegex.source
|
linuxPathStartsWithTildeRegex.source
|
||||||
|
@ -53,7 +56,7 @@ function buildValidRansomwarePathLinuxRegex() {
|
||||||
|
|
||||||
function buildValidRansomwarePathWindowsRegex() {
|
function buildValidRansomwarePathWindowsRegex() {
|
||||||
return new RegExp([
|
return new RegExp([
|
||||||
emptyRegex.source,
|
emptyRegex.source,
|
||||||
windowsAbsolutePathRegex.source,
|
windowsAbsolutePathRegex.source,
|
||||||
windowsPathStartsWithEnvVariableRegex.source,
|
windowsPathStartsWithEnvVariableRegex.source,
|
||||||
windowsUncPathRegex.source
|
windowsUncPathRegex.source
|
||||||
|
|
|
@ -1,24 +1,32 @@
|
||||||
const RANSOMWARE_SCHEMA = {
|
const RANSOMWARE_SCHEMA = {
|
||||||
'title': 'Payloads',
|
'title': 'Payloads',
|
||||||
'properties': {
|
'properties': {
|
||||||
'encryption': {
|
'encryption': {
|
||||||
'title': 'Ransomware simulation',
|
'title': 'Ransomware simulation',
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'description': 'To simulate ransomware encryption, you\'ll need to provide Infection ' +
|
'description': 'To simulate ransomware encryption, you\'ll need to provide Infection ' +
|
||||||
'Monkey with files that it can safely encrypt. On each machine where you would like ' +
|
'Monkey with files that it can safely encrypt. On each machine where you would like ' +
|
||||||
'the ransomware simulation to run, create a directory and put some files in it.' +
|
'the ransomware simulation to run, create a directory and put some files in it.' +
|
||||||
'\n\nProvide the path to the directory that was created on each machine.',
|
'\n\nProvide the path to the directory that was created on each machine.',
|
||||||
'properties': {
|
'properties': {
|
||||||
'enabled': {
|
'enabled': {
|
||||||
'title': 'Encrypt files',
|
'title': 'Encrypt files',
|
||||||
'type': 'boolean',
|
'type': 'boolean',
|
||||||
'default': true,
|
'default': true,
|
||||||
'description': 'Ransomware encryption will be simulated by flipping every bit ' +
|
'description': 'Ransomware encryption will be simulated by flipping every bit ' +
|
||||||
'in the files contained within the target directories.'
|
'in the files contained within the target directories.'
|
||||||
},
|
},
|
||||||
'info_box': {
|
'info_box': {
|
||||||
'info': 'No files will be encrypted if a directory is not specified or doesn\'t ' +
|
'info': 'No files will be encrypted if a directory is not specified or doesn\'t ' +
|
||||||
'exist on a victim machine.'
|
'exist on a victim machine.'
|
||||||
|
},
|
||||||
|
'file_extension': {
|
||||||
|
'title': 'File extension',
|
||||||
|
'type': 'string',
|
||||||
|
'format': 'valid-file-extension',
|
||||||
|
'default': '.m0nk3y',
|
||||||
|
'description': 'The file extension that the Infection Monkey will use for the ' +
|
||||||
|
'encrypted file.'
|
||||||
},
|
},
|
||||||
'directories': {
|
'directories': {
|
||||||
'title': 'Directories to encrypt',
|
'title': 'Directories to encrypt',
|
||||||
|
@ -30,8 +38,8 @@ const RANSOMWARE_SCHEMA = {
|
||||||
'format': 'valid-ransomware-target-path-linux',
|
'format': 'valid-ransomware-target-path-linux',
|
||||||
'default': '',
|
'default': '',
|
||||||
'description': 'A path to a directory on Linux systems that contains ' +
|
'description': 'A path to a directory on Linux systems that contains ' +
|
||||||
'files that you will allow Infection Monkey to encrypt. If no ' +
|
'files that you will allow Infection Monkey to encrypt. If no ' +
|
||||||
'directory is specified, no files will be encrypted.'
|
'directory is specified, no files will be encrypted.'
|
||||||
},
|
},
|
||||||
'windows_target_dir': {
|
'windows_target_dir': {
|
||||||
'title': 'Windows target directory',
|
'title': 'Windows target directory',
|
||||||
|
@ -39,8 +47,8 @@ const RANSOMWARE_SCHEMA = {
|
||||||
'format': 'valid-ransomware-target-path-windows',
|
'format': 'valid-ransomware-target-path-windows',
|
||||||
'default': '',
|
'default': '',
|
||||||
'description': 'A path to a directory on Windows systems that contains ' +
|
'description': 'A path to a directory on Windows systems that contains ' +
|
||||||
'files that you will allow Infection Monkey to encrypt. If no ' +
|
'files that you will allow Infection Monkey to encrypt. If no ' +
|
||||||
'directory is specified, no files will be encrypted.'
|
'directory is specified, no files will be encrypted.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=[".m0nk3y", ".test", ""], ids=["monkeyext", "testext", "noext"])
|
||||||
|
def ransomware_file_extension(request):
|
||||||
|
return request.param
|
|
@ -0,0 +1,29 @@
|
||||||
|
import threading
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import infection_monkey.payload.ransomware.ransomware_builder as ransomware_builder
|
||||||
|
from monkey.common.agent_configuration.default_agent_configuration import RANSOMWARE_OPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def ransomware_options_dict(ransomware_file_extension):
|
||||||
|
options = RANSOMWARE_OPTIONS
|
||||||
|
options["encryption"]["file_extension"] = ransomware_file_extension
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
def test_uses_correct_extension(ransomware_options_dict, tmp_path, ransomware_file_extension):
|
||||||
|
target_dir = tmp_path
|
||||||
|
ransomware_directories = ransomware_options_dict["encryption"]["directories"]
|
||||||
|
ransomware_directories["linux_target_dir"] = target_dir
|
||||||
|
ransomware_directories["windows_target_dir"] = target_dir
|
||||||
|
ransomware = ransomware_builder.build_ransomware(ransomware_options_dict)
|
||||||
|
file = target_dir / "file.txt"
|
||||||
|
file.write_text("Do your worst!")
|
||||||
|
|
||||||
|
ransomware.run(threading.Event())
|
||||||
|
|
||||||
|
# Verify that the file has been encrypted with the correct ending
|
||||||
|
encrypted_file = file.with_suffix(file.suffix + ransomware_file_extension)
|
||||||
|
assert encrypted_file.is_file()
|
|
@ -41,14 +41,15 @@ def build_ransomware(
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def ransomware_options(ransomware_test_data):
|
def ransomware_options(ransomware_file_extension, ransomware_test_data):
|
||||||
class RansomwareOptionsStub(RansomwareOptions):
|
class RansomwareOptionsStub(RansomwareOptions):
|
||||||
def __init__(self, encryption_enabled, readme_enabled, target_directory):
|
def __init__(self, encryption_enabled, readme_enabled, file_extension, target_directory):
|
||||||
self.encryption_enabled = encryption_enabled
|
self.encryption_enabled = encryption_enabled
|
||||||
self.readme_enabled = readme_enabled
|
self.readme_enabled = readme_enabled
|
||||||
|
self.file_extension = file_extension
|
||||||
self.target_directory = target_directory
|
self.target_directory = target_directory
|
||||||
|
|
||||||
return RansomwareOptionsStub(True, False, ransomware_test_data)
|
return RansomwareOptionsStub(True, False, ransomware_file_extension, ransomware_test_data)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
@ -7,6 +7,7 @@ from common.utils.file_utils import InvalidPath
|
||||||
from infection_monkey.payload.ransomware import ransomware_options
|
from infection_monkey.payload.ransomware import ransomware_options
|
||||||
from infection_monkey.payload.ransomware.ransomware_options import RansomwareOptions
|
from infection_monkey.payload.ransomware.ransomware_options import RansomwareOptions
|
||||||
|
|
||||||
|
EXTENSION = ".testext"
|
||||||
LINUX_DIR = "/tmp/test"
|
LINUX_DIR = "/tmp/test"
|
||||||
WINDOWS_DIR = "C:\\tmp\\test"
|
WINDOWS_DIR = "C:\\tmp\\test"
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ def options_from_island():
|
||||||
return {
|
return {
|
||||||
"encryption": {
|
"encryption": {
|
||||||
"enabled": None,
|
"enabled": None,
|
||||||
|
"file_extension": EXTENSION,
|
||||||
"directories": {
|
"directories": {
|
||||||
"linux_target_dir": LINUX_DIR,
|
"linux_target_dir": LINUX_DIR,
|
||||||
"windows_target_dir": WINDOWS_DIR,
|
"windows_target_dir": WINDOWS_DIR,
|
||||||
|
@ -41,6 +43,12 @@ def test_readme_enabled(enabled, options_from_island):
|
||||||
assert options.readme_enabled == enabled
|
assert options.readme_enabled == enabled
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_extension(options_from_island):
|
||||||
|
options = RansomwareOptions(options_from_island)
|
||||||
|
|
||||||
|
assert options.file_extension == EXTENSION
|
||||||
|
|
||||||
|
|
||||||
def test_linux_target_dir(monkeypatch, options_from_island):
|
def test_linux_target_dir(monkeypatch, options_from_island):
|
||||||
monkeypatch.setattr(ransomware_options, "is_windows_os", lambda: False)
|
monkeypatch.setattr(ransomware_options, "is_windows_os", lambda: False)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ log_cli = 1
|
||||||
log_cli_level = "DEBUG"
|
log_cli_level = "DEBUG"
|
||||||
log_cli_format = "%(asctime)s [%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s"
|
log_cli_format = "%(asctime)s [%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s"
|
||||||
log_cli_date_format = "%H:%M:%S"
|
log_cli_date_format = "%H:%M:%S"
|
||||||
addopts = "-v --capture=sys tests/unit_tests"
|
addopts = "-v --capture=sys tests/unit_tests tests/integration_tests"
|
||||||
norecursedirs = "node_modules dist"
|
norecursedirs = "node_modules dist"
|
||||||
markers = ["slow: mark test as slow"]
|
markers = ["slow: mark test as slow"]
|
||||||
pythonpath = "./monkey"
|
pythonpath = "./monkey"
|
||||||
|
|
Loading…
Reference in New Issue