- Dificultad:
medium - Tiempo aprox.
9h(por pasarme 3 sin ver la clave privada deoperator) - Datos Iniciales:
10.129.7.166
Enumeración#
Hacemos un escaneo de puertos, encontramos lo siguiente:
| |
Además, como se nos ha indicado que Nginx sirve páginas distintas en función del dominio solicitado, probamos a enumerar subdominios.
| |
Y apuntamos y añadimos a /etc/hosts el subdominio flow.helix.htb.
HTTP#
Dominio Principal#
Al entrar, encontramos una página que ofrece tecnologías e infraestructuras para sistemas industriales.

De entre todos los botones, el único que hace algo diferente es START A PROJECT.
Si le damos, se muestra una ventana en la que tenemos que introducir un resumen del proyecto, y luego pulsar SEND BRIEF para enviarlo. Si analizamos qué pasa al hacer esto en BurpSuite, vemos que no se manda nada, ni una solicitud http, así que hay que no hay mucho más que podamos hacer.
Subdominio flow#
Al entrar a flow.helix.htb se nos redirige a /nifi. Al parecer, es un panel que permite crear un flujo de trabajo personalizado.

Si pulsamos el botón superior derecho (3 rayas), y pulsamos en About, veremos lo siguiente:

Apache NiFi is a framework to support highly scalable and flexible dataflows. It can be run on laptops up through clusters of enterprise class servers. Instead of dictating a particular dataflow or behavior it empowers you to design your own optimal dataflow tailored to your specific environment.
La versión en uso es la 1.21.0, y si la buscamos:
CVE-2023-34468: RCE via DB. CVSS: 8.8 HIGH
También encontramos un exploit para esta vulnerabilidad, aunque puede hacerse manualmente configurando drivers y nodos en la web.
| |
Y desde el listener:
| |
Privesc#
Aparecemos en el directorio /opt/nifi-1.21.0 como nifi. Si miramos qué otros usuarios hay en el sistema, veremos que el único otro con shell y que no es root es operator.
Contraseña cifrada#
El flujo de trabajo ya tenía configurada una base de datos, y para conectarse a esa base de datos haría falta una contraseña, o al menos para tener una cuenta en Nifi. Tal contraseña debería estar almacenada en algún sitio en los archivos de configuración.
Si buscamos en Google dónde puede estar, vemos que es posible que esté en conf/flow.json.gz (o flow.xml.gz).
Si descomprimimos flow.json.gz y echamos un vistazo, encontramos lo siguiente:
| |
Tenemos una contraseña cifrada de 100 caracteres. El problema es que no es ningún hash estándar, tenemos que encontrar cómo cifra Nifi las contraseñas.
Si miramos en conf/nifi.properties:
| |
Al parecer se usa el cifrado simétrico NIFI_PBKDF2_AES_GCM_256. Tras una búsqueda, veo que con la clave nifi.sensitive.props.key es posible descifrar la contraseña.
Un script (encrypt-config.sh) que proporciona el código fuente de Nifi permite descifrar y cifrar de nuevo a partir de los archivos bootstrap.conf y flow.xml.gz y de la contraseña. El script está disponible en el toolkit de la versión 1.21.0. El problema es que este script no muestra la contraseña descifrada, pero podemos usar un script que sigue el mismo proceso.
Primero hay que derivar la clave PBKDF2, para ello se usa HMAC-SHA-512, se aplican 160.000 iteraciones y se usa un salt hardcodeado de Nifi: NiFi Static Salt.
Luego, NiFi genera un nonce de 16 bytes y cifra la contraseña en texto plano usando AES-GCM con la clave derivada anterior y el nonce.
Y para acabar, se calcula un tag de integridad de otros 16 bytes. Finalmente, se concatena todo de la siguiente manera:
| |
Si usamos un script que deshaga esto:
| |
Y tenemos la contraseña R7qZ9L3xKM2W8pFYcA, aunque si la probamos con cualquier usuario (operator, root o nifi) veremos que no funciona. De todas formas, puede servirnos más adelante.
Puertos en local#
Si miramos puertos en escucha:
| |
El 38733 y el 39831 no devuelven nada, y el 8080 es Nifi hosteado en localhost también.
Los únicos que quedan y que no son del user java son el 8081 y el 4840.
Si probamos con el 4840, veremos que no devuelve nada, da timeout. Así que el único restante es el 8081.
| |
Al parecer es el panel de control de un reactor. Para acceder al puerto 8081 tenemos que hacer local port forwarding, y como no tenemos credenciales SSH ni podemos crear claves porque el directorio $HOME de nifi no existe, tenemos que usar socat.
Si subimos el binario de kali, al intentar ejecutarlo veremos que no están las librerías necesarias:
| |
Así que necesitamos un binario linkado estáticamente, como este.
Ahora sí lo ejecutamos y funciona:
| |
Ahora vamos a helix.htb:4446:
Vemos que se trata del panel de control (HMI, Interfaz Humano-Máquina) de un reactor de fusión nuclear. Además, pone “Maintenance window is NOT the same as MAINTENANCE mode. Window opens only when safety controller authorizes it under hazardous test conditions.”. No sabemos qué es el modo mantenimiento, pero es posible que nos sea de utilidad llegar a él. Se indica que tal modo privilegiado (en la ventana de abajo) se activa cuando la temperatura es mayor o igual a 295ºC o la presión es mayor o igual a 73 bar.
OPC UA#
Se nos indica la url opc.tcp://127.0.0.1:4840/helix/ para acceder al “OPC UA”, que no tengo ni idea de qué es, así que lo buscamos.
Tras una búsqueda, veo esto:
OPC UA es el estándar de comunicación más usado en la industria real (fábricas, centrales eléctricas, robótica). Es el idioma común que permite que sensores, PLCs y paneles de control hablen entre sí.
El prefijo opc.tcp indica que se usa un protocolo binario dedicado más rápido que HTTP para mandar datos. En esta máquina, el panel de control se conecta por detrás al servidor OPC UA para recibir los datos de control que muestra.
Además, los servidores OPC UA no tienen rutas como un servidor HTTP, sino que usan un árbol de nodos. Para visualizar e interactuar con dicho árbol, una herramienta gráfica útil es UaExpert, pero para descargarla hay que crear una cuenta en Unified Automation, y me da pereza, así que una alternativa es FreeOpcUa Client (opcua-client), gratis y de código abierto.
Dicho esto, port-forwardeamos el puerto 4840:
| |
Y ahora, descargamos y abrimos opcua-client:
| |
Y se abrirá lo siguiente:
Ahora ponemos opc.tcp://helix.htb:4841/helix/ y pulsamos en Connect, veremos lo siguiente:

