路径遍历漏洞 (Path Traversal) 详细分析
环境部署参考官方文档:https://docs.astrbot.app/deploy/astrbot/docker.html
漏洞基本信息
-
漏洞类型: 路径遍历 (Path Traversal/CWE-22)
-
安全等级: 高危
-
是否需要认证: 否
-
路由地址:
/api/chat/get_file -
HTTP方法: GET
漏洞点分析
1. 漏洞核心代码位置
漏洞存在于 astrbot/dashboard/routes/chat.py 文件的第58-73行 get_file() 方法中:
async def get_file(self):
filename = request.args.get("filename") # 从URL参数获取文件名
if not filename:
return Response().error("Missing key: filename").__dict__
try:
# 漏洞点:直接拼接用户输入的文件名到基础路径
with open(os.path.join(self.imgs_dir, filename), "rb") as f:
if filename.endswith(".wav"):
return QuartResponse(f.read(), mimetype="audio/wav")
elif filename.split(".")[-1] in self.supported_imgs:
return QuartResponse(f.read(), mimetype="image/jpeg")
else:
return QuartResponse(f.read())
except FileNotFoundError:
return Response().error("File not found").__dict__
2. 基础路径解析
self.imgs_dir 在类初始化时定义于第37行:
self.imgs_dir = os.path.join(get_astrbot_data_path(), "webchat", "imgs")
get_astrbot_data_path() 函数在 astrbot/core/utils/astrbot_path.py 中定义:
def get_astrbot_data_path() -> str:
"""获取Astrbot数据目录路径"""
return os.path.realpath(os.path.join(get_astrbot_root(), "data"))
def get_astrbot_root() -> str:
"""获取Astrbot根目录路径"""
if path := os.environ.get("ASTRBOT_ROOT"):
return os.path.realpath(path)
else:
return os.path.realpath(os.getcwd())
因此,基础路径通常是程序运行目录下的 data/webchat/imgs 目录。
而请求的路由是在路径白名单里的
# astrbot/dashboard/server.py
allowed_endpoints = ["/api/auth/login", "/api/chat/get_file", "/api/file"]
漏洞成因
-
缺乏输入验证: 代码直接使用
request.args.get("filename")获取用户输入,未对文件名进行任何安全检查 -
路径拼接缺陷: 使用
os.path.join(self.imgs_dir, filename)直接拼接用户输入,未防止目录遍历字符 -
无访问控制: 该接口未要求身份验证,任何人都可以访问
漏洞利用原理
当用户输入包含 ../ 字符串时,操作系统会将其解释为”上级目录”,从而可以跳出预设的 imgs_dir 目录范围。
例如:
-
基础路径:
/app/data/webchat/imgs/ -
用户输入:
../../../main.py -
最终路径:
/app/data/webchat/imgs/../../../main.py→/app/main.py
正确的利用Payload
根据实际测试,正确的利用需要足够的 ../ 来回到项目根目录:
GET /api/chat/get_file?filename=../../../main.py
或者为了确保能够访问到系统根目录的文件:
GET /api/chat/get_file?filename=../../../../../etc/passwd
思路扩展
任意文件读如何扩大危害呢,其实可以考虑一下读取cmd_data.json 这里边是存密码hash过后的值的,而且通过抓包可以很清楚知道,登陆的时候密码直接就是密文,所以我们抓取到密文之后直接就可以登录,都不需要进行任何的解密动作
修复建议
-
输入验证: 对用户输入的文件名进行严格验证,禁止包含路径遍历字符
-
路径规范化: 使用
os.path.realpath()获取绝对路径后,检查是否仍在允许的目录范围内 -
白名单机制: 仅允许访问预定义的文件扩展名和文件名
-
访问控制: 对此接口添加身份验证要求
感觉这个漏洞的成因和CVE-2025-26319特别像,都是有白名单的接口,然后里面未校验。