跳转到帖子

Jenkins CLI 任意文件读取事件复盘:从公开暴露到内网权限边界验证

精选回复

发布于

近期公开披露的 Jenkins CLI 参数解析漏洞(CVE-2024-23897)具有较强的代表性:它并不只是一个“读取文件”的单点漏洞,在 Jenkins 控制器暴露于互联网、构建节点权限过宽、凭据未分层管理的环境中,可能演变为代码仓库、镜像仓库和内网构建节点的连续风险。本文以授权渗透测试和防守验证为前提,重点讨论如何结合威胁情报定位暴露面、验证漏洞是否可利用、还原可能的内网攻击链,并通过日志确认是否出现过真实访问。

一、新闻背景与影响范围

Jenkins 官方安全公告披露,部分版本的 CLI 参数解析逻辑存在基于 @文件路径参数的任意文件读取问题。受影响范围通常需要结合 Jenkins 版本、CLI 暴露方式、匿名用户权限以及反向代理配置综合判断,不能仅凭版本号直接认定“必然可利用”。

在实际环境中,风险通常沿着以下链路扩大:

  • 互联网扫描发现 Jenkins 登录页、CLI 接口或反向代理入口;
  • 通过 CLI 参数解析缺陷读取控制器上的配置、构建脚本、工作区文件或进程环境信息;
  • 从构建脚本、日志、临时文件或节点配置中发现 Git、制品库、云平台或 SSH 凭据线索;
  • 使用权限过大的服务账号访问内部 Git、镜像仓库或构建节点;
  • 借助构建节点的本地管理员权限、云实例角色或共享目录,继续扩大横向访问范围。

需要注意的是,Jenkins 的 credentials.xml 中凭据通常不是直接明文保存,单独读取该文件不等于立即获得所有密码。但控制器上的密钥材料、构建日志、脚本参数、工作区文件和环境变量仍可能造成严重泄露,因此不能只围绕“是否读到了 credentials.xml”判断影响。

二、问题定位:先确认暴露面,再确认版本和配置

授权测试时,建议先从资产和网络层确认入口,不要直接发送会读取系统敏感文件的利用请求。外部探测可以先观察 HTTP 头、登录页和 CLI 相关路径:

# 仅用于确认授权目标是否暴露 Jenkins 服务,不执行漏洞利用
TARGET='
curl -sk -D /tmp/jenkins.headers -o /dev/null "$TARGET/login"
curl -sk -D - -o /dev/null "$TARGET/cli/"

# 查看响应中是否存在 Jenkins 版本线索、反向代理和 CLI 相关头
sed -n '1,40p' /tmp/jenkins.headers
grep -Eai 'jenkins|x-jenkins|server:|location:' /tmp/jenkins.headers

生产环境经常通过 Nginx、Apache、云负载均衡或 WAF 转发请求。此时外部响应不一定能准确暴露 Jenkins 版本,应在控制器主机或配置管理系统中确认版本:

# Debian/Ubuntu 常见检查方式
apt-cache policy jenkins 2>/dev/null | sed -n '1,12p'
dpkg-query -W -f='${Version}\n' jenkins 2>/dev/null

# RPM 系列系统
rpm -q --qf '%{NAME}-%{VERSION}-%{RELEASE}\n' jenkins 2>/dev/null

# systemd 服务参数和实际启动路径
systemctl cat jenkins
systemctl show jenkins -p Environment -p ExecStart

随后需要确认以下配置项:

  • Jenkins 是否直接监听公网地址,或仅允许反向代理访问;
  • 匿名用户是否拥有 Overall/Read、Job/Read 等基础权限;
  • CLI 是否通过 HTTP、WebSocket 或 SSH 暴露;
  • 反向代理是否将 /cli/、WebSocket 升级请求和 Jenkins API 全部转发到控制器;
  • 控制器与构建节点之间是否存在过宽的网络和文件访问权限。

三、技术分析:为什么低权限 Jenkins 入口会影响内网边界

Jenkins 控制器通常拥有三类高价值信息:一是流水线定义和构建脚本,二是凭据引用及节点连接配置,三是构建过程中生成的工作文件与日志。即使攻击者无法直接执行构建任务,文件读取能力也可能帮助其判断控制器目录结构、插件版本、节点名称和外部系统地址。