Para activar el modo emergencia, podemos poner, como indicaba la página, TestOverride a true (en Root -> Objects -> Plant -> Control) para intentar conseguir el modo test.

Podríamos intentar modificar la temperatura o la presión, pero además de no tener permisos, los sensores reales sobrescriben este valor todo el rato, así que no duraría mucho. Ahora bien, si nos fijamos, vemos que el primer elemento de Reactor es CalibrationOffset, y en el panel del reactor parece indicarse que la temperatura actual se calcula como la suma de TemperatureRaw (no modificable) y CalibrationOffset (a 0 normalmente, modificable). Si cambiamos este segundo para que Temperature supere los 295ºC, podremos conseguir acceso a la ventana privilegiada.
Para ello, lo ponemos a, p.ej, 15ºC.

El problema es que aún habiendo cambiado esto, seguimos sin poder acceder a la ventana:

Tenemos que poner MODE en “MAINTENANCE`, como se nos indicaba en el texto de la página. Una vez puesto, tenemos lo siguiente:

Aunque ponga open, no aparece nada en la ventana.
Si nos fijamos, cuando la ventana estaba cerrada, ponía: “No maintenance window file present.”, así que posiblemente tengamos que colocar un archivo en algún lugar para que se muestre aquí, tal lugar podría ser /opt/helix. El problema de esto es que no tenemos permisos de escritura (ni de lectura) ahí, así que de algún modo tenemos que conseguirlos.
Buscando credenciales#
Pasadas varias (muchas) horas intentando descifrar de otras formas la contraseña conseguida antes (por si lo había hecho mal, porque no era muy fácil encontrar cómo cifraba/descifraba las contraseñas NiFi) y buscando posibles formas de conseguir la contraseña de operator manualmente, me doy por vencido y ejecuto LinPEAS.
Encuentra varias cosas:
| |
Pero ninguna relevante. Pasado todavía más rato, pruebo a buscar backups, logs, claves ssh, etc.:
| |
Y encontramos la clave SSH de operator. La copiamos y pegamos:
| |
Volviendo como operator#
Ahora que somos operator, primero echamos un vistazo en nuestro directorio /home. Ahí encontramos una imagen que explica el funcionamiento de OPC UA del reactor (que nosotros ya habíamos deducido).
También encontramos un pdf que nos pide contraseña, así que lo pasamos a john para ver si es posible crackearla.
| |
Y tenemos la contraseña operator1 para el pdf. Tras verlo, es simplemente un manual de cómo funciona todo el mecanismo del reactor y que explica cómo activar el modo mantenimiento. No dice nada nuevo.
Dentro del directorio /home de operator, también encontramos una serie de binarios para interactuar con OPC UA en .local/bin
| |
Pero parecen servir para lo mismo para lo que usábamos opcua-client.
Si ejecutamos sudo -l:
| |
helix-maint-console, la consola de mantenimiento de helix, y podemos ejecutarla como root, mediante la que posiblemente consigamos escalar privilegios. Ahora que sabemos esto, sí que tiene un propósito definido el activar el modo mantenimiento.
Vamos a opcua-client y activamos todo, Mode:MAINTENANCE, TestOverride:True, CalibrationOffset:14.
Ahora ejecutamos la consola de mantenimiento:
| |
Pero al parecer el shell morirá automáticamente en 100 segundos, así que rápidamente creamos un vector de escalada estable, como copiar el authorized_keys de operator al de root para poder iniciar sesión con la clave privada de operator:
| |
Ahora, desde nuestra máquina:
| |
Y, finalmente, tenemos root.



