def save(self): """Saves the `Host` in the :class:`Workspace` 's database""" cursor = Db.get().cursor() if self.id is not None: #If we have an ID, the host is already saved in the database : UPDATE cursor.execute( '''UPDATE hosts SET name = ?, hostname = ?, uname = ?, issue = ?, machine_id = ?, macs = ? WHERE id = ?''', (self.name, self.hostname, self.uname, self.issue, self.machine_id, json.dumps(self.macs), self.id)) else: #The host doesn't exists in database : INSERT cursor.execute( '''INSERT INTO hosts(name, hostname, uname, issue, machine_id, macs) VALUES (?, ?, ?, ?, ?, ?) ''', (self.name, self.hostname, self.uname, self.issue, self.machine_id, json.dumps(self.macs))) cursor.close() cursor = Db.get().cursor() cursor.execute('SELECT id FROM hosts WHERE name=?', (self.name, )) self.id = cursor.fetchone()[0] cursor.close() Db.get().commit()
def save(self): """Save the Path in database If the Path object has an id it means it is already stored in database, so it is updated. Else it is inserted and the id is set in the object. """ cursor = Db.get().cursor() if self.id is not None: cursor.execute( '''UPDATE paths SET src = ?, dst = ? WHERE id = ?''', (self.src.id if self.src is not None else 0, self.dst.id, self.id)) else: cursor.execute( '''INSERT INTO paths(src, dst) VALUES (?, ?) ''', (self.src.id if self.src is not None else 0, self.dst.id)) cursor.close() cursor = Db.get().cursor() cursor.execute('SELECT id FROM paths WHERE src=? AND dst=?', \ (self.src.id if self.src is not None else 0, self.dst.id)) self.id = cursor.fetchone()[0] cursor.close() Db.get().commit()
def delete(self): """Removes the `Host` from the :class:`Workspace` Recursively removes all :class:`Path` s starting from this `Host` """ from baboossh import Path if self.id is None: return {} from baboossh.utils import unstore_targets_merge del_data = {} for path in Path.find_all(src=self): unstore_targets_merge(del_data, path.delete()) for endpoint in self.endpoints: endpoint.host = None endpoint.save() cursor = Db.get().cursor() cursor.execute('DELETE FROM hosts WHERE id = ?', (self.id, )) cursor.close() Db.get().commit() unstore_targets_merge( del_data, { "Host": [ type(self).get_id(self.hostname, self.uname, self.issue, self.machine_id, self.macs) ] }) return del_data
def delete(self): """Delete an Endpoint from the :class:`.Workspace`""" from baboossh import Path from baboossh import Connection if self.id is None: return {} from baboossh.utils import unstore_targets_merge del_data = {} if self.host is not None: endpoints = self.host.endpoints if len(endpoints) == 1: unstore_targets_merge(del_data, self.host.delete()) for connection in Connection.find_all(endpoint=self): unstore_targets_merge(del_data, connection.delete()) for path in Path.find_all(dst=self): unstore_targets_merge(del_data, path.delete()) cursor = Db.get().cursor() cursor.execute('DELETE FROM tags WHERE endpoint = ?', (self.id, )) cursor.execute('DELETE FROM endpoints WHERE id = ?', (self.id, )) cursor.close() Db.get().commit() unstore_targets_merge( del_data, {"Endpoint": [type(self).get_id(self.ip, self.port)]}) return del_data
def create(cls, name: str): """Create a new workspace Create a new workspace with its dedicated folder (in `$HOME/.baboossh` by default) and its database. """ if name == "": print("Cannot use workspace with empty name") raise ValueError if re.match(r'^[\w_\.-]+$', name) is None: print('Invalid characters in workspace name. \ Allowed characters are letters, numbers and ._-') raise ValueError workspace_folder = os.path.join(WORKSPACES_DIR, name) if not os.path.exists(workspace_folder): try: os.mkdir(workspace_folder) os.mkdir(os.path.join(workspace_folder, "loot")) os.mkdir(os.path.join(workspace_folder, "keys")) with open(os.path.join(workspace_folder, "workspace.version"), "w") as file: file.write(BABOOSSH_VERSION) except OSError: print("Creation of the directory " + workspace_folder + " failed") raise OSError print("Workspace "+name+" created") else: print("Workspace already exists") raise ValueError #create database Db.build(name) return Workspace(name)
def save(self): """Save the `Connection` to the :class:`Workspace`'s database""" if self.user is None or self.creds is None: return cursor = Db.get().cursor() if self.id is not None: #If we have an ID, the endpoint is already saved in the database : UPDATE cursor.execute( '''UPDATE connections SET endpoint= ?, user = ?, cred = ?, root = ? WHERE id = ?''', (self.endpoint.id, self.user.id, self.creds.id, self.root, self.id)) else: #The endpoint doesn't exists in database : INSERT cursor.execute( '''INSERT INTO connections(endpoint, user, cred, root) VALUES (?, ?, ?, ?) ''', (self.endpoint.id, self.user.id, self.creds.id, self.root)) cursor.close() cursor = Db.get().cursor() cursor.execute( 'SELECT id FROM connections WHERE endpoint=? AND user=? AND cred=?', (self.endpoint.id, self.user.id, self.creds.id)) self.id = cursor.fetchone()[0] cursor.close() Db.get().commit()
def save(self): """Save the `Creds` to the :class:`Workspace` 's database""" cursor = Db.get().cursor() if self.id is not None: #If we have an ID, the creds is already saved in the database : UPDATE cursor.execute('''UPDATE creds SET type = ?, content = ?, identifier = ?, scope = ?, found = ? WHERE id = ?''', (self.creds_type, self.creds_content, self.obj.identifier, self.scope, self.found.id if self.found is not None else None, self.id)) else: #The creds doesn't exists in database : INSERT cursor.execute('''INSERT INTO creds(type, content, identifier, scope, found) VALUES (?, ?, ?, ?, ?) ''', (self.creds_type, self.creds_content, self.obj.identifier, self.scope, self.found.id if self.found is not None else None)) cursor.close() cursor = Db.get().cursor() cursor.execute('SELECT id FROM creds WHERE type=? and identifier=?', (self.creds_type, self.obj.identifier)) self.id = cursor.fetchone()[0] cursor.close() Db.get().commit()
def untag(self, tagname): cursor = Db.get().cursor() cursor.execute('DELETE FROM tags WHERE name = ? and endpoint = ?', (tagname, self.id)) cursor.close() Db.get().commit() try: self.tags.remove(tagname) except KeyError: pass
def delete(self): """Delete an Path from the :class:`.Workspace`""" if self.id is None: return {} cursor = Db.get().cursor() cursor.execute('DELETE FROM paths WHERE id = ?', (self.id, )) cursor.close() Db.get().commit() return {"Path": [type(self).get_id(self.src, self.dst)]}
def tag(self, tagname): cursor = Db.get().cursor() try: cursor.execute( '''INSERT INTO tags (name, endpoint) VALUES (?, ?)''', (tagname, self.id)) except sqlite3.IntegrityError: pass cursor.close() Db.get().commit() self.tags.add(tagname)
def close(self): for tunnel in self.tunnels.values(): tunnel.close() for connection in Connection.find_all(): if connection.transport is not None: connection.close() for obj in self.store.values(): for instance in obj.values(): del instance Db.close() type(self).active = None print("Closing workspace "+self.name)
def delete(self): """Delete the `Connection` from the :class:`Workspace`'s database""" if self.id is None: return {} cursor = Db.get().cursor() cursor.execute('DELETE FROM connections WHERE id = ?', (self.id, )) cursor.close() Db.get().commit() return { "Connection": [type(self).get_id(self.endpoint, self.user, self.creds)] }
def __init__(self, endpoint, user, cred): """Create the object and fetches info from database if it has been saved. Args: endpoint (:class:`Endpoint`): The Connection's endpoint user (:class:`User`): The Connection's user cred (:class:`Creds`): The Connection's credentials """ self.endpoint = endpoint self.user = user self.creds = cred self.id = None self.root = False self.sock = None self.transport = None self.used_by_connections = [] self.used_by_tunnels = [] if user is None or cred is None: return cursor = Db.get().cursor() cursor.execute( 'SELECT id, root FROM connections WHERE endpoint=? AND user=? AND cred=?', (self.endpoint.id, self.user.id, self.creds.id)) saved_connection = cursor.fetchone() cursor.close() if saved_connection is not None: self.id = saved_connection[0] self.root = saved_connection[1] != 0
def find_one(cls, endpoint_id=None, ip_port=None): """Find an `Endpoint` by its id or it's IP address:Port Args: endpoint_id (int): the `Endpoint` id to search ip_port (str): The IP and port as "<ip>:<port>" Returns: A single `Endpoint` or `None`. """ cursor = Db.get().cursor() if endpoint_id is not None: if endpoint_id == 0: cursor.close() return None cursor.execute('''SELECT ip, port FROM endpoints WHERE id=?''', (endpoint_id, )) elif ip_port is not None: ip, sep, port = ip_port.partition(":") if port == "": raise ValueError cursor.execute( '''SELECT ip, port FROM endpoints WHERE ip=? and port=?''', (ip, port)) else: cursor.close() return None row = cursor.fetchone() cursor.close() if row is None: return None return Endpoint(row[0], row[1])
def find_all(cls, scope=None): """Returns a `List` of all `Host` s in the :class:`Workspace` matching the criteria Args: scope (bool): whether to return only `Host`s in scope (`True`), out of scope (`False`) or both (`None`) name (str): the `Host` s' name to match Returns: the `List` of `Host` s """ ret = [] cursor = Db.get().cursor() req = cursor.execute( 'SELECT hostname, uname, issue, machine_id, macs FROM hosts') for row in req: host = Host(row[0], row[1], row[2], row[3], json.loads(row[4])) if scope is None: ret.append(host) elif host.scope == scope: ret.append(host) cursor.close() return ret
def search(cls, field, val, show_all=False): """Search in the workspace for an `Endpoint` Args: field (str): the `Endpoint` attribute to search in val (str): the value to search for show_all (bool): whether to include out-of scope `Endpoint` s in search results Returns: A `List` of `Endpoint` s corresponding to the search. """ if field not in cls.search_fields: raise ValueError ret = [] cursor = Db.get().cursor() val = "%" + val + "%" if show_all: #Ok this sounds fugly, but there seems to be no way to set a column name in a parameter. The SQL injection risk is mitigated as field must be in allowed fields, but if you find something better I take it cursor.execute( 'SELECT ip, port FROM endpoints WHERE {} LIKE ?'.format(field), (val, )) else: cursor.execute( 'SELECT ip, port FROM endpoints WHERE scope=? and {} LIKE ?'. format(field), (True, val)) for row in cursor: ret.append(Endpoint(row[0], row[1])) return ret
def find_all(cls, scope=None, found=None): """Find all Endpoints matching the criteria Args: scope (bool): List Endpoints in scope (`True`), out of scope (`False`), or both (`None`) found (:class:`Endpoint`): The `Endpoint` the endpoints were discovered on Returns: A list of all `Endpoint` s in the :class:`.Workspace` """ ret = [] cursor = Db.get().cursor() if found is None: if scope is None: req = cursor.execute('SELECT ip, port FROM endpoints') else: req = cursor.execute( 'SELECT ip, port FROM endpoints WHERE scope=?', (scope, )) else: if scope is None: req = cursor.execute( 'SELECT ip, port FROM endpoints WHERE found=?', (found.id, )) else: req = cursor.execute( 'SELECT ip, port FROM endpoints WHERE scope=? and found=?', (scope, found.id)) for row in req: ret.append(Endpoint(row[0], row[1])) return ret
def delete(self): """Delete a `Creds` from the :class:`.Workspace`""" from baboossh import Connection if self.id is None: return {} from baboossh.utils import unstore_targets_merge del_data = {} for connection in Connection.find_all(creds=self): unstore_targets_merge(del_data, connection.delete()) self.obj.delete() cursor = Db.get().cursor() cursor.execute('DELETE FROM creds WHERE id = ?', (self.id, )) cursor.close() Db.get().commit() unstore_targets_merge(del_data, {"Creds":[type(self).get_id(self.creds_type, self.creds_content)]}) return del_data
def find_all(cls, scope=None, found=None): """Find all `Creds` Args: scope (bool): List `Creds` in scope (`True`), out of scope (`False`), or both (`None`) found (:class:`Endpoint`): the `Endpoint` the `Creds` were discovered on Returns: A list of all `Creds` in the :class:`.Workspace` """ ret = [] cursor = Db.get().cursor() if found is None: if scope is None: req = cursor.execute('SELECT type, content FROM creds') else: req = cursor.execute('SELECT type, content FROM creds WHERE scope=?', (scope, )) else: if scope is None: req = cursor.execute('SELECT type, content FROM creds WHERE found=?', (found.id, )) else: req = cursor.execute('SELECT type, content FROM creds WHERE found=? AND scope=?', (found.id, scope)) for row in req: ret.append(Creds(row[0], row[1])) return ret
def search(cls, field, val, show_all=False): """Search in the workspace for a `Host` Args: field (str): the `Host` attribute to search in val (str): the value to search for show_all (bool): whether to include out-of scope `Host` s in search results Returns: A `List` of `Host` s corresponding to the search. """ if field not in cls.search_fields: raise ValueError ret = [] cursor = Db.get().cursor() val = "%" + val + "%" #Ok this sounds fugly, but there seems to be no way to set a column name in a parameter. The SQL injection risk is mitigated as field must be in allowed fields, but if you find something better I take it for row in cursor.execute( 'SELECT hostname, uname, issue, machine_id, macs FROM hosts WHERE {} LIKE ?' .format(field), (val, )): ret.append(Host(row[0], row[1], row[2], row[3], json.loads(row[4]))) if not show_all: ret = [host for host in ret if host.scope] return ret
def __init__(self, name): from baboossh import Endpoint self.name = name self.endpoints = [] cursor = Db.get().cursor() for row in cursor.execute('SELECT endpoint FROM tags WHERE name=?', (self.name, )): self.endpoints.append(Endpoint.find_one(endpoint_id=row[0]))
def find_one(cls, connection_id=None, endpoint=None, scope=None, gateway_to=None): """Find a `Connection` by its id, endpoint or if it can be used as a gateway to an :class:`Endpoint` Args: connection_id (int): the `Connection` id to search endpoint (:class:`Endpoint`): the `Connection` endpoint to search gateway_to (:class:`Endpoint`): the Endpoint to which you want to find a gateway scope (bool): whether to include only in scope Connections (`True`), out of scope Connections (`False`) or both (`None`) Returns: A single `Connection` or `None`. """ if gateway_to is not None: if gateway_to.distance is not None and gateway_to.distance == 0: return None try: closest_host = Host.find_one(prev_hop_to=gateway_to) except NoPathError as exc: raise exc if closest_host is None: return None return cls.find_one(endpoint=closest_host.closest_endpoint, scope=True) cursor = Db.get().cursor() if connection_id is not None: req = cursor.execute( 'SELECT endpoint, user, cred FROM connections WHERE id=?', (connection_id, )) elif endpoint is not None: req = cursor.execute( 'SELECT endpoint, user, cred FROM connections WHERE endpoint=? ORDER BY root ASC', (endpoint.id, )) else: cursor.close() return None if scope is None: row = cursor.fetchone() cursor.close() if row is None: return None return Connection(Endpoint.find_one(endpoint_id=row[0]), User.find_one(user_id=row[1]), Creds.find_one(creds_id=row[2])) for row in req: conn = Connection(Endpoint.find_one(endpoint_id=row[0]), User.find_one(user_id=row[1]), Creds.find_one(creds_id=row[2])) if scope == conn.scope: cursor.close() return conn cursor.close() return None
def endpoints(self): """Returns a `List` of the `Host` 's :class:`Endpoint` s""" from baboossh import Endpoint endpoints = [] cursor = Db.get().cursor() for row in cursor.execute( 'SELECT ip, port FROM endpoints WHERE host=?', (self.id, )): endpoints.append(Endpoint(row[0], row[1])) cursor.close() return endpoints
def distance(self): """Returns the `Host` 's number of hops from `"Local"`""" cursor = Db.get().cursor() cursor.execute( 'SELECT distance FROM endpoints WHERE host=? ORDER BY distance DESC', (self.id, )) row = cursor.fetchone() cursor.close() return row[0]
def save(self): """Save the Endpoint in database If the Endpoint object has an id it means it is already stored in database, so it is updated. Else it is inserted and the id is set in the object. """ cursor = Db.get().cursor() if self.id is not None: #If we have an ID, the endpoint is already saved in the database : UPDATE cursor.execute( '''UPDATE endpoints SET ip = ?, port = ?, host = ?, reachable = ?, distance = ?, scope = ?, found = ? WHERE id = ?''', (self.ip, self.port, self.host.id if self.host is not None else None, self.reachable, self.distance, self.scope, self.found.id if self.found is not None else None, self.id)) else: #The endpoint doesn't exists in database : INSERT cursor.execute( '''INSERT INTO endpoints(ip, port, host, reachable, distance, scope, found) VALUES (?, ?, ?, ?, ?, ?, ?) ''', (self.ip, self.port, self.host.id if self.host is not None else None, self.reachable, self.distance, self.scope, self.found.id if self.found is not None else None)) cursor.close() cursor = Db.get().cursor() cursor.execute('SELECT id FROM endpoints WHERE ip=? AND port=?', (self.ip, self.port)) self.id = cursor.fetchone()[0] cursor.close() Db.get().commit()
def closest_endpoint(self): """Returns the `Host` 's closest :class:`Endpoint`""" cursor = Db.get().cursor() cursor.execute( 'SELECT ip, port FROM endpoints WHERE host=? ORDER BY distance DESC', (self.id, )) row = cursor.fetchone() cursor.close() from baboossh import Endpoint return Endpoint(row[0], row[1])
def __init__(self, src, dst): if str(src) == str(dst): raise ValueError("Can't create path to self") self.src = src self.dst = dst self.id = None cursor = Db.get().cursor() cursor.execute('SELECT id FROM paths WHERE src=? AND dst=?', \ (self.src.id if self.src is not None else 0, self.dst.id)) saved_path = cursor.fetchone() cursor.close() if saved_path is not None: self.id = saved_path[0]
def __init__(self, hostname, uname, issue, machine_id, macs): self.hostname = hostname self.id = None self.uname = uname self.issue = issue self.machine_id = machine_id self.macs = macs cursor = Db.get().cursor() cursor.execute( 'SELECT id, name FROM hosts WHERE hostname=? AND uname=? AND issue=? AND machine_id=? AND macs=?', (self.hostname, self.uname, self.issue, self.machine_id, json.dumps(self.macs))) saved_host = cursor.fetchone() cursor.close() if saved_host is not None: self.id = saved_host[0] self.name = saved_host[1] else: if hostname != "": name = hostname.split(".")[0] if len(name) > 20: name = name[:20] incr = 0 else: name = "host" incr = 1 self.name = None while self.name is None: fullname = name if incr == 0 else name + "_" + str(incr) cursor = Db.get().cursor() cursor.execute('SELECT id FROM hosts WHERE name=?', (fullname, )) if cursor.fetchone() is not None: incr = incr + 1 else: self.name = fullname cursor.close()
def find_one(cls, host_id=None, name=None, prev_hop_to=None): """Find a `Host` by its id Args: host_id (int): the desired `Host` 's id name (str): the `Host` 's name to match Returns: A `Host` or `None` """ if prev_hop_to is not None: from baboossh import Path paths = Path.find_all(dst=prev_hop_to) smallest_distance = None closest = None for path in paths: if path.src is None: #Direct path found, we can stop here return None if closest is None: closest = path.src smallest_distance = path.src.distance continue if path.src.distance < smallest_distance: closest = path.src smallest_distance = path.src.distance continue if closest is None: raise NoPathError return closest cursor = Db.get().cursor() if host_id is not None: cursor.execute( '''SELECT hostname, uname, issue, machine_id, macs FROM hosts WHERE id=?''', (host_id, )) elif name is not None: cursor.execute( '''SELECT hostname, uname, issue, machine_id, macs FROM hosts WHERE name=?''', (name, )) else: cursor.close() return None row = cursor.fetchone() cursor.close() if row is None: return None return Host(row[0], row[1], row[2], row[3], json.loads(row[4]))
def __init__(self, name): if name == "": raise ValueError("Cannot use workspace with empty name") if re.match(r'^[\w_\.-]+$', name) is None: print('Invalid characters in workspace name. \ Allowed characters are letters, numbers and ._-') raise ValueError self.workspace_folder = os.path.join(WORKSPACES_DIR, name) if not os.path.exists(self.workspace_folder): raise ValueError("Workspace "+name+" does not exist") try: with open(os.path.join(self.workspace_folder, "workspace.version"), "r") as f: self.version = f.read() except FileNotFoundError: self.version = "1.0.x" if not is_workspace_compat(self.version): raise WorkspaceVersionError(BABOOSSH_VERSION, self.version) Db.connect(name) self.name = name self.tunnels = {} self.options = { "endpoint":None, "user":None, "creds":None, "payload":None, "params":None, } type(self).active = self self.store = { "Connection": {}, "Creds": {}, "Endpoint": {}, "Host": {}, "Path": {}, "User": {}, }