内网风险主要取决于权限边界,而不是 Jenkins 是否“只用于自动化构建”。例如:

  • Jenkins 服务账号被加入本地管理员组,或可以无密码执行 Docker、Podman、kubectl 等高风险命令;
  • 构建节点允许访问生产网段、数据库网段或云元数据服务;
  • 流水线使用长期有效的 SSH 私钥、云访问密钥或镜像仓库管理员令牌;
  • 多个项目共用同一个高权限凭据,导致低信任项目可以调用高信任资源;
  • 构建工作区和临时目录未清理,历史构建产物中保留密码、令牌或内部地址。

因此,漏洞验证应当把“能否读取一个文件”拆解成三个问题:入口是否可达、当前身份能否触发相关解析逻辑、读取能力是否能够跨越控制器与构建节点的权限边界。第三项决定实际影响,不能被简单的 CVSS 分数替代。

四、授权验证步骤:以无害方式确认风险

1. 建立测试边界

在测试单上明确目标域名、允许的源地址、测试时间和禁止读取的文件清单。生产环境中禁止直接读取 /etc/shadow、私钥、云实例元数据和业务数据库配置。优先使用测试控制器或由运维人员预置的无敏感标记文件,例如:

# 由系统管理员预先创建,内容仅为 TEST-JENKINS-CVE-VALIDATION
sudo install -o jenkins -g jenkins -m 0640 /dev/null /var/lib/jenkins/validation-marker.txt
sudo sh -c 'printf "%s\n" TEST-JENKINS-CVE-VALIDATION > /var/lib/jenkins/validation-marker.txt'

# 测试结束后删除标记文件
sudo rm -f /var/lib/jenkins/validation-marker.txt

如果必须判断文件读取风险,应让管理员在测试窗口内观察该标记文件的访问审计记录,而不是将真实敏感文件内容返回给测试人员。

2. 检查控制器本地权限边界

# 服务身份、监听端口和网络路由
ps -o user,group,pid,cmd -C java | grep -i jenkins
ss -lntp | grep -E ':8080|:8443|:50000'
ip route

# 仅查看权限和目录结构,不输出文件内容
namei -l /var/lib/jenkins
find /var/lib/jenkins -xdev -type f \( -name '*.xml' -o -name '*.key' -o -name '*.pem' \) -printf '%M %u:%g %p\n' 2>/dev/null | head -100

# 检查 Jenkins 服务账号是否存在高风险 sudo 权限
sudo -l -U jenkins 2>/dev/null

重点关注 jenkins 用户是否能够读取其他服务的配置、写入系统级脚本目录、调用容器运行时或执行免密提权命令。不要在共享生产主机上直接运行会修改文件、启动容器或访问外部系统的测试命令。

3. 检查构建链路中的凭据使用

从流水线配置和构建记录中确认凭据是否满足最小权限。可以先导出任务列表和凭据引用关系,由管理员在脱敏副本上分析:

# Jenkins 控制器本地:只枚举任务和脚本中的凭据引用,不打印凭据值
grep -RInE --exclude='*.log' --exclude='*.zip' \
  'credentialsId|withCredentials|sshagent|docker login|kubectl config' \
  /var/lib/jenkins/jobs /var/lib/jenkins/workspace 2>/dev/null \
  | sed -E 's/(password|token|secret|privateKey)[[:space:]]*[:=][^,}]*/\1=<REDACTED>/Ig' \
  | head -200

如果发现同一凭据被多个项目、多个环境或多个节点共享,应将其视为权限边界问题处理,而不是仅作为 Jenkins 漏洞问题处理。

五、日志留痕与排查方法

漏洞是否被利用,不能只看 Jenkins 登录日志。应将反向代理、Jenkins 应用日志、主机审计和下游系统日志按时间线关联。

  • 反向代理日志:检索 /cli/、CLI 相关 POST 请求、异常的 WebSocket 升级、非正常 User-Agent,以及短时间内来自同一地址的多次失败请求。
  • Jenkins 日志:关注 CLI 命令解析异常、参数解析错误、匿名请求、异常的节点连接和插件报错。
  • 主机审计:检查 Jenkins Java 进程是否访问过不属于其正常工作流的配置、密钥和系统文件。
  • 下游日志:检查 Git、镜像仓库、跳板机、SSH、云审计和 Kubernetes API 中是否出现新的来源地址或异常服务账号调用。
