That MacOS (formerly OS X) has BSD roots is a well-known fact, with its predecessor NeXTSTEP and its XNU kernel derived from 4.3BSD. Subsequent releases of OS X/MacOS then proceeded to happily copy more bits from 4.4BSD, FreeBSD and other BSDs.
In that respect the thing that makes MacOS unique compared to other BSDs is its user interface, which is what the open source ravynOS seeks to address. By taking FreeBSD as its core, and crafting a MacOS-like UI on top, it intends to provide the MacOS UI experience without locking the user into the Apple ecosystem.
Although FreeBSD already has the ability to use the same desktop environments as Linux, there are quite a few people who prefer the Apple UX. As noted in the project FAQ, one of the goals is also to become compatible with MacOS applications, while retaining support for FreeBSD applications and Linux via the FreeBSD binary compatibility layer.
If this sounds good to you, then it should be noted that ravynOS is still in pre-release, with the recently released ravynOS βHyperpop Hyenaβ 0.6.1 available for download and your perusal. System requirements include UEFI boot, 4+ GB of RAM, x86_x64 CPU and either Intel or AMD graphics. Hardware driver support for the most part is that of current FreeBSD 14.x, which is generally pretty decent on x86 platforms, but your mileage may vary. For testing systems and VMs have a look at the supported device list, and developers are welcome to check out the GitHub page for the source.
Considering our own recent coverage of using FreeBSD as a desktop system, ravynOS provides an interesting counterpoint to simply copying over the desktop experience of Linux, and instead cozying up to its cousin MacOS. If this also means being able to run all MacOS games and applications, it could really propel FreeBSD into the desktop space from an unexpected corner.
In this writeup, we will explore the βArtificialβ machine from Hack The Box, categorized as an easy difficulty challenge. This walkthrough will cover the reconnaissance, exploitation, and privilege escalation steps required to capture the flag.
Objective:
The goal of this walkthrough is to complete the βArtificialβ machine from Hack The Box by achieving the following objectives:
User Flag:
The user flag is obtained by scanning the βArtificialβ machine, identifying a web server on port 80, and creating an account to access its dashboard. The dashboard allows uploading .h5 files, so a malicious .h5 file is crafted to trigger a reverse shell. After setting up a Docker environment and uploading the file, a shell is gained as the app user. A SQLite database (users.db) is found, and cracking its password hashes reveals credentials for the user gael. Logging in via SSH as gael allows retrieval of the user flag from user.txt.
Root Flag:
To escalate to root, a scan reveals port 9898 running Backrest. Forwarding this port and enumerating the service uncovers backup files and a config.json with a bcrypt-hashed password. Decoding a base64 value yields a plaintext password, granting access to a Backrest dashboard. Exploiting the RESTIC_PASSWORD_COMMAND feature in the dashboard triggers a root shell, allowing the root flag to be read from root.txt.
Enumerating the Artificial Machine
Reconnaissance:
Nmap Scan:
Begin with a network scan to identify open ports and running services on the target machine.
Port 22 (SSH): Runs OpenSSH 8.2p1 on Ubuntu 4ubuntu0.13 (protocol 2.0), providing secure remote access with RSA, ECDSA, and ED25519 host keys.
Port 80 (HTTP): Hosts an nginx 1.18.0 web server on Ubuntu, redirecting to http://artificial.htb/, indicating a web application to explore.
Web Application Exploration on an Artificial Machine:
At this stage, the target appears to host a standard website with no immediately visible anomalies or interactive elements.
I actively created a new user account to interact with the application and test its features.
Using the credentials created earlier, I logged into the application.
Finally, access to the dashboard was successfully obtained as shown above.
At this point, the application requires a file to be uploaded.
Two links appear interesting to explore: requirements and Dockerfile.
The main dashboard endpoint returned a response with status 200 OK.
Further analysis of the response revealed that the upload functionality only accepts files in the .h5 format.
Analyzing Application Dependencies
As the dashboard response showed nothing significant, I focused on analyzing the previously downloaded file.
The requirements.txt specifies tensorflow-cpu==2.13.1, indicating that the applicationβs dependencies rely on this TensorFlow version. Attempting to install it outside of a TensorFlow-compatible environment will result in errors.
The Dockerfile creates a Python 3.8 slim environment, sets the working directory to /code, and installs curl. It then downloads the TensorFlow CPU wheel (tensorflow_cpu-2.13.1) and installs it via pip. Finally, it sets the container to start with /bin/bash. This ensures that the environment has TensorFlow pre-installed, which is required to run the application or handle .h5 files.
Setting Up the Docker Environment
While trying to install the requirements, I faced an error stating they need a TensorFlow environment.
I could install TensorFlow locally, but its large file size causes issues. Even after freeing up disk space, the installation fails due to insufficient storage.
Crafting the Exploit
The script constructs and saves a Keras model incorporating a malicious Lambda layer: upon loading the model or executing the layer, it triggers an os.system command to establish a named pipe and launch a reverse shell to 10.10.14.105:9007. Essentially, the .h5 file serves as an RCE payloadβavoid loading it on any trusted system; examine it solely in an isolated, disposable environment (or through static inspection) and handle it as potentially harmful.
Proceed within an isolated Python virtual environment (venv) to analyze the file; perform static inspection only and avoid importing or executing the model.
Installing TensorFlow remains necessary.
Following careful thought, I selected a Docker environment to execute the setup, seeking to bypass local dependency or storage problems.
I built and tagged the Docker image successfully.
At this stage, the Docker environment is running without any issues.
The command updates the package lists and installs the OpenBSD version of Netcat (netcat-openbsd) to enable network connections for testing or reverse shells.
netcat-openbsd is a lightweight, versatile networking utility commonly used in HTB and pentests to create raw TCP/UDP connections, transfer files, and receive reverse shells. The OpenBSD build omits the risky -e/βexec option present in some older variants, but it still pipes stdin/stdout over sockets, so only use it in authorised, isolated lab environments (examples: nc -l -p PORT to listen, nc HOST PORT to connect) .
Ultimately, I executed the script successfully, achieving the expected outcomeβa reverse shell to 10.10.14.105:9007βas demonstrated above.
Executing the Reverse Shell
Consequently, I generated an .h5 model file.
I launched a netcat listener on 10.10.14.105:9007 to receive the incoming reverse shell.
I uploaded the exploit.h5 file to the applicationβs file upload endpoint to initiate model processing.
Successfully uploading the file and clicking the View Predictions button activates the embedded payload.
Page displayed a loading state, indicating that the payload is likely executing.
Gaining Initial Access
The shell connection successfully linked back to my machine.
Upgrading the reverse shell to a fully interactive session simplified command execution.
Gained an interactive shell as the application user app.
Found a Python file named app.py in the application directory.
The app.py section reveals a hard-coded Flask secret key, Sup3rS3cr3tKey4rtIfici4L, sets up SQLAlchemy to utilize a local SQLite database at users.db, and designates the models directory for uploads. The fixed key allows session manipulation or cookie crafting, the SQLite file serves as a simple target for obtaining credentials or tokens, and the specified upload path indicates where malicious model files are kept and can be executedβcollectively offering substantial opportunities for post-exploitation and privilege escalation.
Located a users.db file that appears to be the applicationβs SQLite database; it likely contains user records, password hashes, and session data, making it a prime target for credential extraction and privilege escalation.
Downloaded users.db to our own machine using netcat for offline analysis.
Verification confirms users.db is a SQLite 3.x database.
Extracting Credentials
Extracted password hashes from the users.db (SQLite3) for offline cracking and analysis.
Apart from the test account, I extracted password hashes from the remaining user accounts in the SQLite database for offline cracking and analysis.
Configured hashcat to the appropriate hash mode for the extracted hash type, then launched the cracking job against the dump.
Cracking the hashes revealed two plaintext passwords, but the absence of corresponding usernames in the dataset blocked immediate account takeover.
An easier verification is to use nc β we accessed the user gael with the password mattp005numbertwo.
Authenticated to the target via SSH as user gael using the recovered password, yielding an interactive shell.
The user flag was read by running cat user.txt.
Escalate to Root Privileges Access on Artificial machine
Privilege Escalation:
Artificial host lacks a sudo binary, preventing sudo-based privilege escalation.
Port scan revealed 9898/tcp open β likely a custom service or web interface; enumerate it further with banner grabs, curl, or netcat.
Established a port-forward from the targetβs port 9898 to a local port to interact with the service for further enumeration.
Exploring the Backrest Service
Exploring the forwarded port 9898 revealed Backrest version 1.7.2 as the running service.
Attempting to authenticate to Backrest with gaelβs credentials failed.
Enumerated the Backrest service and discovered several files within its accessible directories.
Enumeration of the Backrest instance revealed several accessible directories, each containing files that warrant further inspection for credentials, configuration data, or backup artefacts.
The install.sh file contains configuration settings that appear standard at first glance, with no immediately suspicious entries.
However, scrolling further reveals sections resembling backup configuration, suggesting the script may handle sensitive data or database dumps.
Analyzing Backup Configurations
Focused on locating backup files referenced in the configuration for potentially sensitive data.
Discovering multiple backup files revealed a substantial amount of stored data potentially containing sensitive information.
Copying the backup file to /tmp enabled local inspection and extraction.
Successfully copying the backup file made it available in /tmp for analysis.
Unzipping the backup file in /tmp allowed access to its contents for further inspection.
Several files contained the keyword βpassword,β but the config.json file appeared unusual or suspicious upon inspection.
Discovered a potential username and a bcrypt-hashed password. Because bcrypt uses salting and is intentionally slow, offline cracking requires a tool like hashcat or John that supports bcrypt, paired with wordlists/rules and significant computational resources; alternatively, explore safe credential reuse checks on low-risk services or conduct password spraying in a controlled lab setting.
Decoding a base64-encoded value uncovered the underlying data.
Recovered the plaintext password after decoding the base64-encoded value.
Credentials recovered earlier were submitted to the service to attempt authentication.
A different dashboard was successfully accessed using the recovered credentials.
To create a new Restic repository, you first need to initialise a storage location where all encrypted backups will be kept
While adding the Restic repository via environment variables, I noticed that RESTIC_PASSWORD is required. I also discovered an interesting variable, RESTIC_PASSWORD_COMMAND, which can execute a command to retrieve the password.
What RESTIC_PASSWORD_COMMAND?
RESTIC_PASSWORD_COMMAND tells restic to run the given command and use its stdout as the repository password. Itβs convenient for integrating with secret stores or helper scripts, but itβs dangerous if an attacker can control that environment variable or the command it points to.
The shell can be triggered by selecting βTest Configurationβ.
The root flag can be accessed by running cat root.txt.