forked from p34709852/monkey
Added new user communication PBA and ZT test, not working yet WIP!
This commit is contained in:
parent
30b74675a5
commit
52a95935c8
|
@ -1,2 +1,3 @@
|
||||||
|
POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user"
|
||||||
POST_BREACH_BACKDOOR_USER = "Backdoor user"
|
POST_BREACH_BACKDOOR_USER = "Backdoor user"
|
||||||
POST_BREACH_FILE_EXECUTION = "File execution"
|
POST_BREACH_FILE_EXECUTION = "File execution"
|
||||||
|
|
|
@ -30,6 +30,7 @@ TEST_SCHEDULED_EXECUTION = u"scheduled_execution"
|
||||||
TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline"
|
TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline"
|
||||||
TEST_SEGMENTATION = u"segmentation"
|
TEST_SEGMENTATION = u"segmentation"
|
||||||
TEST_TUNNELING = u"tunneling"
|
TEST_TUNNELING = u"tunneling"
|
||||||
|
TEST_COMMUNICATE_AS_NEW_USER = u"communicate_as_new_user"
|
||||||
TESTS = (
|
TESTS = (
|
||||||
TEST_SEGMENTATION,
|
TEST_SEGMENTATION,
|
||||||
TEST_MALICIOUS_ACTIVITY_TIMELINE,
|
TEST_MALICIOUS_ACTIVITY_TIMELINE,
|
||||||
|
@ -38,7 +39,8 @@ TESTS = (
|
||||||
TEST_MACHINE_EXPLOITED,
|
TEST_MACHINE_EXPLOITED,
|
||||||
TEST_DATA_ENDPOINT_HTTP,
|
TEST_DATA_ENDPOINT_HTTP,
|
||||||
TEST_DATA_ENDPOINT_ELASTIC,
|
TEST_DATA_ENDPOINT_ELASTIC,
|
||||||
TEST_TUNNELING
|
TEST_TUNNELING,
|
||||||
|
TEST_COMMUNICATE_AS_NEW_USER
|
||||||
)
|
)
|
||||||
|
|
||||||
RECOMMENDATION_DATA_TRANSIT = u"data_transit"
|
RECOMMENDATION_DATA_TRANSIT = u"data_transit"
|
||||||
|
@ -47,13 +49,15 @@ RECOMMENDATION_USER_BEHAVIOUR = u"user_behaviour"
|
||||||
RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic"
|
RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic"
|
||||||
RECOMMENDATION_SEGMENTATION = u"segmentation"
|
RECOMMENDATION_SEGMENTATION = u"segmentation"
|
||||||
RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
|
RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
|
||||||
|
RECOMMENDATION_USERS_MAC_POLICIES = u"users_mac_policies"
|
||||||
RECOMMENDATIONS = {
|
RECOMMENDATIONS = {
|
||||||
RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
|
RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
|
||||||
RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
|
RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
|
||||||
RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
|
RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
|
||||||
RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
|
RECOMMENDATION_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.",
|
||||||
RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
|
RECOMMENDATION_DATA_TRANSIT: u"Secure data at transit by encrypting it.",
|
||||||
RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible."
|
RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.",
|
||||||
|
RECOMMENDATION_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC only.",
|
||||||
}
|
}
|
||||||
|
|
||||||
POSSIBLE_STATUSES_KEY = u"possible_statuses"
|
POSSIBLE_STATUSES_KEY = u"possible_statuses"
|
||||||
|
@ -140,6 +144,16 @@ TESTS_MAP = {
|
||||||
PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
|
PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
|
||||||
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED]
|
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED]
|
||||||
},
|
},
|
||||||
|
TEST_COMMUNICATE_AS_NEW_USER: {
|
||||||
|
TEST_EXPLANATION_KEY: u"The Monkey tried create a new user and communicate with the internet from it.",
|
||||||
|
FINDING_EXPLANATION_BY_STATUS_KEY: {
|
||||||
|
STATUS_FAILED: "Monkey was able to cause a new user to access the network. Your network policies are too permissive - restrict them to MAC only.",
|
||||||
|
STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network."
|
||||||
|
},
|
||||||
|
RECOMMENDATION_KEY: RECOMMENDATION_USERS_MAC_POLICIES,
|
||||||
|
PILLARS_KEY: [PEOPLE, NETWORKS, VISIBILITY_ANALYTICS],
|
||||||
|
POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
EVENT_TYPE_ISLAND = "island"
|
EVENT_TYPE_ISLAND = "island"
|
||||||
|
|
|
@ -17,6 +17,40 @@ WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add,
|
||||||
|
|
||||||
class BackdoorUser(PBA):
|
class BackdoorUser(PBA):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(BackdoorUser, self).__init__(POST_BREACH_BACKDOOR_USER,
|
linux_cmds, windows_cmds = BackdoorUser.get_commands_to_add_user(
|
||||||
linux_cmd=' '.join(LINUX_COMMANDS),
|
WormConfiguration.user_to_add, WormConfiguration.remote_user_pass)
|
||||||
windows_cmd=WINDOWS_COMMANDS)
|
super(BackdoorUser, self).__init__(
|
||||||
|
POST_BREACH_BACKDOOR_USER,
|
||||||
|
linux_cmd=' '.join(linux_cmds),
|
||||||
|
windows_cmd=windows_cmds)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_commands_to_add_user(username, password):
|
||||||
|
linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
|
||||||
|
windows_cmds = BackdoorUser.get_windows_commands_to_add_user(password, username)
|
||||||
|
return linux_cmds, windows_cmds
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_linux_commands_to_add_user(username):
|
||||||
|
linux_cmds = [
|
||||||
|
'useradd',
|
||||||
|
'-M',
|
||||||
|
'--expiredate',
|
||||||
|
datetime.datetime.today().strftime('%Y-%m-%d'),
|
||||||
|
'--inactive',
|
||||||
|
'0',
|
||||||
|
'-c',
|
||||||
|
'MONKEY_USER',
|
||||||
|
username]
|
||||||
|
return linux_cmds
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_windows_commands_to_add_user(password, username):
|
||||||
|
windows_cmds = [
|
||||||
|
'net',
|
||||||
|
'user',
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
'/add',
|
||||||
|
'/ACTIVE:NO']
|
||||||
|
return windows_cmds
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import win32api
|
||||||
|
import win32con
|
||||||
|
import win32process
|
||||||
|
import win32security
|
||||||
|
|
||||||
|
from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER
|
||||||
|
from infection_monkey.post_breach.actions.add_user import BackdoorUser
|
||||||
|
from infection_monkey.post_breach.pba import PBA
|
||||||
|
from infection_monkey.telemetry.post_breach_telem import PostBreachTelem
|
||||||
|
from infection_monkey.utils import is_windows_os
|
||||||
|
|
||||||
|
USERNAME = "somenewuser"
|
||||||
|
PASSWORD = "N3WPa55W0rD!@12"
|
||||||
|
|
||||||
|
|
||||||
|
class CommunicateAsNewUser(PBA):
|
||||||
|
"""
|
||||||
|
This PBA creates a new user, and then pings google as that user. This is used for a Zero Trust test of the People
|
||||||
|
pillar. See the relevant telemetry processing to see what findings are created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(CommunicateAsNewUser, self).__init__(name=POST_BREACH_COMMUNICATE_AS_NEW_USER)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
username = USERNAME + ''.join(random.choice(string.ascii_lowercase) for _ in range(5))
|
||||||
|
if is_windows_os():
|
||||||
|
if not self.try_to_create_user_windows(username, PASSWORD):
|
||||||
|
return # no point to continue if failed creating the user.
|
||||||
|
|
||||||
|
# Logon as new user: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera
|
||||||
|
new_user_logon_token_handle = win32security.LogonUser(
|
||||||
|
username,
|
||||||
|
".", # current domain
|
||||||
|
PASSWORD,
|
||||||
|
win32con.LOGON32_LOGON_BATCH, # logon type
|
||||||
|
win32con.LOGON32_PROVIDER_DEFAULT) # logon provider
|
||||||
|
|
||||||
|
if new_user_logon_token_handle == 0:
|
||||||
|
PostBreachTelem(
|
||||||
|
self,
|
||||||
|
("Can't logon as {} Last error: {}".format(username, win32api.GetLastError()), False)
|
||||||
|
).send()
|
||||||
|
return # no point to continue if can't log on.
|
||||||
|
|
||||||
|
# Using os.path is OK, as this is on windows for sure
|
||||||
|
ping_app_path = os.path.join(os.environ["WINDIR"], "system32", "PING.exe")
|
||||||
|
if not os.path.exists(ping_app_path):
|
||||||
|
PostBreachTelem(self, ("{} not found".format(ping_app_path), False)).send()
|
||||||
|
return # Can't continue without ping.
|
||||||
|
|
||||||
|
# Open process as that user:
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
|
||||||
|
return_value_create_process = win32process.CreateProcessAsUser(
|
||||||
|
new_user_logon_token_handle, # A handle to the primary token that represents a user.
|
||||||
|
# If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module
|
||||||
|
# to execute, and *lpCommandLine specifies the command line.
|
||||||
|
ping_app_path, # The name of the module to be executed.
|
||||||
|
"google.com", # The command line to be executed.
|
||||||
|
None, # Process attributes
|
||||||
|
None, # Thread attributes
|
||||||
|
True, # Should inherit handles
|
||||||
|
win32con.NORMAL_PRIORITY_CLASS, # The priority class and the creation of the process.
|
||||||
|
None, # An environment block for the new process. If this parameter is NULL, the new process
|
||||||
|
# uses the environment of the calling process.
|
||||||
|
None, # CWD. If this parameter is NULL, the new process will have the same current drive and
|
||||||
|
# directory as the calling process.
|
||||||
|
win32process.STARTUPINFO() # STARTUPINFO structure.
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
|
||||||
|
)
|
||||||
|
|
||||||
|
if return_value_create_process == 0:
|
||||||
|
PostBreachTelem(self, (
|
||||||
|
"Failed to open process as user. Last error: {}".format(win32api.GetLastError()), False)).send()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
linux_cmds = BackdoorUser.get_linux_commands_to_add_user(username)
|
||||||
|
linux_cmds.extend([";", "sudo", "-", username, "-c", "'ping -c 2 google.com'"])
|
||||||
|
subprocess.check_output(linux_cmds, stderr=subprocess.STDOUT, shell=True)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
PostBreachTelem(self, (e.output, False)).send()
|
||||||
|
return
|
||||||
|
|
||||||
|
def try_to_create_user_windows(self, username, password):
|
||||||
|
try:
|
||||||
|
windows_cmds = BackdoorUser.get_windows_commands_to_add_user(username, password)
|
||||||
|
subprocess.check_output(windows_cmds, stderr=subprocess.STDOUT, shell=True)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
PostBreachTelem(self, (
|
||||||
|
"Couldn't create the user '{}'. Error output is: '{}'".format(username, e.output), False)).send()
|
||||||
|
return False
|
|
@ -19,7 +19,8 @@ class PBA(object):
|
||||||
def __init__(self, name="unknown", linux_cmd="", windows_cmd=""):
|
def __init__(self, name="unknown", linux_cmd="", windows_cmd=""):
|
||||||
"""
|
"""
|
||||||
:param name: Name of post breach action.
|
:param name: Name of post breach action.
|
||||||
:param command: Command that will be executed on breached machine
|
:param linux_cmd: Command that will be executed on breached machine
|
||||||
|
:param windows_cmd: Command that will be executed on breached machine
|
||||||
"""
|
"""
|
||||||
self.command = PBA.choose_command(linux_cmd, windows_cmd)
|
self.command = PBA.choose_command(linux_cmd, windows_cmd)
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
|
@ -25,8 +25,9 @@ class PostBreach(object):
|
||||||
Executes all post breach actions.
|
Executes all post breach actions.
|
||||||
"""
|
"""
|
||||||
for pba in self.pba_list:
|
for pba in self.pba_list:
|
||||||
|
LOG.debug("Executing PBA: '{}'".format(pba.name))
|
||||||
pba.run()
|
pba.run()
|
||||||
LOG.info("Post breach actions executed")
|
LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list)))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def config_to_pba_list():
|
def config_to_pba_list():
|
||||||
|
|
|
@ -119,6 +119,14 @@ SCHEMA = {
|
||||||
"title": "Back door user",
|
"title": "Back door user",
|
||||||
"attack_techniques": []
|
"attack_techniques": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"CommunicateAsNewUser"
|
||||||
|
],
|
||||||
|
"title": "Communicate as new user",
|
||||||
|
"attack_techniques": []
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"finger_classes": {
|
"finger_classes": {
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
from monkey_island.cc.database import mongo
|
from monkey_island.cc.database import mongo
|
||||||
from common.data.post_breach_consts import *
|
from common.data.post_breach_consts import *
|
||||||
|
from monkey_island.cc.models import Monkey
|
||||||
|
from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import test_new_user_communication
|
||||||
|
|
||||||
|
|
||||||
|
def process_communicate_as_new_user_telemetry(telemetry_json):
|
||||||
|
current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid'])
|
||||||
|
success = telemetry_json['data']['result'][1]
|
||||||
|
message = telemetry_json['data']['result'][0]
|
||||||
|
test_new_user_communication(current_monkey, success, message)
|
||||||
|
|
||||||
|
|
||||||
POST_BREACH_TELEMETRY_PROCESSING_FUNCS = {
|
POST_BREACH_TELEMETRY_PROCESSING_FUNCS = {
|
||||||
|
POST_BREACH_COMMUNICATE_AS_NEW_USER: process_communicate_as_new_user_telemetry,
|
||||||
# `lambda *args, **kwargs: None` is a no-op.
|
# `lambda *args, **kwargs: None` is a no-op.
|
||||||
POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None,
|
POST_BREACH_BACKDOOR_USER: lambda *args, **kwargs: None,
|
||||||
POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None,
|
POST_BREACH_FILE_EXECUTION: lambda *args, **kwargs: None,
|
||||||
|
@ -13,5 +24,7 @@ def process_post_breach_telemetry(telemetry_json):
|
||||||
{'guid': telemetry_json['monkey_guid']},
|
{'guid': telemetry_json['monkey_guid']},
|
||||||
{'$push': {'pba_results': telemetry_json['data']}})
|
{'$push': {'pba_results': telemetry_json['data']}})
|
||||||
|
|
||||||
if telemetry_json["name"] in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
|
post_breach_action_name = telemetry_json["data"]["name"]
|
||||||
POST_BREACH_TELEMETRY_PROCESSING_FUNCS[telemetry_json["name"]](telemetry_json)
|
if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
|
||||||
|
POST_BREACH_TELEMETRY_PROCESSING_FUNCS[post_breach_action_name](telemetry_json)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK, STATUS_FAILED, TEST_COMMUNICATE_AS_NEW_USER, \
|
||||||
|
STATUS_PASSED
|
||||||
|
from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding
|
||||||
|
from monkey_island.cc.models.zero_trust.event import Event
|
||||||
|
|
||||||
|
|
||||||
|
def test_new_user_communication(current_monkey, success, message):
|
||||||
|
tried_to_communicate_event = Event.create_event(
|
||||||
|
title="Communicate as new user",
|
||||||
|
message="Monkey on {} tried to create a new user and communicate from it.".format(current_monkey.hostname),
|
||||||
|
event_type=EVENT_TYPE_MONKEY_NETWORK)
|
||||||
|
events = [tried_to_communicate_event]
|
||||||
|
|
||||||
|
if success:
|
||||||
|
events.append(
|
||||||
|
Event.create_event(
|
||||||
|
title="Communicate as new user",
|
||||||
|
message="New user created by Monkey on {} successfully tried to communicate with the internet. "
|
||||||
|
"Details: {}".format(current_monkey.hostname, message),
|
||||||
|
event_type=EVENT_TYPE_MONKEY_NETWORK)
|
||||||
|
)
|
||||||
|
test_status = STATUS_FAILED
|
||||||
|
else:
|
||||||
|
events.append(
|
||||||
|
Event.create_event(
|
||||||
|
title="Communicate as new user",
|
||||||
|
message="Monkey on {} couldn't communicate as new user. Details: {}".format(
|
||||||
|
current_monkey.hostname, message),
|
||||||
|
event_type=EVENT_TYPE_MONKEY_NETWORK)
|
||||||
|
)
|
||||||
|
test_status = STATUS_PASSED
|
||||||
|
|
||||||
|
AggregateFinding.create_or_add_to_existing(
|
||||||
|
test=TEST_COMMUNICATE_AS_NEW_USER, status=test_status, events=events
|
||||||
|
)
|
Loading…
Reference in New Issue