def from_target(cls, arg): if '@' in arg and ':' in arg: auth, sep, endpoint = arg.partition('@') endpoint = Endpoint.find_one(ip_port=endpoint) if endpoint is None: raise ValueError("Supplied endpoint isn't in workspace") user, sep, cred = auth.partition(":") if sep == "": raise ValueError("No credentials supplied") user = User.find_one(name=user) if user is None: raise ValueError("Supplied user isn't in workspace") if cred[0] == "#": cred = cred[1:] cred = Creds.find_one(creds_id=cred) if cred is None: raise ValueError("Supplied credentials aren't in workspace") return Connection(endpoint, user, cred) if ':' not in arg: arg = arg + ':22' endpoint = Endpoint.find_one(ip_port=arg) if endpoint is None: raise ValueError("Supplied endpoint isn't in workspace") connection = cls.find_one(endpoint=endpoint) if connection is None: raise ValueError("No working connection for supplied endpoint") return connection
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 path_find_existing(self, dst, as_ip=False): if dst in [host.name for host in Host.find_all()]: host = Host.find_one(name=dst) dst = host.closest_endpoint else: try: dst = Endpoint.find_one(ip_port=dst) except: print("Please specify a valid endpoint in the IP:PORT form") return if dst is None: print("The endpoint provided doesn't exist in this workspace") return if Path.direct(dst): print("The destination should be reachable from the host") return try: chain = Path.get(dst) except NoPathError: print("No path could be found to the destination") return if chain[0] is None: chain[0] = "local" if as_ip: print(" > ".join(str(link.closest_endpoint) \ if isinstance(link, Host) else str(link) for link in chain)) else: print(" > ".join(str(link) for link in chain))
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 endpoint_add(self, ipaddr, port): """Add an :class:`Endpoint` to the workspace Args: ipaddr (str): the `Endpoint` 's IP address ipaddr (str): the `Endpoint` 's port """ Endpoint(ipaddr, port).save()
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 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 enum_probe(self, target=None, again=False): if target is not None: if target == "*": endpoints = Endpoint.find_all(scope=True) elif target[0] == "!": tag = Tag(target[1:]) endpoints = tag.endpoints else: endpoint = Endpoint.find_one(ip_port=target) if endpoint is None: raise ValueError("Supplied endpoint isn't in workspace") return [endpoint] elif self.options["endpoint"] is not None: if isinstance(self.options["endpoint"], Tag): return self.options["endpoint"].endpoints return [self.options["endpoint"]] else: endpoints = Endpoint.find_all(scope=True) if not again: endpoints = [endpoint for endpoint in endpoints if not endpoint.reachable] return endpoints
def __init__(self, creds_type, creds_content): self.creds_type = creds_type self.creds_content = creds_content self.obj = Extensions.auths[creds_type](creds_content) self.id = None self.scope = True self.found = None cursor = Db.get().cursor() cursor.execute('SELECT id, scope, found FROM creds WHERE type=? AND identifier=?', (self.creds_type, self.obj.identifier)) saved_creds = cursor.fetchone() cursor.close() if saved_creds is not None: self.id = saved_creds[0] self.scope = saved_creds[1] != 0 if saved_creds[2] is not None: from baboossh import Endpoint self.found = Endpoint.find_one(endpoint_id=saved_creds[2])
def enum_run(self, target=None): if target is not None: if '@' not in target: host = Host.find_one(name=target) if host is not None: conn = Connection.find_one(endpoint=host.closest_endpoint) if conn is not None: return [conn] raise ValueError("Supplied value doesn't match a known host or a connection string") auth, sep, endpoint = target.partition('@') if endpoint == "*": endpoint = None elif endpoint[0] == "!": tag = Tag(endpoint[1:]) endpoints = tag.endpoints else: endpoint = Endpoint.find_one(ip_port=endpoint) if endpoint is None: raise ValueError("Supplied endpoint isn't in workspace") user, sep, cred = auth.partition(":") if sep == "": raise ValueError("No credentials supplied") if user == "*": user = None else: user = User.find_one(name=user) if user is None: raise ValueError("Supplied user isn't in workspace") if cred == "*": cred = None else: if cred[0] == "#": cred = cred[1:] cred = Creds.find_one(creds_id=cred) if cred is None: raise ValueError("Supplied credentials aren't in workspace") else: user = self.options["user"] endpoint = self.options["endpoint"] cred = self.options["creds"] return Connection.find_all(endpoint=endpoint, user=user, creds=cred)
def endpoint_untag(self, endpoint, tagname): """Remove a :class:`Tag` from an :class:`Endpoint` Args: endpoint (str): the `Endpoint` 's string (ip:port) tagname (str): the :class:`Tag` name """ if tagname[0] == "!": tagname = tagname[1:] try: endpoint = Endpoint.find_one(ip_port=endpoint) except ValueError: print("Could not find endpoint.") return False if endpoint is None: print("Could not find endpoint.") return False endpoint.untag(tagname) return True
def path_add(self, src, dst): if src.lower() != "local": if src not in [host.name for host in Host.find_all()]: print("Not a known Host name.") return src = Host.find_one(name=src) if src is None: print("The source Host provided doesn't exist in this workspace") return else: src = None try: dst = Endpoint.find_one(ip_port=dst) except: print("Please specify valid destination endpoint in the IP:PORT form") if dst is None: print("The destination endpoint provided doesn't exist in this workspace") return path = Path(src, dst) path.save() print("Path saved")
def find_one(cls, path_id=None): """Find an path by its id Args: pathId (int): the path id to search Returns: A single `Path` or `None`. """ if path_id is None: return None cursor = Db.get().cursor() cursor.execute('''SELECT src, dst FROM paths WHERE id=?''', (path_id, )) row = cursor.fetchone() cursor.close() if row is None: return None return Path(Host.find_one(host_id=row[0]), Endpoint.find_one(endpoint_id=row[1]))
def identify_object(self, target): if target[0] == "#": creds_id = target[1:] else: creds_id = target creds = Creds.find_one(creds_id=creds_id) if creds is not None: return creds user = User.find_one(name=target) if user is not None: return user try: dst = Endpoint.find_one(ip_port=target) if dst is not None: return dst except: pass host = Host.find_one(name=target) if host is not None: return host print("Could not identify object.") return None
def get_objects(self, local=False, hosts=False, connections=False, endpoints=False, \ users=False, creds=False, tunnels=False, paths=False, scope=None, tags=None): ret = [] if local: ret.append("local") if hosts: ret = ret + Host.find_all(scope=scope) if connections: ret = ret + Connection.find_all(scope=scope) if endpoints: ret = ret + Endpoint.find_all(scope=scope) if users: ret = ret + User.find_all(scope=scope) if creds: ret = ret + Creds.find_all(scope=scope) if tunnels: ret = ret + list(self.tunnels.values()) if paths: ret = ret + Path.find_all() if tags: ret = ret + Tag.find_all() return ret
def find_all(cls, src=None, dst=None): """Find all Paths Args: src (:class:`.Host` or `None`): the Host to use as source, `"Local"` if `(int)0` dst (:class:`.Endpoint`): the Endpoint to use as destination Returns: A list of all `Path` s in the :class:`.Workspace` """ if src is not None and src == 0: src_id = 0 elif src is not None: src_id = src.id ret = [] cursor = Db.get().cursor() if src is None: if dst is None: req = cursor.execute('SELECT src, dst FROM paths') else: req = cursor.execute('SELECT src, dst FROM paths WHERE dst=?', (dst.id, )) else: if dst is None: req = cursor.execute('SELECT src, dst FROM paths WHERE src=?', (src_id, )) else: req = cursor.execute('SELECT src, dst FROM paths WHERE src=? AND dst=?', \ (src_id, dst.id)) for row in req: ret.append( Path(Host.find_one(host_id=row[0]), Endpoint.find_one(endpoint_id=row[1]))) cursor.close() return ret
def endpoint_del(self, endpoint): """Remove an :class:`Endpoint` from the workspace Args: endpoint (str): the `Endpoint` 's string (ip:port) """ if endpoint[0] == "!": tag = Tag(endpoint[1:]) for endpoint in tag.endpoints: self.unstore(endpoint.delete()) return True try: endpoint = Endpoint.find_one(ip_port=endpoint) except ValueError: print("Could not find endpoint.") return False if endpoint is None: print("Could not find endpoint.") return False if self.options["endpoint"] == endpoint: self.set_option("endpoint", None) self.unstore(endpoint.delete()) return True
def path_del(self, src, dst): if str(src).lower() != "local": if src not in [host.name for host in Host.find_all()]: print("Not a known Host name.") return False src = Host.find_one(name=src) if src is None: print("The source Host provided doesn't exist in this workspace") return False else: src = None try: dst = Endpoint.find_one(ip_port=dst) except: print("Please specify valid destination endpoint in the IP:PORT form") if dst is None: print("The destination endpoint provided doesn't exist in this workspace") return False path = Path(src, dst) if path.id is None: print("The specified Path doesn't exist in this workspace.") return False self.unstore(path.delete()) return True
def endpoint_search(self, field, val, show_all=False, add_tag=None): endpoints = Endpoint.search(field, val, show_all) if add_tag is not None: for endpoint in endpoints: endpoint.tag(add_tag) return endpoints
def find_all(cls, endpoint=None, user=None, creds=None, scope=None): """Find all `Connection` matching the criteria If two or more arguments are specified, the returned Connections must match each ("AND") Args: endpoint (:class:`Endpoint` or :class:`Tag`): the `Connection` endpoint to search or a :class:`Tag` of endpoints to search user (:class:`User`): the `Connection` user to search creds (:class:`Creds`): the `Connection` creds to search scope (bool): whether to include only in scope Connections (`True`), out of scope Connections (`False`) or both (`None`) Returns: A list of matching `Connection`. """ ret = [] cursor = Db.get().cursor() query = 'SELECT endpoint, user, cred FROM connections' params = [] first = True if endpoint is not None and not isinstance(endpoint, Tag): if first: query = query + ' WHERE ' first = False else: query = query + ' AND ' query = query + 'endpoint=?' params.append(endpoint.id) elif endpoint is not None and isinstance(endpoint, Tag): if first: query = query + ' WHERE (' first = False else: query = query + ' AND (' first_endpoint = True for end in endpoint.endpoints: if not first_endpoint: query = query + ' OR ' else: first_endpoint = False query = query + 'endpoint=?' params.append(end.id) query = query + ' )' if user is not None: if first: query = query + ' WHERE ' first = False else: query = query + ' AND ' query = query + 'user=?' params.append(user.id) if creds is not None: if first: query = query + ' WHERE ' first = False else: query = query + ' AND ' query = query + 'cred=?' params.append(creds.id) req = cursor.execute(query, tuple(params)) 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 is None or conn.scope == scope: ret.append(conn) cursor.close() return ret
def run(cls, stmt, workspace): outfile = getattr(stmt, 'output') findings = getattr(stmt, 'findings', False) dotcode = 'digraph compromission_graph {\nnode [shape=plain,fontname="monospace"];\nrankdir="LR";\n' for endpoint in workspace.get_objects(endpoints=True, scope=True): label = "<table cellborder='1' cellspacing='0'><tr><td>" + str( endpoint) + "</td></tr>" if endpoint.host is not None: label = label + '<tr><td>' + str(endpoint.host) + '</td></tr>' if findings: foundEndpoints = Endpoint.find_all(found=endpoint, scope=True) foundUsers = User.find_all(found=endpoint, scope=True) foundCreds = Creds.find_all(found=endpoint, scope=True) if foundEndpoints or foundUsers or foundCreds: label = label + "<tr><td><table cellborder='1' cellspacing='0'><tr><td colspan='2'>Findings</td></tr>" if foundEndpoints: label = label + '<tr><td>Endpoints</td><td>' first = True for foundEndpoint in foundEndpoints: if not first: label = label + '<br />' else: first = False label = label + str(foundEndpoint) label = label + '</td></tr>' if foundUsers: label = label + '<tr><td>Users</td><td>' first = True for user in foundUsers: if not first: label = label + '<br />' else: first = False label = label + str(user) label = label + '</td></tr>' if foundCreds: label = label + '<tr><td>Creds</td><td>' first = True for cred in foundCreds: if not first: label = label + '<br />' else: first = False label = label + str(cred) label = label + '</td></tr>' label = label + "</table></td></tr>" label = label + "</table>" dotcode = dotcode + 'node_' + str( endpoint.id) + ' [label=<' + label + '>]\n' if endpoint.found is None: dotcode = dotcode + '"local" -> "node_' + str( endpoint.id) + '"\n' else: dotcode = dotcode + '"node_' + str( endpoint.found.id) + '" -> "node_' + str( endpoint.id) + '"\n' dotcode = dotcode + '}' with open(outfile, "w") as f: f.write(dotcode) print("Export saved as " + outfile) return True
def set_option(self, option, value): """Set an option for the `Workspace` Args: option (str): the option to set value (str): the new value """ if option == 'connection': if value is None: self.options['endpoint'] = None self.options['user'] = None self.options['creds'] = None print("endpoint => "+str(self.options['endpoint'])) print("user => "+str(self.options['user'])) print("creds => "+str(self.options['creds'])) elif '@' not in value or ':' not in value: return connection = Connection.from_target(value) if connection is None: return self.options['endpoint'] = connection.endpoint self.options['user'] = connection.user self.options['creds'] = connection.creds print("endpoint => "+str(self.options['endpoint'])) print("user => "+str(self.options['user'])) print("creds => "+str(self.options['creds'])) return if not option in list(self.options.keys()): raise ValueError(option+" isn't a valid option.") if value is not None: value = value.strip() if option == "endpoint": if value[0] == "!": value = Tag(value[1:]) else: endpoint = Endpoint.find_one(ip_port=value) if endpoint is None: raise ValueError value = endpoint elif option == "user": user = User.find_one(name=value) if user is None: raise ValueError value = user elif option == "creds": if value[0] == '#': creds_id = value[1:] else: creds_id = value creds = Creds.find_one(creds_id=creds_id) if creds is None: raise ValueError value = creds elif option == "payload": value = Extensions.payloads[value] self.options[option] = value else: self.options[option] = None print(option+" => "+str(self.options[option]))
def enum_connect(self, target=None, force=False, unprobed=False): if target is not None: if '@' not in target: host = Host.find_one(name=target) if host is not None: conn = Connection.find_one(endpoint=host.closest_endpoint) if conn is not None: return [conn] raise ValueError("Supplied value doesn't match a known host or a connection string") auth, sep, endpoint = target.partition('@') if endpoint == "*": endpoints = Endpoint.find_all(scope=True) elif endpoint[0] == "!": tag = Tag(endpoint[1:]) endpoints = tag.endpoints else: endpoint = Endpoint.find_one(ip_port=endpoint) if endpoint is None: raise ValueError("Supplied endpoint isn't in workspace") endpoints = [endpoint] user, sep, cred = auth.partition(":") if sep == "": raise ValueError("No credentials supplied") if user == "*": users = User.find_all(scope=True) else: user = User.find_one(name=user) if user is None: raise ValueError("Supplied user isn't in workspace") users = [user] if cred == "*": creds = Creds.find_all(scope=True) else: if cred[0] == "#": cred = cred[1:] cred = Creds.find_one(creds_id=cred) if cred is None: raise ValueError("Supplied credentials aren't in workspace") creds = [cred] if len(endpoints)*len(users)*len(creds) == 1: return [Connection(endpoints[0], users[0], creds[0])] else: user = self.options["user"] if user is None: users = User.find_all(scope=True) else: users = [user] endpoint = self.options["endpoint"] if isinstance(endpoint, Tag): endpoints = endpoint.endpoints elif endpoint is None: endpoints = Endpoint.find_all(scope=True) else: endpoints = [endpoint] cred = self.options["creds"] if cred is None: creds = Creds.find_all(scope=True) else: creds = [cred] if len(endpoints)*len(users)*len(creds) == 1: return [Connection(endpoints[0], users[0], creds[0])] ret = [] for endpoint in endpoints: if not unprobed and not endpoint.reachable: continue for user in users: if len(creds) != 1: working_connections = Connection.find_all(endpoint=endpoint, user=user) if not force and working_connections: print("Connection already found with user "+str(user)+" on endpoint "+str(endpoint)+", creds bruteforcing is disabled. Specify creds or use --force.") continue for cred in creds: conn = Connection(endpoint, user, cred) if force: ret.append(conn) else: if conn.id is None: ret.append(conn) return ret