forked from p15670423/monkey
Refactored process list collector
This commit is contained in:
parent
04b7370575
commit
2286571a72
|
@ -1,3 +1,4 @@
|
||||||
AWS_COLLECTOR = "AwsCollector"
|
AWS_COLLECTOR = "AwsCollector"
|
||||||
HOSTNAME_COLLECTOR = "HostnameCollector"
|
HOSTNAME_COLLECTOR = "HostnameCollector"
|
||||||
ENVIRONMENT_COLLECTOR = "EnvironmentCollector"
|
ENVIRONMENT_COLLECTOR = "EnvironmentCollector"
|
||||||
|
PROCESS_LIST_COLLECTOR = "ProcessListCollector"
|
||||||
|
|
|
@ -62,44 +62,12 @@ class InfoCollector(object):
|
||||||
|
|
||||||
def get_info(self):
|
def get_info(self):
|
||||||
# Collect all hardcoded
|
# Collect all hardcoded
|
||||||
self.get_process_list()
|
|
||||||
self.get_network_info()
|
self.get_network_info()
|
||||||
self.get_azure_info()
|
self.get_azure_info()
|
||||||
|
|
||||||
# Collect all plugins
|
# Collect all plugins
|
||||||
SystemInfoCollectorsHandler().execute_all_configured()
|
SystemInfoCollectorsHandler().execute_all_configured()
|
||||||
|
|
||||||
def get_process_list(self):
|
|
||||||
"""
|
|
||||||
Adds process information from the host to the system information.
|
|
||||||
Currently lists process name, ID, parent ID, command line
|
|
||||||
and the full image path of each process.
|
|
||||||
:return: None. Updates class information
|
|
||||||
"""
|
|
||||||
LOG.debug("Reading process list")
|
|
||||||
processes = {}
|
|
||||||
for process in psutil.process_iter():
|
|
||||||
try:
|
|
||||||
processes[process.pid] = {"name": process.name(),
|
|
||||||
"pid": process.pid,
|
|
||||||
"ppid": process.ppid(),
|
|
||||||
"cmdline": " ".join(process.cmdline()),
|
|
||||||
"full_image_path": process.exe(),
|
|
||||||
}
|
|
||||||
except (psutil.AccessDenied, WindowsError):
|
|
||||||
# we may be running as non root
|
|
||||||
# and some processes are impossible to acquire in Windows/Linux
|
|
||||||
# in this case we'll just add what we can
|
|
||||||
processes[process.pid] = {"name": "null",
|
|
||||||
"pid": process.pid,
|
|
||||||
"ppid": process.ppid(),
|
|
||||||
"cmdline": "ACCESS DENIED",
|
|
||||||
"full_image_path": "null",
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.info['process_list'] = processes
|
|
||||||
|
|
||||||
def get_network_info(self):
|
def get_network_info(self):
|
||||||
"""
|
"""
|
||||||
Adds network information from the host to the system information.
|
Adds network information from the host to the system information.
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import logging
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
from common.data.system_info_collectors_names import PROCESS_LIST_COLLECTOR
|
||||||
|
from infection_monkey.system_info.system_info_collector import SystemInfoCollector
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Linux doesn't have WindowsError
|
||||||
|
try:
|
||||||
|
WindowsError
|
||||||
|
except NameError:
|
||||||
|
# noinspection PyShadowingBuiltins
|
||||||
|
WindowsError = psutil.AccessDenied
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessListCollector(SystemInfoCollector):
|
||||||
|
def __init__(self):
|
||||||
|
super(ProcessListCollector, self).__init__(name=PROCESS_LIST_COLLECTOR)
|
||||||
|
|
||||||
|
def collect(self) -> dict:
|
||||||
|
"""
|
||||||
|
Adds process information from the host to the system information.
|
||||||
|
Currently lists process name, ID, parent ID, command line
|
||||||
|
and the full image path of each process.
|
||||||
|
"""
|
||||||
|
logger.debug("Reading process list")
|
||||||
|
processes = {}
|
||||||
|
for process in psutil.process_iter():
|
||||||
|
try:
|
||||||
|
processes[process.pid] = {
|
||||||
|
"name": process.name(),
|
||||||
|
"pid": process.pid,
|
||||||
|
"ppid": process.ppid(),
|
||||||
|
"cmdline": " ".join(process.cmdline()),
|
||||||
|
"full_image_path": process.exe(),
|
||||||
|
}
|
||||||
|
except (psutil.AccessDenied, WindowsError):
|
||||||
|
# we may be running as non root and some processes are impossible to acquire in Windows/Linux.
|
||||||
|
# In this case we'll just add what we know.
|
||||||
|
processes[process.pid] = {
|
||||||
|
"name": "null",
|
||||||
|
"pid": process.pid,
|
||||||
|
"ppid": process.ppid(),
|
||||||
|
"cmdline": "ACCESS DENIED",
|
||||||
|
"full_image_path": "null",
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
return {'process_list': processes}
|
|
@ -1,3 +1,5 @@
|
||||||
|
from common.data.system_info_collectors_names import *
|
||||||
|
|
||||||
WARNING_SIGN = " \u26A0"
|
WARNING_SIGN = " \u26A0"
|
||||||
|
|
||||||
SCHEMA = {
|
SCHEMA = {
|
||||||
|
@ -106,7 +108,7 @@ SCHEMA = {
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"EnvironmentCollector"
|
ENVIRONMENT_COLLECTOR
|
||||||
],
|
],
|
||||||
"title": "Collect which environment this machine is on (on prem/cloud)",
|
"title": "Collect which environment this machine is on (on prem/cloud)",
|
||||||
"attack_techniques": []
|
"attack_techniques": []
|
||||||
|
@ -114,7 +116,7 @@ SCHEMA = {
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"AwsCollector"
|
AWS_COLLECTOR
|
||||||
],
|
],
|
||||||
"title": "If on AWS, collect more information about the instance",
|
"title": "If on AWS, collect more information about the instance",
|
||||||
"attack_techniques": []
|
"attack_techniques": []
|
||||||
|
@ -122,11 +124,19 @@ SCHEMA = {
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"HostnameCollector"
|
HOSTNAME_COLLECTOR
|
||||||
],
|
],
|
||||||
"title": "Collect the machine's hostname",
|
"title": "Collect the machine's hostname",
|
||||||
"attack_techniques": []
|
"attack_techniques": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
PROCESS_LIST_COLLECTOR
|
||||||
|
],
|
||||||
|
"title": "Collect running processes on the machine",
|
||||||
|
"attack_techniques": []
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"post_breach_acts": {
|
"post_breach_acts": {
|
||||||
|
@ -471,9 +481,10 @@ SCHEMA = {
|
||||||
"$ref": "#/definitions/system_info_collectors_classes"
|
"$ref": "#/definitions/system_info_collectors_classes"
|
||||||
},
|
},
|
||||||
"default": [
|
"default": [
|
||||||
"EnvironmentCollector",
|
ENVIRONMENT_COLLECTOR,
|
||||||
"AwsCollector",
|
AWS_COLLECTOR,
|
||||||
"HostnameCollector"
|
HOSTNAME_COLLECTOR,
|
||||||
|
PROCESS_LIST_COLLECTOR
|
||||||
],
|
],
|
||||||
"description": "Determines which system information collectors will collect information."
|
"description": "Determines which system information collectors will collect information."
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,6 @@ from monkey_island.cc.services.config import ConfigService
|
||||||
from monkey_island.cc.services.node import NodeService
|
from monkey_island.cc.services.node import NodeService
|
||||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \
|
from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \
|
||||||
SystemInfoTelemetryDispatcher
|
SystemInfoTelemetryDispatcher
|
||||||
from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence
|
|
||||||
from monkey_island.cc.services.wmi_handler import WMIHandler
|
from monkey_island.cc.services.wmi_handler import WMIHandler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -18,8 +17,7 @@ def process_system_info_telemetry(telemetry_json):
|
||||||
process_ssh_info,
|
process_ssh_info,
|
||||||
process_credential_info,
|
process_credential_info,
|
||||||
process_mimikatz_and_wmi_info,
|
process_mimikatz_and_wmi_info,
|
||||||
test_antivirus_existence,
|
dispatcher.dispatch_collector_results_to_relevant_processors
|
||||||
dispatcher.dispatch_to_relevant_collectors
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Calling safe_process_telemetry so if one of the stages fail, we log and move on instead of failing the rest of
|
# Calling safe_process_telemetry so if one of the stages fail, we log and move on instead of failing the rest of
|
||||||
|
|
|
@ -5,6 +5,7 @@ from common.data.system_info_collectors_names import *
|
||||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.aws import process_aws_telemetry
|
from monkey_island.cc.services.telemetry.processing.system_info_collectors.aws import process_aws_telemetry
|
||||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.environment import process_environment_telemetry
|
from monkey_island.cc.services.telemetry.processing.system_info_collectors.environment import process_environment_telemetry
|
||||||
from monkey_island.cc.services.telemetry.processing.system_info_collectors.hostname import process_hostname_telemetry
|
from monkey_island.cc.services.telemetry.processing.system_info_collectors.hostname import process_hostname_telemetry
|
||||||
|
from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS = {
|
||||||
AWS_COLLECTOR: [process_aws_telemetry],
|
AWS_COLLECTOR: [process_aws_telemetry],
|
||||||
ENVIRONMENT_COLLECTOR: [process_environment_telemetry],
|
ENVIRONMENT_COLLECTOR: [process_environment_telemetry],
|
||||||
HOSTNAME_COLLECTOR: [process_hostname_telemetry],
|
HOSTNAME_COLLECTOR: [process_hostname_telemetry],
|
||||||
|
PROCESS_LIST_COLLECTOR: [test_antivirus_existence]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,9 +34,9 @@ class SystemInfoTelemetryDispatcher(object):
|
||||||
:param telemetry_json: Telemetry sent from the Monkey
|
:param telemetry_json: Telemetry sent from the Monkey
|
||||||
"""
|
"""
|
||||||
if "collectors" in telemetry_json["data"]:
|
if "collectors" in telemetry_json["data"]:
|
||||||
self.dispatch_each_result_to_relevant_processors(telemetry_json)
|
self.dispatch_single_result_to_relevant_processor(telemetry_json)
|
||||||
|
|
||||||
def dispatch_each_result_to_relevant_processors(self, telemetry_json):
|
def dispatch_single_result_to_relevant_processor(self, telemetry_json):
|
||||||
relevant_monkey_guid = telemetry_json['monkey_guid']
|
relevant_monkey_guid = telemetry_json['monkey_guid']
|
||||||
|
|
||||||
for collector_name, collector_results in telemetry_json["data"]["collectors"].items():
|
for collector_name, collector_results in telemetry_json["data"]["collectors"].items():
|
||||||
|
|
|
@ -7,36 +7,36 @@ from monkey_island.cc.models.zero_trust.event import Event
|
||||||
from monkey_island.cc.services.telemetry.zero_trust_tests.known_anti_viruses import ANTI_VIRUS_KNOWN_PROCESS_NAMES
|
from monkey_island.cc.services.telemetry.zero_trust_tests.known_anti_viruses import ANTI_VIRUS_KNOWN_PROCESS_NAMES
|
||||||
|
|
||||||
|
|
||||||
def test_antivirus_existence(telemetry_json):
|
def test_antivirus_existence(process_list_json, monkey_guid):
|
||||||
current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
|
current_monkey = Monkey.get_single_monkey_by_guid(monkey_guid)
|
||||||
if 'process_list' in telemetry_json['data']:
|
|
||||||
process_list_event = Event.create_event(
|
|
||||||
title="Process list",
|
|
||||||
message="Monkey on {} scanned the process list".format(current_monkey.hostname),
|
|
||||||
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL)
|
|
||||||
events = [process_list_event]
|
|
||||||
|
|
||||||
av_processes = filter_av_processes(telemetry_json)
|
process_list_event = Event.create_event(
|
||||||
|
title="Process list",
|
||||||
|
message="Monkey on {} scanned the process list".format(current_monkey.hostname),
|
||||||
|
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL)
|
||||||
|
events = [process_list_event]
|
||||||
|
|
||||||
for process in av_processes:
|
av_processes = filter_av_processes(process_list_json["process_list"])
|
||||||
events.append(Event.create_event(
|
|
||||||
title="Found AV process",
|
|
||||||
message="The process '{}' was recognized as an Anti Virus process. Process "
|
|
||||||
"details: {}".format(process[1]['name'], json.dumps(process[1])),
|
|
||||||
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL
|
|
||||||
))
|
|
||||||
|
|
||||||
if len(av_processes) > 0:
|
for process in av_processes:
|
||||||
test_status = zero_trust_consts.STATUS_PASSED
|
events.append(Event.create_event(
|
||||||
else:
|
title="Found AV process",
|
||||||
test_status = zero_trust_consts.STATUS_FAILED
|
message="The process '{}' was recognized as an Anti Virus process. Process "
|
||||||
AggregateFinding.create_or_add_to_existing(
|
"details: {}".format(process[1]['name'], json.dumps(process[1])),
|
||||||
test=zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events
|
event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL
|
||||||
)
|
))
|
||||||
|
|
||||||
|
if len(av_processes) > 0:
|
||||||
|
test_status = zero_trust_consts.STATUS_PASSED
|
||||||
|
else:
|
||||||
|
test_status = zero_trust_consts.STATUS_FAILED
|
||||||
|
AggregateFinding.create_or_add_to_existing(
|
||||||
|
test=zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def filter_av_processes(telemetry_json):
|
def filter_av_processes(process_list):
|
||||||
all_processes = list(telemetry_json['data']['process_list'].items())
|
all_processes = list(process_list.items())
|
||||||
av_processes = []
|
av_processes = []
|
||||||
for process in all_processes:
|
for process in all_processes:
|
||||||
process_name = process[1]['name']
|
process_name = process[1]['name']
|
||||||
|
|
Loading…
Reference in New Issue