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 host_del(self, host): """Remove a :class:`Host` from the workspace Args: name (str): The `Host` 's username """ if host not in [host.name for host in Host.find_all()]: print("Not a known Host name.") return False host = Host.find_one(name=host) self.unstore(host.delete()) return True
def get(cls, dst, first=True): """Get the chain of paths from `"Local"` to an `Endpoint` Args: dst (:class:`Endpoint`): the destination `Endpoint` Returns: A `List` of :class:`Hosts` forming a chain from `"Local"` to dst Raises: NoPathError: if no path could be found to `dst` """ try: previous_hop = Host.find_one(prev_hop_to=dst) except NoPathError as exc: raise exc if previous_hop is None: return [None, dst.host] chain = cls.get(previous_hop.closest_endpoint, first=False) if first: chain.append(dst) return chain chain.append(dst.host) return chain
def host_search(self, field, val, show_all=False, add_tag=None): hosts = Host.search(field, val, show_all) if add_tag is not None: for host in hosts: for endpoint in host.endpoints: endpoint.tag(add_tag) return hosts
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_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 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 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 host_untag(self, host, tagname): """Remove a :class:`Tag` from an :class:`Host` Args: host (str): the `Host` 's string (ip:port) tagname (str): the :class:`Tag` name """ if tagname[0] == "!": tagname = tagname[1:] try: host = Host.find_one(name=host) except ValueError: print("Could not find host.") return False if host is None: print("Could not find host.") return False for endpoint in host.endpoints: endpoint.untag(tagname) return True
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 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 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 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 __init__(self, ip, port): #check if ip is actually an IP ipaddress.ip_address(ip) if not isinstance(port, int) and not port.isdigit(): raise ValueError("The port is not a positive integer") self.ip = ip self.__port = port self.host = None self.id = None self.scope = True self.reachable = None self.distance = None self.found = None self.tags = set() cursor = Db.get().cursor() cursor.execute( 'SELECT id, host, reachable, distance, scope, found FROM endpoints WHERE ip=? AND port=?', (self.ip, self.port)) saved_endpoint = cursor.fetchone() if saved_endpoint is not None: self.id = saved_endpoint[0] self.host = Host.find_one(host_id=saved_endpoint[1]) if saved_endpoint[2] is None: self.reachable = None else: self.reachable = saved_endpoint[2] != 0 if saved_endpoint[3] is not None: self.distance = saved_endpoint[3] self.scope = saved_endpoint[4] != 0 if saved_endpoint[5] is not None: self.found = Endpoint.find_one(endpoint_id=saved_endpoint[5]) for row in cursor.execute('SELECT name FROM tags WHERE endpoint=?', (self.id, )): self.tags.add(row[0]) cursor.close()
def identify(self): #TODO """Indentify the host""" if self.transport is None: raise ConnectionClosedError try: ######## hostname ######## chan = self.transport.open_channel("session", timeout=3) hostname = "" chan.exec_command("hostname") try: x = u(chan.recv(1024)) while len(x) != 0: hostname = hostname + x x = u(chan.recv(1024)) except socket.timeout: pass chan.close() hostname = hostname.rstrip() ######## uname ######## chan = self.transport.open_channel("session", timeout=3) uname = "" chan.exec_command("uname -a") try: x = u(chan.recv(1024)) while len(x) != 0: uname = uname + x x = u(chan.recv(1024)) except socket.timeout: pass chan.close() uname = uname.rstrip() ######## issue ######## chan = self.transport.open_channel("session", timeout=3) issue = "" chan.exec_command("cat /etc/issue") try: x = u(chan.recv(1024)) while len(x) != 0: issue = issue + x x = u(chan.recv(1024)) except socket.timeout: pass chan.close() issue = issue.rstrip() ######## machineid ######## chan = self.transport.open_channel("session", timeout=3) machine_id = "" chan.exec_command("cat /etc/machine-id") try: x = u(chan.recv(1024)) while len(x) != 0: machine_id = machine_id + x x = u(chan.recv(1024)) except socket.timeout: pass chan.close() machine_id = machine_id.rstrip() ######## macs ######## chan = self.transport.open_channel("session", timeout=3) mac_str = "" chan.exec_command( "for i in `ls -l /sys/class/net/ | grep -v virtual | grep 'devices' | tr -s '[:blank:]' | cut -d ' ' -f 9 | sort`; do ip l show $i | grep ether | tr -s '[:blank:]' | cut -d ' ' -f 3; done" ) try: x = u(chan.recv(1024)) while len(x) != 0: mac_str = mac_str + x x = u(chan.recv(1024)) except socket.timeout: pass mac_str = mac_str.rstrip() macs = mac_str.split() chan.close() ######## host ######## new_host = Host(hostname, uname, issue, machine_id, macs) if new_host.id is None: print("\t" + str(self) + " is a new host: " + new_host.name) else: print("\t" + str(self) + " is an existing host: " + new_host.name) if not new_host.scope: self.endpoint.scope = False new_host.save() self.endpoint.host = new_host self.endpoint.save() except Exception as exc: print("Error : " + str(exc)) return False return True
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
def probe(self, targets, gateway="auto", verbose=False, find_new=False): for endpoint in targets: print("Probing \033[1;34m"+str(endpoint)+"\033[0m > ", end="", flush=True) if verbose: print("") conn = Connection(endpoint, None, None) working = False if not find_new and endpoint.reachable and str(gateway) == "auto": if verbose: print("\nEndpoint is supposed to be reachable, trying...") working = conn.probe(verbose=verbose) host = Host.find_one(prev_hop_to=endpoint) if not working and str(gateway) != "auto": if verbose: print("\nA gateway was given, trying...") if gateway == "local": gateway_conn = None host = None else: host = Host.find_one(name=gateway) gateway_conn = Connection.find_one(endpoint=host.closest_endpoint) try: working = conn.probe(gateway=gateway_conn, verbose=verbose) except ConnectionClosedError as exc: print("\nError: "+str(exc)) return if not working and not find_new: try: Path.get(endpoint) except NoPathError: pass else: if verbose: print("\nThere is an existing path to the Endpoint, trying...") working = conn.probe(verbose=verbose) host = Host.find_one(prev_hop_to=endpoint) if not working and host is not None: self.path_del(host, endpoint) if not working: if verbose: print("\nTrying to reach directly from local...") host = None working = conn.probe(gateway=None, verbose=verbose) if not working: if verbose: print("\nTrying from every Host from closest to furthest...") hosts = Host.find_all(scope=True) hosts.sort(key=lambda h: h.distance) working = False for host in hosts: gateway_endpoint = host.closest_endpoint loop_gateway = Connection.find_one(endpoint=gateway_endpoint) working = conn.probe(gateway=loop_gateway, verbose=verbose) if working: break if working: path = Path(host, endpoint) path.save() if host is None: print("\033[1;32mOK\033[0m: reached directly from \033[1;34mlocal\033[0m.") else: print("\033[1;32mOK\033[0m: reached using \033[1;34m"+str(host)+"\033[0m as gateway") else: print("\033[1;31mKO\033[0m: could not reach the endpoint.") if verbose: print("########################\n")