jenkins CVE-2024-23897浅析

Jenkins

先简单介绍一下jenkins:开源ci/cd(应用程序开发自动化交付)软件,用于自动化各种任务,包括创建、测试、部署软件

复现分析

简单来说由这几点一起造成的这个漏洞:

  1. 用户使用jenkins-cli.jar时,命令行解析时传到服务器进行解析而非jenkins-cli.jar
  2. jenkins服务端解析命令行使用了第三方库args4j,其中实现了将@开头的参数解析为文件名(Linux常见功能)

分析一下

拿到jenkins-cli.jar,定位到hudson.cli.CLI#_main方法

-s参数设置jenkins的服务地址,-http指定了通信方式(其他还有ssh、web socket),其余的参数会留到args数组中。处理部分如下,首先创建cli连接工厂类,并根据参数对工厂类进行赋值操作,根据通信方式(ssh再上面没截取)执行不同返回不同的执行类实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
...
CLIConnectionFactory factory = new CLIConnectionFactory();
String userInfo = (new URL(url)).getUserInfo();
if (userInfo != null) {
    factory = factory.basicAuth(userInfo);
} else if (auth != null) {
    factory = factory.basicAuth(auth.startsWith("@") ? readAuthFromFile(auth).trim() : auth);
} else if (bearer != null) {
    factory = factory.bearerAuth(bearer.startsWith("@") ? readAuthFromFile(bearer).trim() : bearer);
}

if (mode == CLI.Mode.HTTP) {
    return plainHttpConnection(url, args, factory);
} else if (mode == CLI.Mode.WEB_SOCKET) {
    return webSocketConnection(url, args, factory);
} else {
    throw new AssertionError();
}
...

往下跟了一下,没什么意义,通信处理,然后将参数中的命令发送出去

看一下处理端,根据commit定位到hudson.cli.CLICommand#getCmdLineParser,简单看一下,修复前直接交给CmdLineParser处理

image-20240410202404204

debug一下,以help命令为例,直接调用了CLICommand的子类HelpCommand进行处理

image-20240411144833595

CmdLineParser方法进行了简单的初始化,包括命令参数个数等,我们直接跟进命令处理位置,调用parseArgument来处理命令

image-20240411145415331

跟进parseArgument,可以看到这边经过expandAtFiles处理后直接拿到了文件内容,跟进expandAtFiles看一下

image-20240411145713195

这样一切就都清楚了,直接看for循环中内容,遍历命令中参数,然后进行判断,如果参数以@开头,就尝试去拿@后面的文件,并且将文件内容全部放入result中,并返回,这边就是漏洞根源位置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private String[] expandAtFiles(String[] args) throws CmdLineException {
    List<String> result = new ArrayList();
    String[] arr$ = args;
    int len$ = args.length;

    for(int i$ = 0; i$ < len$; ++i$) {
        String arg = arr$[i$];
        if (arg.startsWith("@")) {
            File file = new File(arg.substring(1));
            if (!file.exists()) {
                throw new CmdLineException(this, Messages.NO_SUCH_FILE, new String[]{file.getPath()});
            }

            try {
                result.addAll(readAllLines(file));
            } catch (IOException var9) {
                throw new CmdLineException(this, "Failed to parse " + file, var9);
            }
        } else {
            result.add(arg);
        }
    }

    return (String[])result.toArray(new String[result.size()]);
}

回显位置如下,以使用help命令为例,参数个数限制为1,所以这边抛出错误并显示读取文件拿到的第一行

image-20240411152636944

读文件技巧

help报错

如果管理员关闭”匿名用户可读“功能,那么我们只能使用helpwho-am-i两个命令,并且,使用help命令(触发报错)只能读取到文件的前两行,因为文件中的每一个换行都被解析为第二个参数

image-20240129131519263

help命令会先检查命令的参数个数是否正确,然后在检查当前权限,因此如果在检查参数时令其报错,就可以利用报错读取文件信息

image-20240129131904922

其他

如果管理员开启了“匿名用户可读”,那么大部分命令都可以被调用来进行文件读取

reload-job读取文件全部内容

image-20240129132244772

jenkins的一些敏感文件利用

jenkins默认数据根目录/var/jenkins_home(一般会被修改)

如果拿到了根目录,那么可以尝试读这些:

  • /var/jenkins_home/users/*/config.xml
  • /var/jenkins_home/secret.key
  • /var/jenkins_home/secrets/master.key
  • /var/jenkins_home/secrets/org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices.mac

其他

当前进程的环境变量:/proc/self/environ

cmd启动命令行:/proc/self/cmdline

更详细的敏感信息利用这边就不赘述了,直接p神文章:https://www.leavesongs.com/PENETRATION/jenkins-cve-2024-23897.html

0%