Added new user communication PBA and ZT test, not working yet WIP!

This commit is contained in:
Shay Nehmad 2019-09-03 21:17:13 +03:00
parent 30b74675a5
commit 52a95935c8
9 changed files with 214 additions and 9 deletions

View File

@ -1,2 +1,3 @@
POST_BREACH_COMMUNICATE_AS_NEW_USER = "Communicate as new user"
POST_BREACH_BACKDOOR_USER = "Backdoor user"
POST_BREACH_FILE_EXECUTION = "File execution"

View File

@ -30,6 +30,7 @@ TEST_SCHEDULED_EXECUTION = u"scheduled_execution"
TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline"
TEST_SEGMENTATION = u"segmentation"
TEST_TUNNELING = u"tunneling"
TEST_COMMUNICATE_AS_NEW_USER = u"communicate_as_new_user"
TESTS = (
TEST_SEGMENTATION,
TEST_MALICIOUS_ACTIVITY_TIMELINE,
@ -38,7 +39,8 @@ TESTS = (
TEST_MACHINE_EXPLOITED,
TEST_DATA_ENDPOINT_HTTP,
TEST_DATA_ENDPOINT_ELASTIC,
TEST_TUNNELING
TEST_TUNNELING,
TEST_COMMUNICATE_AS_NEW_USER
)
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_SEGMENTATION = u"segmentation"
RECOMMENDATION_RESTRICTIVE_NETWORK_POLICIES = u"network_policies"
RECOMMENDATION_USERS_MAC_POLICIES = u"users_mac_policies"
RECOMMENDATIONS = {
RECOMMENDATION_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.",
RECOMMENDATION_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.",
RECOMMENDATION_USER_BEHAVIOUR: u"Adopt security user behavior analytics.",
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_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"
@ -140,6 +144,16 @@ TESTS_MAP = {
PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS],
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"

View File

@ -17,6 +17,40 @@ WINDOWS_COMMANDS = ['net', 'user', WormConfiguration.user_to_add,
class BackdoorUser(PBA):
def __init__(self):
super(BackdoorUser, self).__init__(POST_BREACH_BACKDOOR_USER,
linux_cmd=' '.join(LINUX_COMMANDS),
windows_cmd=WINDOWS_COMMANDS)
linux_cmds, windows_cmds = BackdoorUser.get_commands_to_add_user(
WormConfiguration.user_to_add, WormConfiguration.remote_user_pass)
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

View File

@ -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

View File

@ -19,7 +19,8 @@ class PBA(object):
def __init__(self, name="unknown", linux_cmd="", windows_cmd=""):
"""
: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.name = name

View File

@ -25,8 +25,9 @@ class PostBreach(object):
Executes all post breach actions.
"""
for pba in self.pba_list:
LOG.debug("Executing PBA: '{}'".format(pba.name))
pba.run()
LOG.info("Post breach actions executed")
LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list)))
@staticmethod
def config_to_pba_list():

View File

@ -119,6 +119,14 @@ SCHEMA = {
"title": "Back door user",
"attack_techniques": []
},
{
"type": "string",
"enum": [
"CommunicateAsNewUser"
],
"title": "Communicate as new user",
"attack_techniques": []
},
],
},
"finger_classes": {

View File

@ -1,7 +1,18 @@
from monkey_island.cc.database import mongo
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_COMMUNICATE_AS_NEW_USER: process_communicate_as_new_user_telemetry,
# `lambda *args, **kwargs: None` is a no-op.
POST_BREACH_BACKDOOR_USER: 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']},
{'$push': {'pba_results': telemetry_json['data']}})
if telemetry_json["name"] in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
POST_BREACH_TELEMETRY_PROCESSING_FUNCS[telemetry_json["name"]](telemetry_json)
post_breach_action_name = telemetry_json["data"]["name"]
if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS:
POST_BREACH_TELEMETRY_PROCESSING_FUNCS[post_breach_action_name](telemetry_json)

View File

@ -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
)