# Nginx 常见日志格式下的初步筛选,按实际日志路径调整
zgrep -hEi '(/cli/|websocket|connect-node|@/|@%2f|jenkins-cli)' \
  /var/log/nginx/access.log* 2>/dev/null | tail -200

# Jenkins 服务日志:关注 CLI、匿名访问和参数解析异常
journalctl -u jenkins --since '2024-01-01' --until '2024-12-31' --no-pager \
  | grep -Ei 'cli|anonymous|args4j|parse|websocket|access denied|permission' \
  | tail -300

# Linux auditd:在维护窗口内对测试标记文件进行临时审计
sudo auditctl -w /var/lib/jenkins/validation-marker.txt -p r -k jenkins-validation
sudo ausearch -k jenkins-validation -i
sudo auditctl -W /var/lib/jenkins/validation-marker.txt -p r -k jenkins-validation

日志中出现 403 或异常参数并不等于攻击成功;相反,未出现 Jenkins 应用层错误也不能排除攻击,因为请求可能在反向代理、WebSocket 或插件层被处理。建议统一使用 UTC 时间、客户端真实 IP、请求 ID 和 Jenkins 节点名称进行关联。

六、修复与加固建议

  • 优先升级:按照 Jenkins 官方安全公告升级到已修复版本,并同步升级相关 CLI、核心插件和反向代理组件。升级前在隔离环境验证流水线、凭据绑定和节点连接。
  • 缩小暴露面:控制器不应直接暴露公网;通过 VPN、零信任访问代理或管理网段白名单限制访问。没有使用 CLI 的环境应在反向代理层禁止 /cli/ 和不必要的 WebSocket 路径。
  • 关闭匿名能力:启用矩阵权限或基于角色的授权,匿名用户不应拥有不必要的 Overall/Read、Job/Read 或节点查看权限。不要把“隐藏登录页”当作访问控制。
  • 轮换凭据:如果存在可疑访问,按控制器、Git、镜像仓库、云平台、SSH 和 Kubernetes 凭据分别轮换,并检查历史构建日志和工作区中的泄露副本。
  • 隔离构建节点:控制器、普通构建节点和生产发布节点分离网络与身份。构建节点禁止无必要访问数据库、生产网段和云元数据服务。
  • 限制服务账号:确认 Jenkins 服务账号不在本地管理员组,不能免密执行 Docker 或其他可获得宿主机权限的命令。对容器运行时、云角色和 Kubernetes ServiceAccount 单独做最小权限审计。
  • 保护日志:保留反向代理、Jenkins、主机审计和下游系统日志,至少覆盖漏洞公告后的回溯窗口,并将日志发送到不可由 Jenkins 服务账号修改的集中平台。

七、结论

这类 Jenkins 安全事件的价值不只在于提醒管理员升级版本,更在于暴露了自动化平台常见的权限边界问题:控制器通常连接多个内部系统,一处入口失守后,真正决定影响范围的是凭据分层、节点隔离、服务账号权限和日志完整性。

在授权验证中,推荐采用“资产暴露确认—版本与配置核对—无害标记文件验证—权限边界检查—日志回溯—凭据轮换”的顺序。这样既能证明风险是否存在,又能避免为了验证漏洞而读取真实敏感数据或破坏生产环境,最终把一条新闻情报转化为可执行的检测和修复任务。

网络流量与边界分析示意图
网络请求、日志与边界流量分析示意
这个事件建议不要只按“外网 Jenkins 是否可访问”来收口,真正要补的是两层:CLI 暴露面和 Jenkins 到内网的信任边界。 可以按下面几个点复盘: 1. 先确认是否落在 CVE-2024-23897 影响面 重点看 Jenkins 版本和 CLI 是否可被匿名访问。
curl -sI  | grep -i 'X-Jenkins'
curl -s  | grep -i 'Jenkins'
受影响版本大致是: - Jenkins weekly < 2.442 - Jenkins LTS < 2.426.3 如果还在这些版本,优先升级,不建议只靠 WAF 规则兜底。 2. 临时缓解:禁用 CLI 的 `@file` 语法 如果短时间不能升级,可以加 JVM 参数:
-Dhudson.cli.CLICommand.allowAtSyntax=false
例如 systemd 部署可以检查:
systemctl cat jenkins
grep -R "JAVA_OPTS\|JENKINS_JAVA_OPTIONS" /etc/default/jenkins /etc/sysconfig/jenkins 2>/dev/null
加完后重启:
systemctl restart jenkins
Docker 场景类似:
docker run \
  -e JAVA_OPTS="-Dhudson.cli.CLICommand.allowAtSyntax=false" \
  jenkins/jenkins:lts
