PyHackTheBox
Advanced tools
@@ -0,1 +1,14 @@ | ||
| """ | ||
| Examples: | ||
| Starting a challenge and submitting the flag:: | ||
| challenge = client.get_challenge(100) | ||
| instance = challenge.start() | ||
| r = remote(instance.ip, instance.port) | ||
| # Do the challenge..... | ||
| instance.stop() | ||
| challenge.submit(flag, difficulty=50) | ||
| """ | ||
| from __future__ import annotations | ||
@@ -2,0 +15,0 @@ |
| API_BASE = "https://www.hackthebox.eu/api/v4/" | ||
| USER_AGENT = "htb-api/0.4.3" | ||
| USER_AGENT = "htb-api/0.4.4" | ||
| DOWNLOAD_COOLDOWN = 30 |
@@ -150,7 +150,7 @@ from __future__ import annotations | ||
| # noinspection PyUnresolvedReferences | ||
| def get_machine(self, machine_id: int) -> "Machine": | ||
| def get_machine(self, machine_id: int | str) -> "Machine": | ||
| """ | ||
| Args: | ||
| machine_id: The platform ID of the `Machine` to fetch | ||
| machine_id: The platform ID or name of the `Machine` to fetch | ||
@@ -185,7 +185,7 @@ Returns: The requested `Machine` | ||
| # noinspection PyUnresolvedReferences | ||
| def get_challenge(self, challenge_id: int) -> "Challenge": | ||
| def get_challenge(self, challenge_id: int | str) -> "Challenge": | ||
| """ | ||
| Args: | ||
| challenge_id: The platform ID of the `Challenge` to fetch | ||
| challenge_id: The platform ID or name of the `Challenge` to fetch | ||
@@ -374,3 +374,3 @@ Returns: The requested `Challenge` | ||
| """ | ||
| Returns: A list of `VPNServer`s | ||
| Returns: A list of `VPNServer` | ||
@@ -377,0 +377,0 @@ Args: |
+103
-5
@@ -1,8 +0,9 @@ | ||
| from typing import List | ||
| import dateutil.parser | ||
| from datetime import datetime, timedelta | ||
| from typing import List, Union | ||
| from . import htb | ||
| import dateutil.parser | ||
| from . import htb, vpn | ||
| from .errors import IncorrectArgumentException, IncorrectFlagException | ||
| from .solve import MachineSolve | ||
| from .errors import IncorrectArgumentException, IncorrectFlagException | ||
| from .utils import parse_delta | ||
@@ -21,3 +22,3 @@ | ||
| root_owns: The number of root owns the Machine has | ||
| free: Whethere the Machine is available on free servers | ||
| free: Whether the Machine is available on free servers | ||
| user_owned: Whether the active User has owned the Machine's user account | ||
@@ -29,2 +30,3 @@ root_owned: Whether the active User has owned the Machine's user account | ||
| difficulty: The difficulty of the machine | ||
| ip: The IP address of the machine | ||
@@ -74,2 +76,4 @@ active: Whether the Machine is active | ||
| _author_ids: List[int] = None | ||
| _is_release: bool = None | ||
| _ip: str = None | ||
@@ -110,2 +114,57 @@ def submit(self, flag: str, difficulty: int): | ||
| @property | ||
| def is_release(self): | ||
| if self._is_release is not None: | ||
| return self._is_release | ||
| self._is_release = False | ||
| data = self._client.do_request("connections")["data"] | ||
| try: | ||
| if data['release_arena']['machine']['id'] == self.id: | ||
| self._is_release = True | ||
| except AttributeError: | ||
| pass | ||
| return self._is_release | ||
| @property | ||
| def ip(self): | ||
| """The IP of an active machine.""" | ||
| if self._ip is not None: | ||
| return self._ip | ||
| listing = self._client.do_request("machine/list")["info"] | ||
| m = next(filter(lambda x: x["id"] == self.id, listing)) | ||
| self._ip = m["ip"] | ||
| return self._ip | ||
| def start(self, release_arena=False) -> Union["MachineInstance", None]: | ||
| """Alias for `Machine.spawn()`""" | ||
| return self.spawn(release_arena) | ||
| 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 = self._client.do_request("release_arena/spawn", post=True) | ||
| if data.get("success") != 1: | ||
| raise Exception(f"Failed to spawn: {data}") | ||
| ip = self._client.do_request("release_arena/active")["info"]["ip"] | ||
| server = self._client.get_current_vpn_server(release_arena=True) | ||
| else: | ||
| data = self._client.do_request("vm/spawn", json_data={"machine_id": self.id}) | ||
| if "Machine deployed" in data.get("message"): | ||
| ip = 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) | ||
| def __repr__(self): | ||
@@ -164,1 +223,40 @@ return f"<Machine '{self.name}'>" | ||
| self._is_summary = True | ||
| 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 = None | ||
| server: vpn.VPNServer = None | ||
| client: htb.HTBClient = None | ||
| machine: Machine = None | ||
| 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}>" | ||
| 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 |
+20
-1
@@ -0,1 +1,16 @@ | ||
| """ | ||
| Examples: | ||
| Retrieving the current VPN server:: | ||
| print(client.get_current_vpn_server()) | ||
| Switching to a given VPN server:: | ||
| 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") | ||
| """ | ||
| from __future__ import annotations | ||
@@ -24,3 +39,2 @@ | ||
| def __init__(self, data: dict, client: "HTBClient", summary=False): | ||
| """Initialise a `VPNServer` using API data""" | ||
| self._client = client | ||
@@ -38,2 +52,7 @@ self._detailed_func = lambda x: None | ||
| def switch(self) -> bool: | ||
| """ | ||
| Switches the client to use this VPN server | ||
| Returns: Whether the switch was completed successfully | ||
| """ | ||
| # TODO: Throw exception on failure | ||
@@ -40,0 +59,0 @@ return self._client.do_request(f"connections/servers/switch/{self.id}", post=True)["status"] is True |
+1
-1
| Metadata-Version: 2.1 | ||
| Name: PyHackTheBox | ||
| Version: 0.4.3 | ||
| Version: 0.4.4 | ||
| Summary: A wrapper for the Hack The Box API. | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/clubby789/htb-api |
| Metadata-Version: 2.1 | ||
| Name: PyHackTheBox | ||
| Version: 0.4.3 | ||
| Version: 0.4.4 | ||
| Summary: A wrapper for the Hack The Box API. | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/clubby789/htb-api |
+1
-1
@@ -11,3 +11,3 @@ import setuptools | ||
| name="PyHackTheBox", | ||
| version="0.4.3", | ||
| version="0.4.4", | ||
| author="clubby789@github.com", | ||
@@ -14,0 +14,0 @@ author_email="clubby789@gmail.com", |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
71178
6.36%1615
6.95%