def __init__(self, dumpIO, factory): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data """ self.m_dumpIO = dumpIO self.m_ll_data = CategoryLoader("sysvipc", self.m_dumpIO) self.m_daw_factory = factory
def __init__(self, dumpIO, uid_gid=None): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data :dict uid_gid: contains inode: data entries of found user namespaces """ self.m_dumpIO = dumpIO self.m_ll_data = CategoryLoader("userdata", self.m_dumpIO) self.m_data = self.m_ll_data.getData() if uid_gid: # load additional uid/gid-mappings from inside user namespaces for inode, ns_data in uid_gid.items(): if not ns_data['uid'] and not ns_data['gid']: # no uid-/gid-mappings inside this user-namespace continue # mapping-lists contain the following structure: # ID-inside-ns ID-outside-ns length # however, there can be multiple entries, we only care # about the first one and the offset of the parent ids. uid_offset = int(ns_data['uid'][0][1]) gid_offset = int(ns_data['gid'][0][1]) for offset, key in [(uid_offset, 'uids'), (gid_offset, 'gids')]: for mapping in ns_data[key].items(): value = int(mapping[0]) + offset if not value in self.m_data[key]: # adding the ids to the ones we already know self.m_data[key][value] = "{}(user-ns)".format( mapping[1]) else: error = """Overlapping {}: {} already exists! This could be caused by misconfigured uid/gid mapping on scanning target!""".format( key, value) raise ValueError(error)
class NetworkingWrapper(object): """ This class abstracts all info about network connections including unix sockets. """ PROTOCOLS = ["tcp", "tcp6", "udp", "udp6", "unix", "netlink", "packet"] def __init__(self, dumpIO): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data """ self.m_dumpIO = dumpIO self.m_ll_protos = CategoryLoader("networking", self.m_dumpIO) def getProtocols(self): """Returns a list of all currently possible protocols.""" return self.PROTOCOLS def getProtocolData(self, protocol): """Returns the data for a specific protocol.""" return self.m_ll_protos.getData()[protocol] def getDataForAllProtocols(self): """Returns the data for all protocols""" return self.m_ll_protos.getData()
def __init__(self, dumpIO): """ :param dumpIO: An instance of squinnie.dio.DumpIO """ self.m_dumpIO = dumpIO self.m_data = CategoryLoader("namespaces", self.m_dumpIO) self.m_data_deep = CategoryLoader("namespaces_deep", self.m_dumpIO)
class SystemData(object): """This class abstracts the SysVIPC communications.""" def __init__(self, dumpIO): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data """ self.m_dumpIO = dumpIO self.m_ll_data = CategoryLoader("systemdata", self.m_dumpIO) def getSysconfData(self): """ Returns all sysconfig values/ """ return self.m_ll_data.getData()['sysconf'] def getSystemUptime(self): """ Returns the uptime of the system at the time of scanning in seconds. """ return float(self.m_ll_data.getData()['uptime']) def getMountinfo(self): """ Returns information about all mounted filesystems. """ return self.m_ll_data.getData()['mounts'] def getProcessUptime(self, ticks): """ Returns the time a process has been running for in seconds. :param ticks: The ticks as given in /proc/<pid>/stat :return: The runtime in seconds. """ # the tick count in stat is the time in ticks the process has been started # so, at first, we need to convert the ticks to seconds: seconds_at_start = float(ticks) / float( self.getSysconfData()['SC_CLK_TCK']) # so now, we need to substract the starttime from the uptime to get the actual runtime return self.getSystemUptime() - seconds_at_start def getShmData(self): """ Returns the shm data as dict array with inode and names. :return: """ return self.m_ll_data.getData()['shm'] def getNameForShmInode(self, inode): """ Returns the name of a shm or semaphore in /dev/shm by inode. :param inode: The inode to search for. :return: The name as a string. """ for item in self.getShmData(): if item['inode'] == int(inode): return item['name'] return None
def __init__(self, dumpIO, factory): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data """ self.m_dumpIO = dumpIO self.m_data = {} self.m_ll_proc = CategoryLoader("proc_data", self.m_dumpIO) # self.m_ll_children = CategoryLoader("children", self.m_dumpIO) self.m_ll_parents = CategoryLoader("parents", self.m_dumpIO) self.m_daw_factory = factory self.m_socket_connection_cache = SocketCache() self.m_shm_cache = ShmCache()
class NetworkInterfaceWrapper(object): """ This class abstracts all information about network interfaces """ def __init__(self, dumpIO): """ :param dumpIO: An instance of squinnie.dio.DumpIO """ self.m_dumpIO = dumpIO self.m_data = CategoryLoader("nwifaces", self.m_dumpIO) def getAllNwIfaceData(self): return self.m_data.getData()
class NamespaceWrapper(object): """ This class abstracts all information about available namespaces. """ def __init__(self, dumpIO): """ :param dumpIO: An instance of squinnie.dio.DumpIO """ self.m_dumpIO = dumpIO self.m_data = CategoryLoader("namespaces", self.m_dumpIO) self.m_data_deep = CategoryLoader("namespaces_deep", self.m_dumpIO) def getNamespaceUidGid(self): result = None data = self.m_data_deep.getData() if 'user' in data: result = data['user'] return result def getAllNamespaceData(self): return self.m_data.getData() def getAllDeepNsData(self): return self.m_data_deep.getData()
class SysVIPC(object): """This class abstracts the SysVIPC communications.""" # this is the name of the key which contains the id name for each protocol IDKEYS = {'shm': 'shmid', 'msg': 'msqid', 'sem': 'semid'} def __init__(self, dumpIO, factory): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data """ self.m_dumpIO = dumpIO self.m_ll_data = CategoryLoader("sysvipc", self.m_dumpIO) self.m_daw_factory = factory def getSysVIpcData(self): """Return the raw sysv ipc data.""" return self.m_ll_data.getData() def getFormattedData(self): """ Returns the data formatted for printing as a table with the columns: type\perms\user\group\key\id\ctime\cpid\otime\opid """ data = [] authmgr = self.m_daw_factory.getAccountWrapper() for category, ccons in self.getSysVIpcData().items(): for cdata in ccons: data.append([ category, cdata['perms'][-3:], authmgr.getNameForUid(cdata['uid'], ''), authmgr.getNameForGid(cdata['gid'], ''), cdata['key'], cdata[self.IDKEYS[category]], self._formatTimestamp(cdata['ctime']), cdata['cpid'] if 'cpid' in cdata else '', self._formatTimestamp(cdata['atime'] if 'atime' in cdata else cdata.get('otime', '')), cdata['lpid'] if 'lpid' in cdata else '' ]) return data @staticmethod def _formatTimestamp(ts): """Makes a timestamp human-readable.""" return datetime.datetime.fromtimestamp( int(ts)).strftime('%Y-%m-%d %H:%M:%S') if ts else ''
def __init__(self, dumpIO): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data """ self.m_dumpIO = dumpIO self.m_ll_data = CategoryLoader("systemdata", self.m_dumpIO)
class AccountWrapper(object): """ This class abstracts all data about users and groups. So far this data is unfortunately only the mapping of the uid/gid to a name, which does not allow any more metadata (for example home directories). """ def __init__(self, dumpIO, uid_gid=None): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data :dict uid_gid: contains inode: data entries of found user namespaces """ self.m_dumpIO = dumpIO self.m_ll_data = CategoryLoader("userdata", self.m_dumpIO) self.m_data = self.m_ll_data.getData() if uid_gid: # load additional uid/gid-mappings from inside user namespaces for inode, ns_data in uid_gid.items(): if not ns_data['uid'] and not ns_data['gid']: # no uid-/gid-mappings inside this user-namespace continue # mapping-lists contain the following structure: # ID-inside-ns ID-outside-ns length # however, there can be multiple entries, we only care # about the first one and the offset of the parent ids. uid_offset = int(ns_data['uid'][0][1]) gid_offset = int(ns_data['gid'][0][1]) for offset, key in [(uid_offset, 'uids'), (gid_offset, 'gids')]: for mapping in ns_data[key].items(): value = int(mapping[0]) + offset if not value in self.m_data[key]: # adding the ids to the ones we already know self.m_data[key][value] = "{}(user-ns)".format( mapping[1]) else: error = """Overlapping {}: {} already exists! This could be caused by misconfigured uid/gid mapping on scanning target!""".format( key, value) raise ValueError(error) def getNameForUid(self, uid, default=None): """Returns the name of the user for a specific uid.""" data = self.m_data['uids'] return data[int(uid)] if int(uid) in data else default def getNameForGid(self, gid, default=None): """Returns the name of the group for a specific group id.""" data = self.m_data['gids'] return data[int(gid)] if int(gid) in data else default def getGidForName(self, name): """Returns the GID for an account or None if it can't be resolved.""" # this lookup is quite inefficient, but so far its only used once per execution and one shouldn't over-optimize. data = self.m_data['gids'] for gid, gname in data.items(): if gname == name: return gid return None def getUidForName(self, name): """Returns the GID for an account or None if it can't be resolved.""" # this lookup is quite inefficient, but so far its only used once per execution and one shouldn't over-optimize. data = self.m_data['uids'] for uid, uname in data.items(): if uname == name: return uid return None
def __init__(self, dumpIO): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data """ self.m_dumpIO = dumpIO self.m_ll_protos = CategoryLoader("networking", self.m_dumpIO)
class ProcessData(object): def __init__(self, dumpIO, factory): """ :param dumpIO: An instance of squinnie.dio.DumpIO for loading the data """ self.m_dumpIO = dumpIO self.m_data = {} self.m_ll_proc = CategoryLoader("proc_data", self.m_dumpIO) # self.m_ll_children = CategoryLoader("children", self.m_dumpIO) self.m_ll_parents = CategoryLoader("parents", self.m_dumpIO) self.m_daw_factory = factory self.m_socket_connection_cache = SocketCache() self.m_shm_cache = ShmCache() def getProcData(self): """Return general process data.""" return self.m_ll_proc.getData() def getParents(self): """Return the parents of each process""" return self.m_ll_parents.getData() # def getChildren(self): # """Return the children of each process""" # return self.m_ll_children.getData() def getProcessCount(self): """Returns the number of recorded processes for the scanned host""" return len(self.getProcData()) def getAllPids(self): """Returns all process ids found on the scanned host""" return self.getProcData().keys() def getProcessInfo(self, pid): """Get the data for a specific process""" return self.getProcData()[pid] def processHasUid(self, pid, uid): """This method checks whether a process contains the given UID.""" return uid in self.getProcData()[pid]["Uid"] def processHasGid(self, pid, gid): """This method checks whether a process contains the given GID.""" return gid in self.getProcData()[pid]["Gid"] def getChildrenForPid(self, searched_pid): """Returns all children for a given pid.""" return [pid for pid, parent in self.getParents().items() if searched_pid == parent] def getFileDescriptorsForPid(self, pid): """Returns an instance of FileHandlerWrapper for a given process.""" data = self.getProcessInfo(pid) return FdWrapper(pid, data['open_files'], data['Uid'], data['Gid'], self.m_daw_factory) def getEndpointsForPipe(self, id): """Returns the endpoints for a pipe.""" self.m_socket_connection_cache.buildIfNecessary(self.getProcData) return self.m_socket_connection_cache.getEndpointsForPipe(id) def getEndpointsForSocket(self, id): """Returns the endpoints for a socket.""" self.m_socket_connection_cache.buildIfNecessary(self.getProcData) return self.m_socket_connection_cache.getEndpointsForSocket(id) def getEndpointsForQueue(self, id): """Returns the endpoints for a queue.""" self.m_socket_connection_cache.buildIfNecessary(self.getProcData) return self.m_socket_connection_cache.getEndpointsForQueue(id) def getOtherPointOfPipe(self, pipe_id, pid): """Returns the first endpoint of a pipe which does not have the given pid.""" self.m_socket_connection_cache.buildIfNecessary(self.getProcData) return self.m_socket_connection_cache.getOtherPointOfPipe(pipe_id, pid) def getThreadsForPid(self, pid): return self.getProcData()[pid]['threads'] def getRuntimeOfProcess(self, pid): return self.m_daw_factory.getSystemDataWrapper().getProcessUptime(self.getProcessInfo(pid)['starttime']) def getShmsForPid(self, pid): """ Returns all shm entries a pid has. :param pid: The pid to search for. :return: """ self.m_shm_cache.buildIfNecessary(self.getProcData, lambda: self.m_daw_factory.getSystemDataWrapper().getShmData()) return self.m_shm_cache.getShmsForPid(pid)