### VPN server retrieval and switching examples Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/vpn.html Examples from the module docstring: retrieving the current VPN server and switching to a given VPN server by friendly name, then downloading the OVPN file. ```python print(client.get_current_vpn_server()) ``` ```python req = input("What server? ") server = next(filter(lambda x: x.friendly_name == req), client.get_all_vpn_servers())) server.switch() server.download(path="/tmp/out.ovpn") ``` -------------------------------- ### Challenge start and flag submission example Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/challenge.html Workflow for starting a challenge, interacting with its remote instance, stopping it, and submitting the flag with a difficulty rating of 50. ```python challenge = client.get_challenge(100) instance = challenge.start() r = remote(instance.ip, instance.port) # Do the challenge..... instance.stop() challenge.submit(flag, difficulty=50) ``` -------------------------------- ### Challenge.start() Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/challenge.html Requests the challenge be started and returns a DockerInstance. Raises NoDockerException if the challenge does not have a Docker instance available. ```APIDOC ## Challenge.start() ### Description Requests the challenge be started and returns a DockerInstance. Raises NoDockerException if the challenge does not have a Docker instance available. ### Method POST ### Endpoint challenge/start ### Parameters #### Request Body - **challenge_id** (integer) - Required - The ID of the challenge ### Request Example { "challenge_id": 100 } ### Response #### Success Response (200) - **ip** (string) - The IP address of the Docker instance - **port** (integer) - The port of the Docker instance - **id** (string) - The ID of the Docker instance #### Response Example { "ip": "127.0.0.1", "port": 1337, "id": "abc123" } ### Error Handling - **NoDockerException** - Raised if the challenge does not have a Docker instance available ``` -------------------------------- ### Challenge.start() -> DockerInstance Source: https://pyhackthebox.readthedocs.io/en/latest/challenge.html Requests that the challenge be started and returns the DockerInstance that was started. ```APIDOC ## Challenge.start() -> DockerInstance ### Description Requests that the challenge be started and returns the DockerInstance that was started. ### Method ` method` ### Endpoint `Challenge.start()` ### Parameters None ### Returns - **DockerInstance** - The DockerInstance that was started ``` -------------------------------- ### Machine.start(release_arena=False) Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Alias for the spawn() method. Starts a machine instance, optionally using the Release Arena. ```APIDOC ## start(release_arena=False) ### Description Alias for `Machine.spawn()`. Starts an instance of the machine. ### Parameters - **release_arena** (bool) - Optional - Whether to use Release Arena to spawn the machine. Defaults to False. ### Returns - **MachineInstance** - The spawned machine instance, or None if spawning fails. ### Example ```python machine = client.get_machine(123) instance = machine.start() ``` ``` -------------------------------- ### start method alias for spawn Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Alias method that delegates to spawn() with the same release_arena argument. It is documented and returns the spawned MachineInstance or None. ```python [docs] def start(self, release_arena=False) -> Union["MachineInstance", None]: """Alias for `Machine.spawn()`""" return self.spawn(release_arena) ``` -------------------------------- ### Get a challenge by ID Source: https://pyhackthebox.readthedocs.io/en/latest/index.html Retrieves a challenge by its ID and prints the challenge's name. Assumes a client instance is already created. ```python client.get_challenge(200) print(challenge.name) ``` -------------------------------- ### API request with authentication and rate-limit retry Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Builds request headers with a User-Agent and optional Bearer token (app token or refreshed access token), then sends GET or POST requests to the API. Retries on HTTP 429 with a 1-second sleep, raises NotFoundException on 404, and returns raw content when download is true, otherwise parsed JSON. ```python headers = {"User-Agent": USER_AGENT} if authorized: # Don't use authorization if the API base URL isn't the real one - # i.e. we're running a test if self._app_token is not None: headers["Authorization"] = "Bearer " + self._app_token elif self._access_token is not None and self._refresh_token is not None: if jwt_expired(self._access_token): self._refresh_access_token() headers["Authorization"] = "Bearer " + self._access_token else: raise AuthenticationException("No authentication tokens available") while True: if not json_data and not data: if post: r = requests.post( self._api_base + endpoint, headers=headers, stream=download ) else: r = requests.get( self._api_base + endpoint, headers=headers, stream=download ) else: r = requests.post( self._api_base + endpoint, json=json_data, data=data, headers=headers, stream=download, ) if r.status_code != 429: break # Not sure on the exact ratelimit - loop until we don't get 429 else: time.sleep(1) if r.status_code == 404: raise NotFoundException if download: return r.content else: return r.json() ``` -------------------------------- ### VPNServer Methods Source: https://pyhackthebox.readthedocs.io/en/latest/vpn.html Documentation for the VPNServer class methods: download and switch. ```APIDOC ## [VPN] VPNServer.download ### Description Downloads the OVPN configuration file for this VPN server. If no path is provided, the file is saved to the current directory. ### Method VPN (method of VPNServer class) ### Endpoint VPNServer.download(path=None, tcp=False) ### Parameters - **path** (str) - Optional - The name of the OVPN file to download to. If none is provided, it is saved to the current directory. - **tcp** (bool) - Optional - Download TCP instead of UDP. ### Returns - **str** - The path of the file. ### Example ```python server.download(path='my_vpn.ovpn', tcp=True) ``` ``` ```APIDOC ## [VPN] VPNServer.switch ### Description Switches the client to use this VPN server. ### Method VPN (method of VPNServer class) ### Endpoint VPNServer.switch() ### Parameters None ### Returns - **bool** - Whether the switch was completed successfully. ### Example ```python success = server.switch() ``` ``` -------------------------------- ### Challenge __init__ method Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/challenge.html Initializes a Challenge instance from API data. If summary is True, only summary fields are set. ```python # noinspection PyUnresolvedReferences def __init__(self, data: dict, client: "HTBClient", summary: bool = False): """Initialise a `Challenge` using API data""" self._client = client self._detailed_func = client.get_challenge # type: ignore self.id = data["id"] self.name = data["name"] self.retired = bool(data["retired"]) self.points = int(data["points"]) self.difficulty = data["difficulty"] self.difficulty_chart = data["difficulty_chart"] self.solves = data["solves"] self.solved = data["solved"] self.likes = data["likes"] self.dislikes = data["dislikes"] self.release_date = dateutil.parser.parse(data["release_date"]) if not summary: self.description = data["description"] self.category = data["category_name"] self._author_ids = [data["author_id"]] if data["author2_id"]: self._author_ids.append(data["author2_id"]) self.has_download = data["download"] self.has_docker = data["docker"] if data["docker_ip"]: self.instance = DockerInstance( data["docker_ip"], data["docker_port"], self.id, self._client ) else: self.instance = None else: self._is_summary = True ``` -------------------------------- ### Initialize HTBClient with credentials Source: https://pyhackthebox.readthedocs.io/en/latest/index.html Sets up an API connection to Hack The Box using the HTBClient class. Requires an email, password, and optionally an OTP code. ```python from hackthebox import HTBClient client = HTBClient(email="user@example.com", password="S3cr3tP455w0rd!", otp="123456") print(client.user) ``` -------------------------------- ### Challenge download method Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/challenge.html Downloads the challenge zip file. Raises NoDownloadException if the challenge has no download, RateLimitException if the cooldown has not elapsed, and writes the file to the specified path or the current working directory. ```python if not self.has_download: raise NoDownloadException if self._client.challenge_cooldown > time.time(): raise RateLimitException( "Challenge download ratelimit exceeded - please do not remove this" ) if path is None: path = os.path.join(os.getcwd(), f"{self.name}.zip") data = cast( bytes, self._client.do_request(f"challenge/download/{self.id}", download=True), ) self._client.challenge_cooldown = int(time.time()) + DOWNLOAD_COOLDOWN with open(path, "wb") as f: f.write(data) return path ``` -------------------------------- ### Machine.__init__ constructor Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Initializes a Machine object from API data. Handles both summary and detailed modes, parsing dates and ownership information. Sets up user and root blood data when available. ```python def __init__(self, data: dict, client: htb.HTBClient, summary: bool = False): self._client = client self._detailed_func = client.get_machine # type: ignore self.id = data["id"] self.name = data["name"] self.os = data["os"] self.points = data["points"] self.release_date = dateutil.parser.parse(data["release"]) self.user_owns = data["user_owns_count"] self.root_owns = data["root_owns_count"] self.user_owned = data["authUserInUserOwns"] self.root_owned = data["authUserInRootOwns"] self.reviewed = data["authUserHasReviewed"] self.stars = float(data["stars"]) self.avatar = data["avatar"] self.difficulty = data["difficultyText"] self.free = data["free"] self._author_ids = [data["maker"]["id"]] if data.get("ip"): self._ip = data["ip"] if data["maker2"]: self._author_ids.append(data["maker2"]["id"]) if not summary: self.active = bool(data["active"]) self.retired = bool(data["retired"]) if data["authUserInUserOwns"]: self.user_own_time = parse_delta(data["authUserFirstUserTime"]) if data["authUserInRootOwns"]: self.root_own_time = parse_delta(data["authUserFirstRootTime"]) self.difficulty_ratings = data["feedbackForChart"] if data["userBlood"]: user_blood_data = { "date": dateutil.parser.parse(data["userBlood"]["created_at"]), "first_blood": True, "id": data["id"], "name": data["name"], "type": "user", } self.user_blood = MachineSolve(user_blood_data, self._client) self.user_blood_time = parse_delta( data["userBlood"]["blood_difference"] ) if data["rootBlood"]: user_blood_data = { "date": dateutil.parser.parse(data["rootBlood"]["created_at"]), "first_blood": True, "id": data["id"], "name": data["name"], "type": "root", } self.root_blood = MachineSolve(user_blood_data, self._client) self.root_blood_time = parse_delta( data["rootBlood"]["blood_difference"] ) else: self._is_summary = True ``` -------------------------------- ### spawn method Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Spawns a machine instance, either in the release arena or the normal VM pool. It raises exceptions on failure, checks the response message for success, and returns a MachineInstance with the obtained IP and VPN server. ```python [docs] def spawn(self, release_arena=False) -> "MachineInstance": """Spawn an instance of this machine. Args: release_arena: Whether to use Release Arena to spawn the machine Returns: The spawned `MachineInstance` """ if release_arena: if not self.is_release: # TODO: Better exception raise Exception("Machine is not on release arena") data = cast(dict, self._client.do_request("release_arena/spawn", post=True)) if data.get("success") != 1: raise Exception(f"Failed to spawn: {data}") ip = cast(dict, self._client.do_request("release_arena/active"))["info"][ "ip" ] server = self._client.get_current_vpn_server(release_arena=True) else: data = cast( dict, self._client.do_request("vm/spawn", json_data={"machine_id": self.id}), ) if "Machine deployed" in cast( str, data.get("message") ) or "You have been assigned" in cast(str, data.get("message")): ip = cast(dict, self._client.do_request(f"machine/profile/{self.id}"))[ "info" ]["ip"] server = self._client.get_current_vpn_server() else: raise Exception(f"Failed to spawn: {data}") return MachineInstance(ip, server, self, self._client) ``` -------------------------------- ### Challenge.download(path=None) -> str Source: https://pyhackthebox.readthedocs.io/en/latest/challenge.html Downloads the challenge's zip file to a specified path or the current directory. Returns the path of the downloaded file. ```APIDOC ## Challenge.download(path=None) -> str ### Description Downloads the challenge's downloadable file to a specified path or the current directory. Returns the path of the downloaded file. ### Method SDK Method ### Endpoint `Challenge.download(path=None)` ### Parameters - **path** (str) - Optional - The name of the zipfile to download to. If none is provided, it is saved to the current directory. ### Returns - **str** - The path of the file ``` -------------------------------- ### Machine.spawn(release_arena=False) Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Spawns an instance of the machine. If release_arena is True, it uses the Release Arena; otherwise, it uses the standard VM spawn endpoint. ```APIDOC ## spawn(release_arena=False) ### Description Spawn an instance of this machine. ### Parameters - **release_arena** (bool) - Optional - Whether to use Release Arena to spawn the machine. Defaults to False. ### Returns - **MachineInstance** - The spawned machine instance. ### Raises - **Exception** - If the machine is not on release arena (when release_arena=True) or if spawning fails. ### Example ```python machine = client.get_machine(123) instance = machine.spawn() ``` ``` -------------------------------- ### HTBClient Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Initializes the HTB API client with authentication credentials and optional session caching. ```APIDOC ## HTBClient ### Description Initializes the HTB API client with authentication credentials and optional session caching. ### Constructor `HTBClient(email: Optional[str] = None, password: Optional[str] = None, otp: Optional[str | int] = None, cache: Optional[str] = None, api_base: str = 'https://www.hackthebox.com/api/v4/', remember: Optional[bool] = False, app_token: Optional[str] = None)` ### Parameters - **email** (str) - Optional - The authenticating user's email address - **password** (str) - Optional - The authenticating user's password - **otp** (str | int) - Optional - The current OTP of the user, if 2FA is enabled - **cache** (str) - Optional - The path to load/store access tokens from/to - **api_base** (str) - Optional - Base URL for the API (default: 'https://www.hackthebox.com/api/v4/') - **remember** (bool) - Optional - Whether to create a long-lasting 'remember me' token - **app_token** (str) - Optional - Authenticate using a provided App Token ### Example ```python from hackthebox import HTBClient client = HTBClient(email="user@example.com", password="S3cr3tP455w0rd!") ``` ``` -------------------------------- ### do_login Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Authenticates against the API. If credentials are not provided, they will be prompted for. ```APIDOC ## do_login ### Description Authenticates against the API. If credentials are not provided, they will be prompted for. ### Method `do_login(email: Optional[str] = None, password: Optional[str] = None, otp: Optional[str | int] = None, remember: Optional[bool] = False, app_token: Optional[str] = None)` ### Parameters - **email** (str) - Optional - The authenticating user's email address - **password** (str) - Optional - The authenticating user's password - **otp** (str | int) - Optional - The current OTP of the user, if 2FA is enabled - **remember** (bool) - Optional - Whether to create a long-lasting 'remember me' token - **app_token** (str) - Optional - Authenticate using a provided App Token ``` -------------------------------- ### HTB client __init__ with token cache support Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Constructor for the API client. If a cache path is provided, it tries to load tokens from that file; on failure it performs a normal login and dumps the tokens, then registers a dump on exit via atexit. Without a cache, it always calls do_login. ```python def __init__( self, email: Optional[str] = None, password: Optional[str] = None, otp: Optional[str | int] = None, cache: Optional[str] = None, api_base: str = API_BASE, remember: Optional[bool] = False, app_token: Optional[str] = None, ): """ Authenticates to the API. If `cache` is set, the client will attempt to load access tokens from the given path. If they cannot be found, or are expired, normal API authentication will take place, and the tokens will be dumped to the file for the next launch. Args: email: The authenticating user's email address password: The authenticating user's password otp: The current OTP of the user, if 2FA is enabled cache: The path to load/store access tokens from/to remember: Whether to create a long-lasting 'remember me' token app_token: Authenticate using a provided App Token """ self._api_base = api_base if cache is not None: if self.load_from_cache(cache) is False: self.do_login(email, password, otp, remember, app_token) self.dump_to_cache(cache) # Make sure we dump our current tokens out when we exit atexit.register(self.dump_to_cache, cache) else: self.do_login(email, password, otp, remember, app_token) ``` -------------------------------- ### User class initialization and methods Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/user.html Defines the User class constructor that parses API data into user attributes, handling both summary and detailed data. Also includes methods to retrieve user content, machines, and challenges. ```python # noinspection PyUnresolvedReferences def __init__(self, data: dict, client: "HTBClient", summary: bool = False): """Initialise a `User` using API data""" self._client = client self._detailed_func = client.get_user # type: ignore self.id = data["id"] self.name = data["name"] self.user_owns = data["user_owns"] self.points = data["points"] self.avatar = data["avatar"] if summary: self._is_summary = True self.ranking = data["rank"] self.root_owns = data["root_owns"] self.user_bloods = data.get("user_bloods_count") or 0 self.root_bloods = data.get("root_bloods_count") or 0 self.rank_name = data.get("rank_text") or "" else: self.ranking = data["ranking"] self.root_owns = data["system_owns"] self.user_bloods = data["user_bloods"] self.root_bloods = data["system_bloods"] self.rank_name = data["rank"] self.respects = data["respects"] self.university = data["university"] self.university_name = data["university_name"] self.description = data["description"] self.github = data["github"] self.linkedin = data["linkedin"] self.twitter = data["twitter"] self.website = data["website"] self.respected = data.get("isRespected", False) self.followed = data.get("isFollowed", False) self.rank_progress = data["current_rank_progress"] self.next_rank = data["next_rank"] self.next_rank_points = data["next_rank_points"] self.rank_ownership = float(data["rank_ownership"]) self.rank_requirement = data["rank_requirement"] self.country_name = data["country_name"] self.team = data["team"] self.public = bool(data["public"]) # noinspection PyUnresolvedReferences def get_content(self): return Content(self.id, self._client) # noinspection PyUnresolvedReferences def get_machines(self): return self.get_content().machines # noinspection PyUnresolvedReferences def get_challenges(self): return self.get_content().challenges ``` -------------------------------- ### Machine and MachineInstance Methods Source: https://pyhackthebox.readthedocs.io/en/latest/machine.html Methods for managing Hack The Box machines and their instances. ```APIDOC ## Machine.spawn ### Description Spawns an instance of this machine. ### Method spawn ### Parameters - **release_arena** (bool) - Optional - Whether to use Release Arena to spawn the machine ### Returns - **MachineInstance** - The spawned MachineInstance ``` ```APIDOC ## Machine.start ### Description Alias for Machine.spawn(). ### Method start ### Parameters - **release_arena** (bool) - Optional - Whether to use Release Arena to spawn the machine ### Returns - **MachineInstance** - The spawned MachineInstance ``` ```APIDOC ## Machine.submit ### Description Submits a flag for a Machine. ### Method submit ### Parameters - **flag** (str) - Required - The flag for the Machine - **difficulty** (int) - Required - A rating between 10 and 100 of the Machine difficulty. Must be a multiple of 10. ### Returns - **str** - The result of the submission ``` ```APIDOC ## MachineInstance.reset ### Description Request the instance be reset. ### Method reset ### Parameters None ### Returns None ``` ```APIDOC ## MachineInstance.stop ### Description Request the instance be stopped. ### Method stop ### Parameters None ### Returns None ``` -------------------------------- ### Connect to HTB API with HTBClient Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Initializes an HTBClient with email and password to authenticate against the HackTheBox API. The client will prompt for credentials if not provided, and can use a cache file to store tokens. ```python from hackthebox import HTBClient client = HTBClient(email="user@example.com", password="S3cr3tP455w0rd!") ``` -------------------------------- ### Search for a user Source: https://pyhackthebox.readthedocs.io/en/latest/index.html Searches for a user by username and prints the search results. Assumes a client instance is already created. ```python results = client.search("g0blin") print(results.users) ``` -------------------------------- ### get_all_vpn_servers Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Returns a list of VPNServer objects. ```APIDOC ## get_all_vpn_servers ### Description Returns a list of VPNServer objects. ### Method `get_all_vpn_servers(release_arena=False) -> List[VPNServer]` ### Parameters - **release_arena** (bool) - Optional - Use the release arena VPN servers ### Returns - A list of VPNServer ``` -------------------------------- ### get_user: Fetch user profile Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Fetches a user's basic profile by platform ID. The response data is extracted from the `profile` key and wrapped in a `User` object. ```python def get_user(self, user_id: int) -> "User": """ Args: user_id: The platform ID of the `User` to fetch Returns: The requested `User` """ from .user import User data = cast(dict, self.do_request(f"user/profile/basic/{user_id}"))["profile"] return User(data, self) ``` -------------------------------- ### MachineInstance class definition and __init__ Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Defines the MachineInstance class representing an active machine instance. The constructor stores the IP, server, machine, and client references. ```python class MachineInstance: """Representation of an active machine instance Attributes: ip: The IP the instance can be reached at server: The `VPNServer` that the machine is on machine: The `Machine` this is an instance of client: The passed-through API client """ ip: str server: vpn.VPNServer client: htb.HTBClient machine: Machine def __init__( self, ip: str, server: vpn.VPNServer, machine: Machine, client: htb.HTBClient ): self.client = client self.ip = ip self.server = server self.machine = machine def __repr__(self): return f"<'{self.machine.name}'@{self.server.friendly_name} - {self.ip}>" ``` -------------------------------- ### get_active_machine(release_arena: bool = False) -> Optional[MachineInstance] Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Retrieves the Machine currently assigned to the user, optionally releasing the arena first. ```APIDOC ## get_active_machine(release_arena: bool = False) -> Optional[MachineInstance] ### Description Retrieves the Machine currently assigned to the user, optionally releasing the arena first. ### Method get_active_machine ### Parameters - **release_arena** (bool) - Optional - Whether to release the arena before retrieving the active machine ### Returns The `Machine` currently assigned (or active) to user, or None if no machine is active ``` -------------------------------- ### Machine.submit(flag, difficulty) Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Documentation for the Machine.submit method, which submits a flag for a Hack The Box machine. ```APIDOC ## Machine.submit(flag, difficulty) ### Description Submits a flag for a Machine. Validates the difficulty rating and sends the flag to the HTB API. ### Method POST ### Endpoint machine/own ### Parameters #### Request Body - **flag** (string) - Required - The flag for the Machine - **id** (integer) - Required - The ID of the Machine - **difficulty** (integer) - Required - A rating between 10 and 100 of the Machine difficulty. Must be a multiple of 10. ### Request Example { "flag": "HTB{example_flag}", "id": 123, "difficulty": 50 } ### Response #### Success Response (200) - **message** (string) - The response message from the API #### Response Example { "message": "Machine owned!" } ### Error Handling - **IncorrectArgumentException** - Raised if difficulty is not a multiple of 10 between 10 and 100 - **IncorrectFlagException** - Raised if the flag is incorrect - **UserAlreadySubmitted** - Raised if the user account is already owned - **RootAlreadySubmitted** - Raised if the root account is already owned - **SolveError** - Raised for other submission errors ``` -------------------------------- ### do_login authentication method Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Method signature for performing API login. Accepts email, password, OTP, remember flag, and app token; if credentials are omitted, the user is prompted for them. ```python def do_login( self, email: Optional[str] = None, password: Optional[str] = None, otp: Optional[str | int] = None, remember: Optional[bool] = False, app_token: Optional[str] = None, ): """ Authenticates against the API. If credentials are not provided, they will be prompted for. """ ``` -------------------------------- ### get_challenge Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Fetches a Challenge by its platform ID or name. ```APIDOC ## get_challenge ### Description Fetches a Challenge by its platform ID or name. ### Method `get_challenge(challenge_id: int | str) -> Challenge` ### Parameters - **challenge_id** (int | str) - Required - The platform ID or name of the Challenge to fetch ### Returns - The requested Challenge ``` -------------------------------- ### VPNServer Methods Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/vpn.html Methods for interacting with VPN servers in the Hack The Box API client. ```APIDOC ## VPNServer.switch() ### Description Switches the client to use this VPN server. Returns whether the switch was completed successfully. ### Method switch() ### Parameters None ### Returns - **bool** - Whether the switch was completed successfully ### Exceptions - **CannotSwitchWithActive** - Raised if the user must stop an active machine before switching VPN - **VpnException** - Raised for other switch failures ``` ```APIDOC ## VPNServer.download(path=None, tcp=False) ### Description Downloads the OVPN file for this VPN server. If no path is provided, the file is saved to the current directory with the server's friendly name as the filename. If TCP is requested, the download uses a different URL. ### Method download(path=None, tcp=False) ### Parameters - **path** (str) - Optional - The name of the OVPN file to download to. If none is provided, it is saved to the current directory. - **tcp** (bool) - Optional - Download TCP instead of UDP. ### Returns - **str** - The path of the downloaded file ``` -------------------------------- ### load_from_cache token loading Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Loads access, refresh, and app tokens from a JSON cache file. Returns False if the file does not exist or if the access token is expired and the refresh attempt fails with AuthenticationException; otherwise returns True. ```python def load_from_cache(self, cache: str) -> bool: """ Args: cache: The cache file path Returns: Whether loading from the cache was successful """ if not os.path.exists(cache): return False with open(cache, "r") as f: data = json.load(f) self._access_token = data.get("access_token") self._refresh_token = data.get("refresh_token") self._app_token = data.get("app_token") if self._access_token is not None: if jwt_expired(self._access_token): try: self._refresh_access_token() # Our refresh token is also invalid, we must log in again except AuthenticationException: return False return True ``` -------------------------------- ### MachineInstance.stop method Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Requests the instance be stopped. Uses different API endpoints for release arena machines versus regular VMs. Sets all attributes to None after stopping to prevent further usage. ```python def stop(self): """Request the instance be stopped.""" if self.machine.is_release: self.client.do_request("release_arena/terminate", post=True) else: self.client.do_request( "vm/terminate", json_data={"machine_id": self.machine.id} ) # Can't delete references to the object from here so we just have # to set everything to None and prevent further usage self.server = None self.ip = None self.client = None self.machine = None ``` -------------------------------- ### __repr__ method Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Defines the string representation of a Machine object, returning a formatted string with the machine's name. ```python def __repr__(self): return f"" ``` -------------------------------- ### get_team: Fetch team info Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Fetches team information by platform ID. The response is wrapped in a `Team` object. ```python def get_team(self, team_id: int) -> "Team": """ Args: team_id: The platform ID of the `Team` to fetch Returns: The requested `Team` """ from .team import Team data = cast(dict, self.do_request(f"team/info/{team_id}")) return Team(data, self) ``` -------------------------------- ### Challenge repr method Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/challenge.html Returns a string representation of the challenge, e.g., . ```python def __repr__(self): return f"" ``` -------------------------------- ### MachineInstance.reset method Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html Requests the instance be reset. Handles both release arena and VM endpoints, checking response messages to determine success or raise TooManyResetAttempts or MachineException. ```python def reset(self): """Request the instance be reset.""" if self.machine.is_release: resp = self.client.do_request( "release_arena/reset", json_data={"machine_id": self.machine.id} ) else: resp = self.client.do_request( "vm/reset", json_data={"machine_id": self.machine.id} ) # VM if resp["message"].endswith(" will be reset in 1 minute."): return True elif resp["message"] == "Too many reset machine attempts. Try again later!": raise TooManyResetAttempts # RA elif resp["success"]: return True elif resp["message"].startswith("You must wait"): raise TooManyResetAttempts raise MachineException ``` -------------------------------- ### Machine class and submit method for HTB machine flag submission Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/machine.html The Machine class represents Hack The Box machines with attributes for name, OS, points, release date, ownership counts, and difficulty. The submit method validates that the difficulty is a multiple of 10 between 10 and 100, then sends a request to the 'machine/own' endpoint with the flag and difficulty. It raises specific exceptions for incorrect flags, already owned user or root, and other solve errors. ```python from datetime import datetime, timedelta from typing import List, Union, cast, Optional, TYPE_CHECKING import dateutil.parser from . import htb, vpn from .errors import ( IncorrectArgumentException, IncorrectFlagException, TooManyResetAttempts, MachineException, UserAlreadySubmitted, RootAlreadySubmitted, SolveError, ) from .solve import MachineSolve from .utils import parse_delta if TYPE_CHECKING: from .user import User [docs]class Machine(htb.HTBObject): """The class representing Hack The Box machines Attributes: name: The Machine name os: The name of the operating system points: The points awarded for completion release_date: The date the Machine was released user_owns: The number of user owns the Machine has root_owns: The number of root owns the Machine has free: Whether the Machine is available on free servers user_owned: Whether the active User has owned the Machine's user account root_owned: Whether the active User has owned the Machine's user account reviewed: Whether the active User has reviewed the Machine stars: The average star rating of the Machine avatar: The relative URL of the Machine avatar difficulty: The difficulty of the machine :noindex: ip: The IP address of the machine active: Whether the Machine is active retired: Whether the Machine is retired avg_difficulty: The average numeric difficulty of the Machine completed: Whether the active User has completed the Machine :noindex: user_own_time: How long the active User took to own user :noindex: root_own_time: How long the active User took to own root user_blood: The Solve of the Machine's first user blood root_blood: The Solve of the Machine's first root blood user_own_time: How long the first User took to own user root_own_time: How long the first User took to own root difficulty_ratings: A dict of difficulty ratings given """ name: str os: str points: int release_date: datetime root_owns: int free: bool user_owned: bool root_owned: bool reviewed: bool stars: float avatar: str difficulty: str _detailed_attributes = ( "active", "retired", "user_own_time", "root_own_time", "user_blood", "root_blood", "user_blood_time", "root_blood_time", "difficulty_ratings", ) active: bool retired: bool avg_difficulty: int completed: bool user_own_time: timedelta root_own_time: timedelta user_blood: MachineSolve root_blood: MachineSolve user_blood_time: timedelta root_blood_time: timedelta difficulty_ratings: dict # noinspection PyUnresolvedReferences _authors: Optional[List["User"]] = None _author_ids: List[int] _is_release: Optional[bool] = None _ip: Optional[str] = None [docs] def submit(self, flag: str, difficulty: int) -> str: """Submits a flag for a Machine Args: flag: The flag for the Machine difficulty: A rating between 10 and 100 of the Machine difficulty. Must be a multiple of 10. """ if difficulty < 10 or difficulty > 100 or difficulty % 10 != 0: raise IncorrectArgumentException( reason="Difficulty must be a multiple of 10, between 10 and 100" ) submission = cast( dict, self._client.do_request( "machine/own", json_data={"flag": flag, "id": self.id, "difficulty": difficulty}, ), ) if submission["status"] == 400: if submission["message"] == "Incorrect flag!": raise IncorrectFlagException elif submission["message"] == f"{self.name} user is already owned.": raise UserAlreadySubmitted elif submission["message"] == f"{self.name} root is already owned.": raise RootAlreadySubmitted else: raise SolveError return submission["message"] ``` -------------------------------- ### get_challenges: Fetch challenge list Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Fetches a list of challenges from the API. Use `retired=True` to fetch from the retired list instead of the active list. The `limit` parameter controls the maximum number of challenges returned. ```python def get_challenges(self, limit=None, retired=False) -> List["Challenge"]: """Requests a list of `Challenge` from the API Args: limit: The maximum number of `Challenge` to fetch retired: Whether to fetch from the retired list instead of the active list Returns: A list of `Challenge` """ from .challenge import Challenge if retired: data = cast(dict, self.do_request("challenge/list/retired")) else: data = cast(dict, self.do_request("challenge/list")) challenges = [] for challenge in data["challenges"][:limit]: challenges.append(Challenge(challenge, self, summary=True)) return challenges ``` -------------------------------- ### get_machine(machine_id) Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Requests a Machine from the API by its platform ID or name. Returns the requested Machine. ```APIDOC ## get_machine(machine_id) ### Description Requests a Machine from the API by its platform ID or name. ### Method GET ### Endpoint /machine/{machine_id} ### Parameters #### Path Parameters - **machine_id** (int | str) - Required - The platform ID or name of the Machine to fetch ### Response #### Success Response (200) - **machine** (Machine) - The requested Machine #### Response Example { "machine": {} } ``` -------------------------------- ### get_fortresses Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Fetches a list of Fortresses from the API. ```APIDOC ## get_fortresses(limit=None) ### Description Requests a list of Fortress objects from the API, with an optional limit on the number returned. ### Method GET ### Endpoint fortresses ### Parameters - **limit** (int, optional) - The maximum number of Fortresses to fetch. ### Returns List[Fortress] - A list of Fortress objects. ``` -------------------------------- ### get_fortresses(limit) Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Requests a list of Fortresses from the API. The limit parameter specifies the maximum number of Fortresses to fetch. ```APIDOC ## get_fortresses(limit) ### Description Requests a list of Fortresses from the API. ### Method GET ### Endpoint /fortresses ### Parameters #### Query Parameters - **limit** (Optional[int]) - Optional - The maximum number of Fortresses to fetch ### Response #### Success Response (200) - **fortresses** (List[Fortress]) - A list of Fortresses #### Response Example { "fortresses": [] } ``` -------------------------------- ### get_fortresses: Fetch list of Fortresses Source: https://pyhackthebox.readthedocs.io/en/latest/_modules/hackthebox/htb.html Fetches a list of Fortresses from the API. The response is a dictionary keyed by fortress ID, so the code sorts the items before applying the `limit`. ```python def get_fortresses(self, limit: int = None) -> List["Fortress"]: """Requests a list of Fortresses from the API Args: limit: The maximum number of Fortresses to fetch Returns: A list of Fortresses """ from .fortress import Fortress data = cast(dict, self.do_request(f"fortresses"))["data"] fortresses = [] # For some reason, the fortress list is in the format {"1": , "2": } # instead of [, ], meaning we have to sort it ourselves for fortress_id, fortress in sorted(data.items())[:limit]: fortresses.append(Fortress(fortress, self, summary=True)) return fortresses ``` -------------------------------- ### load_from_cache(cache) Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Loads data from a cache file. Returns whether loading from the cache was successful. ```APIDOC ## load_from_cache(cache) ### Description Loads data from a cache file. ### Method GET ### Endpoint /cache ### Parameters #### Query Parameters - **cache** (str) - Required - The cache file path ### Response #### Success Response (200) - **success** (bool) - Whether loading from the cache was successful #### Response Example { "success": true } ``` -------------------------------- ### get_user(user_id) Source: https://pyhackthebox.readthedocs.io/en/latest/htb.html Requests a User from the API by its platform ID. Returns the requested User. ```APIDOC ## get_user(user_id) ### Description Requests a User from the API by its platform ID. ### Method GET ### Endpoint /user/{user_id} ### Parameters #### Path Parameters - **user_id** (int) - Required - The platform ID of the User to fetch ### Response #### Success Response (200) - **user** (User) - The requested User #### Response Example { "user": {} } ```