Craft HTB
Craft HTB Box Summary Craft is a Linux machine that focuses on source code review, API abuse, and secret management. The initial foothold is obtained by discovering hardcoded credentials in a public Gogs repository, which are then used to authenticate to the application's API. A vulnerable eval() call in the API leads to remote code execution inside a Docker container. Further enumeration of the container exposes database credentials, allowing additional user credentials to be extracted from MySQL. Those credentials grant access to a private Git repository containing an encrypted SSH private key, which can be unlocked using the recovered password to gain SSH access to the host. Finally, a forgotten HashiCorp Vault token with the root policy is abused to generate a one-time SSH password, resulting in full root access to the machine. Attack Overview The attack began by enumerating the exposed web application and discovering additional virtual hosts. A public Gogs repository exposed both a vulnerable eval() implementation and hardcoded API credentials, allowing authenticated remote code execution inside a Docker container. From the container, application configuration files revealed MySQL credentials, which were used to dump additional user accounts from the database. One of those credentials provided access to a private Gogs repository containing an encrypted SSH private key. Using the recovered account password as the key's passphrase granted SSH access to the host as gilfoyle. Finally, a forgotten HashiCorp Vault token with the root policy was discovered in the user's home directory and abused to generate a one-time SSH password, resulting in full root access. Enumeration Nmap Scanning Starting with a service and version scan against the target. Nmap Analysis The scan reveals only two exposed services: | Port | Service | Notes | |------|---------|------| | 22 | SSH | Remote administration | | 443 | HTTPS | Primary web application | Since the web application is the only externally accessible service besides SSH, it becomes the primary focus of our enumeration. First, add the target domain to the local hosts file. Web Enumeration Browsing to https://craft.htb reveals a simple landing page. Screenshot 2026-06-28 at 17-31-43 While exploring the page, hovering over the API navigation link reveals two additional virtual hosts: These are not immediately visible on the homepage, making them valuable enumeration findings. Add both virtual hosts to /etc/hosts. API Documentation Browsing to https://api.craft.htb reveals an API documentation page. Screenshot 2026-06-28 at 17-33-12 The documentation describes several API endpoints used by the application. At this stage we don't have valid credentials, but the documentation will become useful later. Gogs Enumeration Next, browse to https://gogs.craft.htb. A Gogs instance is running. Screenshot 2026-06-28 at 17-57-27 Selecting Explore reveals a public repository. Screenshot 2026-06-28 at 17-57-52 Screenshot 2026-06-28 at 17-58-32 Since the repository is public, reviewing its commit history is the next logical step. Developers often accidentally expose credentials or introduce vulnerable code in commits. Source Code Review While reviewing the commit history, two commits immediately stand out: The first commit (c414b16057) modifies: Examining the changes reveals something very interesting. Screenshot 2026-06-28 at 18-01-20 The application passes user-controlled input directly into Python's eval() function without any sanitization. Using eval() on untrusted input is extremely dangerous because it evaluates the supplied string as Python code. If we can control this parameter, arbitrary Python code can be executed on the server. However, exploiting this endpoint requires authentication. Looking further through the API documentation confirms that a valid API token is required. Screenshot 2026-06-28 at 18-03-48 At this point we know an authenticated RCE exists, but we still need valid credentials. Recovering Credentials Continuing to inspect older commits reveals another interesting change. Commit: Message: Opening the commit reveals hardcoded credentials that were accidentally committed by the developer. Screenshot 2026-06-28 at 18-05-46 Recovered credentials: Obtaining an API Token Returning to the API documentation, authenticate using the recovered credentials. Screenshot 2026-06-28 at 18-09-16 Authentication succeeds and returns a valid JWT token. Rather than manually repeating this process every time, it's more convenient to automate both authentication and exploitation. Automating the Exploit The following Python script performs the complete attack: Authenticates using the recovered credentials. Retrieves a valid API token. Crafts the malicious payload. Sends the payload to the vulnerable endpoint. Starts a Netcat listener. Create a file named: Paste the following script: Run the exploit: Success! The payload executes through the vulnerable eval() call, giving us remote code execution inside the application container. We now have an initial shell as the user running the web application. It looks like our shell is running inside a Docker container rather than on the host itself. This can be confirmed by the presence of the .dockerenv file. At this stage it's worth looking around the application directory for configuration files, source code, or anything that might contain credentials. While enumerating /opt/app, an interesting file named dbtest.py was found. The script is simply a small utility used by the developers to test database connectivity. More importantly, it imports its database configuration from settings.py. Reading that file reveals the database credentials. We now have the following MySQL credentials: The original dbtest.py script is hardcoded to execute a single SQL query. Instead of repeatedly editing the file every time we want to query the database, it's much more convenient to modify it so that it accepts any SQL query as a command-line argument. The original code executes a fixed query: Replace it with the following: With this small modification, the script becomes a generic database client capable of executing any SQL statement we provide. After saving the updated file locally as dbtest1.py, transfer it to the compromised container. Output: Before enumerating the database, it's always a good idea to verify that our modified script works correctly. Executing a simple query: Perfect. The script is working exactly as expected. Now enumerate the available databases and tables. The interesting table is clearly user. Dumping its contents: Great! We have recovered two additional sets of credentials. The next step is to determine where these credentials are valid. Returning to the Gogs login page, the first account (ebachman) is rejected. However, the second set of credentials works successfully. Screenshot 2026-06-28 at 18-36-25 We are now authenticated as gilfoyle. image Unlike the previous account, this user owns a private repository. Browsing through the repository reveals an interesting directory. Screenshot 2026-06-28 at 18-37-42 Inside the repository is an .ssh directory. Screenshot 2026-06-28 at 18-38-13 Inspecting it reveals an SSH private key. Screenshot 2026-06-28 at 18-38-23 After copying the key locally, assign the appropriate permissions. Attempting to authenticate with the key: Unfortunately, SSH prompts for a passphrase. This usually means the private key is encrypted. Fortunately, we already recovered Gilfoyle's account password from the database. It's common for users to protect their private keys with the same password they use for authentication. Entering the recovered password: successfully unlocks the key. Success! We now have our first shell on the actual host, escaping the Docker container and obtaining initial access as the user gilfoyle. User Flag Reading the user flag: Privilege Escalation After obtaining a shell as gilfoyle, the next step was to perform the usual post-exploitation enumeration to look for anything that might lead to privilege escalation. Listing all files in the home directory reveals an interesting hidden file. Among the hidden files, .vault-token immediately stands out. Reading its contents reveals what appears to be an authentication token. At this point we don't yet know what this token is for, but the filename strongly suggests it belongs to HashiCorp Vault. To confirm this, check whether the Vault client is installed on the system. Indeed, the Vault CLI is installed. Running it without any arguments displays the available commands. What is HashiCorp Vault? HashiCorp Vault is a secrets management platform commonly used in enterprise environments to securely store sensitive information such as: Database passwords API keys SSH credentials Certificates Cloud secrets Instead of storing privileged credentials directly on servers, administrators retrieve them from Vault whenever they're needed. Finding a Vault token on a compromised machine is therefore extremely interesting because the token may grant access to secrets or privileged authentication mechanisms. Authenticating to Vault Since we already have a token, there's no need for a username or password. Simply run: Paste the contents of .vault-token when prompted. This output is extremely significant. The authenticated token belongs to the root policy, meaning it has unrestricted access to Vault. Now the goal becomes determining how this Vault server is being used inside the environment. Discovering the SSH Role One of the available Vault commands is: Vault supports generating One-Time Passwords (OTP) for SSH authentication. Initially, it's tempting to guess a role name manually. For example: However, this fails. Trying random role names produces the same result. Instead of guessing, let Vault determine the correct role automatically. Simply omit both the role and mode. This time the output is much more interesting. This tells us two important things: The configured Vault SSH role is named root_otp. Vault has generated a one-time SSH password for the root account. How Vault SSH OTP Works This feature allows administrators to log in without knowing the actual root password. The authentication flow is: Authenticate to Vault using a valid token. Request a one-time password for a specific SSH role. Vault generates a temporary password. Use that password during SSH authentication. Once used, the password becomes invalid. Since our token has the root policy, Vault happily generates a valid OTP for us. Root Access The SSH client now prompts for a password. Enter the OTP generated by Vault: Authentication succeeds immediately. Success! We now have a root shell on the target. Root Flag The machine is now fully compromised. Final Thoughts Craft is an excellent machine that demonstrates how seemingly small security oversights can chain together into a complete system compromise. It begins with a public source code repository exposing sensitive information, escalates through an unsafe use of Python's eval() for remote code execution, and highlights the risks of hardcoded credentials inside application configuration files. The journey continues through Docker container enumeration, database credential reuse, private Git repository discovery, and finally ends with the abuse of a forgotten HashiCorp Vault token to obtain root access via SSH OTP authentication. Some of the key takeaways from this machine are: Never expose sensitive information such as credentials or API tokens in public Git repositories or commit history. Avoid using dangerous functions like eval() on user-controlled input, as they almost always lead to remote code execution. Containers should never be treated as a security boundary—compromising an application inside a container can often lead to further compromise of the host. Secrets management solutions such as HashiCorp Vault are only effective when access tokens are properly protected. A leaked Vault token with excessive