3. 日志侧重点不要只看登录失败 这类读取不一定会留下明显的“登录失败”,建议查访问日志里的 CLI 相关路径:
grep -Ei '(/cli|/jnlpJars/jenkins-cli.jar|/cli/ws|remoting)' /var/log/nginx/access.log*
grep -Ei '(/cli|/jnlpJars/jenkins-cli.jar|/cli/ws|remoting)' /var/log/apache2/access.log*
Jenkins 自身日志也可以查异常参数、CLI 调用痕迹:
grep -Ei 'CLI|hudson.cli|args4j|No such file|Permission denied|Invalid argument' /var/log/jenkins/jenkins.log*
如果发现异常来源 IP 多次访问 `/jnlpJars/jenkins-cli.jar` 后紧接 `/cli`,基本就要当成已被探测甚至读取处理。 4. 重点排查敏感文件是否可能被读取 Jenkins 的风险点不只是 `/etc/passwd`,更严重的是这些:
$JENKINS_HOME/secrets/master.key
$JENKINS_HOME/secrets/hudson.util.Secret
$JENKINS_HOME/credentials.xml
$JENKINS_HOME/jobs/*/config.xml
$JENKINS_HOME/users/*/config.xml
$JENKINS_HOME/nodes/*/config.xml
如果这些存在被读取可能,建议直接进入凭据轮换流程: - Git token / deploy key - Docker registry 凭据 - K8s kubeconfig / service account token - 云厂商 AK/SK - SSH 私钥 - 制品库、数据库、堡垒机账号 不要只改 Jenkins 管理员密码,很多横向移动是从构建凭据开始的。 5. 内网边界验证建议从 Jenkins 节点视角做 Jenkins 往往能访问 GitLab、Nexus、Harbor、K8s API、数据库、测试环境,这些才是后续风险面。可以在 Jenkins controller 和 agent 上分别跑:
ip route
cat /etc/resolv.conf
env | grep -Ei 'proxy|token|key|secret|password'
find /var/jenkins_home -maxdepth 3 -type f \( -name "*.key" -o -name "*.pem" -o -name "*kube*" -o -name "credentials.xml" \)
再核对安全组 / ACL:Jenkins 是否真的需要访问这些网段。最小化建议: - Jenkins Web 入口只允许 VPN / 办公网段访问 - controller 不直接访问生产数据库 - agent 分环境隔离,生产构建和测试构建不要混用节点 - 禁止 Jenkins 到办公网、运维网的任意出站 - 对 K8s API、Harbor、Nexus 这类服务做源地址限制 6. Jenkins 安全配置也要顺手确认 看是否允许匿名读取或过宽权限:
Manage Jenkins
  - Security
    - Authorization
    - Agent protocols
    - CLI
建议: - 禁止匿名 Overall/Read - 关闭不需要的 agent 协议 - CLI 不对公网开放 - 管理入口放到反向代理后面做 IP 白名单或 SSO Nginx 可以先粗暴拦 CLI:
location ~ ^/(cli|cli/ws|jnlpJars/jenkins-cli.jar) {
    deny all;
}
但这只是临时措施,最终还是要升级和收敛权限。 7. 如果怀疑已被利用 我的处理顺序一般是: - 保全 Jenkins、Nginx、反代、系统日志 - 升级 Jenkins 到修复版本 - 禁用 CLI `@file` - 轮换 Jenkins 内所有高权限凭据 - 检查 Job 配置是否被改过 - 检查新增用户、API Token、SSH key - 检查 agent 节点是否有异常进程、定时任务、反连 常用排查:
find /var/jenkins_home -mtime -14 -type f | sort
grep -R "scriptApproval\|credentials\|token\|password\|ssh" /var/jenkins_home/jobs/*/config.xml 2>/dev/null
crontab -l
ps auxf
ss -antp
这个漏洞的复盘重点不是“读到了哪些文件”这么简单,而是要判断 Jenkins 是否已经从一个 CI 系统变成了内网跳板。只要凭据文件存在泄露可能,就应该按凭据泄露事件处理。

最近浏览 0

  • 没有会员查看此页面。