KindEditor任意文件上传漏洞详解:原理、复现与加固 📅 发布时间:2026/9/18 10:33:14 👁 浏览次数: 你排查了半夜最后在kindeditor/php/目录里找到一个不明不白的 PHP 文件时间戳恰好是网站被篡改的那一天。这种场景这几年并不少见。KindEditor 作为早期国产富文本编辑器市面上有大量遗留站点还在使用而其中一部分老版本存在一个非常典型的文件上传漏洞攻击者不需要登录后台发一个构造好的上传请求就能把任意文件写到服务器上后续拿到执行权限也只是时间问题。我会把这个问题从头到尾讲透漏洞是怎么来的、本地怎么复现验证、线上如何排查、遇到之后怎么止血和加固。内容偏防御和自查方向适合运维、开发和安全测试人员作为参考。1. 这个漏洞为什么会烂大街KindEditor 的历史包袱1.1 编辑器本身的设计思路与普及度KindEditor 是很多早期 PHP 项目里常见的所见即所得编辑器部署方式很简单前端引入一个 JS后端放一套上传处理文件就能在表单里插入富文本内容。它的核心卖点就是“开箱即用”不用改太多业务代码。很多 CMS、OA 系统、B2B 网站甚至一些自动化建站工具的默认模板里都直接内置了这套编辑器。问题也出在“开箱即用”上。为了兼容不同站点、不同目录结构上传接口里保留了大量可配置参数其中一部分参数暴露在 URL 中由客户端直接传入。这个设计在早期或许不算致命但放到公网环境下就是隐患暴露面。更麻烦的是很多站长从建站到上线从来没有改过编辑器目录名也没有关注过后端上传文件的权限和鉴权逻辑导致攻击者在扫描的时候很容易命中路径。我见过不少老系统还在用 4.1.5 甚至更早的版本原因很一致系统跑了很多年换编辑器怕影响正文排版和前端样式就一直拖着没动。结果就是这类站点成了漏洞重灾区而且往往在被攻破很久之后运维才从木马文件上发现异常。1.2 漏洞影响版本与公开情报KindEditor 的上传漏洞公开信息很多影响范围比较明确。官方在后续版本里修复了上传模块的相关问题但很多存量系统根本没有跟进版本所以旧版本依然暴露在公网。下面列一下影响面和公开情报的梳理方式项目说明影响组件KindEditor 编辑器上传模块常见版本4.1.5 及更早版本存在已知风险涉及文件php/upload_json.php、asp/upload_json.asp、ashx/upload_json.ashx 等漏洞类型未授权文件上传、目录控制不严利用条件上传接口可被公网访问修复版本官方后续版本4.1.6 起对上传鉴权与类型校验做了调整这里需要说清楚一点实际项目中即使版本号显示较新也可能因为二次开发、旧文件残留、或者某条路径没有被替换干净导致风险继续存在。所以排查的时候不能只看版本号还要看实际能访问到的接口文件是否存在、是否还能接收上传请求。1.3 攻击者把它当“入口”的逻辑攻击者扫描公网时不会精确到只盯着 KindEditor。他们会用一套通用指纹库去匹配所有主流的旧组件KindEditor 只是其中一个目标。上传接口有几个特点让攻击者特别喜欢第一上传功能天然和文件写入绑定一旦绕过限制效果直接反映在服务器文件系统上。第二富文本编辑器的上传接口往往没有独立的后台权限校验甚至很多集成场景下根本没有接入业务系统的登录态。第三上传后的文件路径通常由接口返回攻击者很快就能拿到完整 URL直接访问验证。从攻击链条来看这个漏洞往往只是第一步。上传一个可执行脚本之后攻击者会通过这个脚本做内网探测、提权、横向移动、持久化。所以防御方判断危害程度时不能只看上传了多少个文件而是要看攻击者已经走了多远。这也是为什么我在实际响应中宁愿把这类事件先按最严重的情况去排查而不是看到只有一个文件就掉以轻心。2. 漏洞原理拆解上传接口哪里出了问题2.1 upload_json.php 的职责KindEditor 的后端上传接口逻辑并不复杂。前端上传图片时会由编辑器请求后端的上传处理文件后端接收文件后保存到指定目录然后返回 JSON 格式的访问地址。这个接口通常承担三类事情接收文件、重命名或原样保存、返回可访问的 URL。从功能角度看这是标准设计没什么特别。问题出在实现细节上。上传接口属于资源入口任何资源入口都必须回答三个问题谁在调用、允许传什么、保存到哪里。如果这三个问题有一个没答好后续就会出安全事件。旧版本的上传模块在有些场景下三个问题全都没答好这才是漏洞能成立的根本原因。2.2 三个核心缺陷用 PHP 版本的 upload_json.php 举例我简化一下核心逻辑你大概一眼就能看出来问题?php $php_path dirname(__FILE__) . DIRECTORY_SEPARATOR; $php_url dirname($_SERVER[PHP_SELF]) . DIRECTORY_SEPARATOR; $dir empty($_GET[dir]) ? image : $_GET[dir]; $save_path $php_path . $dir . DIRECTORY_SEPARATOR; $file_name $_FILES[imgFile][name]; $file_ext strtolower(pathinfo($file_name, PATHINFO_EXTENSION)); move_uploaded_file($_FILES[imgFile][tmp_name], $save_path . $file_name); echo json_encode(array(error 0, url $php_url . $dir . / . $file_name)); ?这是一段极端简化的演示伪代码只是为了说清问题所在真实代码要复杂一些但风险点很类似。第一个缺陷是缺少登录鉴权代码里没有任何 session 或 token 校验只要请求能到达 PHP 文件谁都能触发上传。第二个缺陷是目录参数完全由客户端控制dirimage、dirmedia、dirfile都行某些场景下还可能出现目录拼接穿越导致文件写到非预期目录。第三个缺陷是扩展名判断不严格虽然代码里取了扩展名但如果只做了简单判断或者没有把这个判断结果应用到最终保存白名单就形同虚设。单个缺陷单独拎出来危害都不一样大。缺少鉴权影响了访问入口目录控制影响了写入位置类型校验影响了写入内容。三者组合在一起就是未授权任意文件上传。2.3 为什么组合在一起非常危险单独把“缺少登录态”拿出来看很多旧系统的后台接口都有这个问题但如果只能上传图片到固定目录风险还是可控的。单独把“目录可控”拿出来看如果你能跨目录写文件但只能写图片后缀利用价值也没那么高。真正决定性的因素是文件类型限制失效。安全加固里常提一个概念叫“纵深防御”意思是每一层防护都要独立有效不能指望某一层拦住所有攻击。上传场景同理鉴权是一层、目录白名单是一层、扩展名白名单是一层、存储目录禁止执行脚本又是一层。KindEditor 旧版本的问题在于攻击者绕过了扩展名校验又有可能控制保存路径一旦服务器缺少禁止脚本执行的配置上传一个有脚本内容的文件到可访问目录就直接变成了代码执行入口。从防御视角理解这个漏洞重点不是记住某个 payload而是想明白一个逻辑文件上传接口必须假设攻击者会尝试上传所有可能的内容所以必须在入口处把所有不可接受的内容拒绝掉并且确保即使有漏网文件落盘也没办法被当作脚本执行。3. 本地复现验证从零还原一次漏洞触发过程3.1 准备本地实验环境如果你没在真实环境里见过这个漏洞我建议自己搭个本地环境验证一次。只有亲手触发过一次你对漏洞成因和排查特征的印象才会深。环境准备很简单本地装一套集成环境就行Windows 上常见的有 phpstudymacOS/Linux 上可以直接用 Docker 或者本地 PHP 内置服务器。把老版本 KindEditor 的 PHP 示例目录丢到 Web 根目录保证kindeditor/php/upload_json.php能通过 HTTP 访问到就行。有一点必须强调不要拿任何公网系统做测试也不要对不属于自己的站点做验证。所有实验都限制在本地环境这是安全工作的底线。如果手边没有老版本安装包也可以用我上面写的简化伪代码部署一个最小还原环境。重点是模拟出“未鉴权 目录可控 类型校验缺失”这三个条件而不是纠结是否 100% 还原原版代码。3.2 上传接口请求构造环境起来之后打开浏览器访问http://127.0.0.1/kindeditor/php/upload_json.php确认接口存在。正常访问可能会报错也可能返回一个 JSON这一步的目的是验证路由可达。接下来用 curl 构造一个上传请求curl -F imgFiletest.txt http://127.0.0.1/kindeditor/php/upload_json.php?dirimage如果接口不做登录校验也不限制扩展名返回里会出现一个 URL 字段比如{error:0,url:http://127.0.0.1/kindeditor/php/../image/test.txt}我建议你在本地多测试几个角度把dir改成file、media观察返回 URL 是否变化确认目录参数是否直接参与路径拼接。换不同的文件类型上传观察扩展名是否被过滤同名文件是否被覆盖。带一个不存在的dir值观察是否能在服务器上新建目录。这些测试结论会直接帮助你判断当前部署的版本有没有风险。3.3 从验证结果反推防御思路本地验证完成之后不要只停留在“能传文件”这个层面建议你顺手做几步反推想一想每种现象对应什么防御措施。如果确认接口没有登录校验那上线部署时要么改代码加鉴权要么在网络层限制接口访问来源。如果确认目录参数能影响保存路径那最好使用固定的安全目录拼接方式而不是直接把用户输入放进文件路径。如果确认扩展名没被严格限制那就必须做好白名单并且配合禁止执行脚本的目录权限。我自己的习惯是验证完之后保留当时的请求记录和返回结果作为后续应急排查时的特征参考。很多情况下你本地复现出来的请求路径和线上攻击日志里的路径是一致的。你把正常请求和恶意请求对比着看就能识别出线上的异常请求。4. 线上排查如何第一时间发现是否中招4.1 日志特征线上系统如果已经跑过一段时间怀疑被利用时第一个动作不是去改代码而是先确认有没有被攻击过、攻击有没有成功。排查入口就是 Web 访问日志。攻击者访问 KindEditor 上传接口时路径特征非常明显。你可以直接搜索/kindeditor/相关的访问记录尤其是upload_json.php、upload_json.asp、upload_json.ashx这类文件名。日志里需要重点观察几个特征日志特征说明请求方法该接口被正常使用时是 POST但攻击者也可能先用 GET 探测请求路径路径中出现 kindeditor 且指向上传处理文件请求参数查询串中带 dir 参数且值可能不是默认的 image响应大小上传成功响应 JSON 通常比较小客户端信息同一 IP 短时间多次访问不同路径可能是扫描行为只看日志还不够因为很多攻击者会先做扫描探测成功后才手工上传。所以日志里有访问记录不代表已经被利用但没有访问记录也不代表一定安全还要结合其他痕迹判断。4.2 文件系统排查日志给了线索之后就要去文件系统里找证据。上传接口一旦被利用服务器上会多出一些原本不存在的文件这些文件的位置通常在上传目录或者 Web 根目录附近的子目录里。现场排查时我会重点关注以下几个方面查看 Web 目录下最近 30 天内新增的可执行文件扩展名包括 php、php5、phtml、asp、aspx、jsp 等。检查上传目录下是否有脚本文件。正常编辑器上传目录里应该只有图片、文档、音视频出现脚本文件基本可以判定异常。查看文件创建时间是否与攻击日志时间吻合。用查杀工具做一次全目录扫描覆盖 Web 根目录和临时上传目录。写过 Webshell 的老手可能会把文件伪装成图片马、日志马或者藏得很深但对大多数利用 KindEditor 漏洞的攻击者来说路径都比较直白因为上传接口返回的 URL 会直接暴露文件位置攻击者没必要把文件藏到别处。4.3 权限与进程检查如果已经确认服务器上出现过恶意脚本就不只是删文件那么简单了。你需要假定攻击者可能已经获得了代码执行权限并按这个假设做一次全面排查。Linux 环境下优先看进程列表定位可疑的 CPU 高占用进程尤其是一些随机名字的 PHP 进程或者 crontab 里新出现的任务。Windows 环境下要看计划任务和启动项。还要确认是否存在外连行为。攻击者拿到代码执行权限后通常会尝试反连、下载工具或者做内网扫描。这些行为在服务器上会留下 network 连接记录防火墙日志里也能看到线索。排查到这一步事情的性质已经变成入侵应急响应建议按公司内部应急流程处理必要时请专业团队介入。5. 修复与加固从应急处理到长效机制5.1 立刻止血确认中招后的第一件事是止血不要让攻击者继续利用漏洞。如果业务允许先把上传接口暂时停掉或者直接删除无用的上传处理文件。如果删除会影响正常业务就临时改文件名并加上访问控制只允许后台管理 IP 访问。同时把已确认的恶意文件隔离删除。不要只删你找到的第一个文件要反复扫描因为攻击者可能在多个目录里都写了东西。删除之前保留一份备份方便后续溯源分析。如果服务器上有备份机制可以考虑从最近一次干净备份恢复但前提是确认备份时间点早于攻击时间并且备份中没有被植入后门。5.2 配置文件权限代码修复需要时间但服务器配置可以立刻改。核心思路是让上传目录失去脚本执行能力。Apache 环境下可以在上传目录配置里加上这个限制Directory /var/www/html/uploads php_admin_flag engine off FilesMatch \.(php|php5|phtml|pht)$ Require all denied /FilesMatch /DirectoryNginx 环境下可以通过location指令限制上传目录的 PHP 请求转发location ~* /uploads/.*\.(php|php5|phtml|pht)$ { deny all; }配置完成之后重启 Web 服务再用浏览器访问上传目录下的一个文件确认配置生效。改动配置前先备份原配置文件出问题可以快速回滚。5.3 代码层修复示例如果业务还必须继续用 KindEditor或者短时间内无法升级系统那至少要补上代码层的漏洞点。下面是一段可以参考的修复逻辑?php session_start(); // 1. 登录态校验 if (empty($_SESSION[user])) { echo json_encode(array(error 1, message unauthorized)); exit; } // 2. 目录白名单 $allowDirs array(image, flash, media, file); $dir empty($_GET[dir]) ? image : $_GET[dir]; if (!in_array($dir, $allowDirs)) { echo json_encode(array(error 1, message invalid dir)); exit; } // 3. 扩展名白名单 $allowExts array(jpg, jpeg, gif, png, bmp, doc, docx, xls, xlsx, pdf, mp3, mp4, swf); $file_name $_FILES[imgFile][name]; $file_ext strtolower(pathinfo($file_name, PATHINFO_EXTENSION)); if (!in_array($file_ext, $allowExts)) { echo json_encode(array(error 1, message invalid file type)); exit; } // 4. 使用固定的安全保存目录不支持用户控制路径 $save_path dirname(__FILE__) . /upload/ . $dir . /; if (!is_dir($save_path)) { mkdir($save_path, 0755, true); } $save_file $save_path . date(YmdHis) . _ . mt_rand(100, 999) . . . $file_ext; move_uploaded_file($_FILES[imgFile][tmp_name], $save_file); echo json_encode(array(error 0, url /path/to/ . basename($save_file))); ?这个示例核心做了四件事校验登录态、限制目录参数、白名单扩展名、保存路径由服务端生成。如果你对原项目比较熟悉可以按同样的思路对原文件做最小改动。有一点要注意上传文件名也建议服务端生成不要直接使用用户上传的原始文件名。原始文件名可能包含特殊字符、路径分隔符在某些环境下可能引发文件覆盖或者路径穿越问题。5.4 长期方案修复单个漏洞只能解决眼前问题长期来看需要从几个方向同时推进。第一版本升级。把 KindEditor 升级到官方修复后的版本或者直接评估替换为其他更活跃维护的编辑器。很多老系统其实只用了编辑器 20% 的功能替换成本没有想象中高。第二入口安全。上传接口这类敏感功能应该放在登录态校验之后即使是后台功能也要做二次权限校验。生产环境尽量不暴露安装目录、示例目录、测试文件。第三定期巡检。把“检查 Web 目录下新增文件”“检查访问日志中的上传接口路径”“扫描常见组件版本号”纳入定期巡检项。很多漏洞不是技术门槛有多高而是没有人持续关注。第四边界防护。在 Web 应用防火墙侧增加针对上传接口的规则拦截对kindeditor/php/upload_json.php等路径的可疑请求。防护规则可以作为纵深防御的一层不能完全替代代码修复但能挡住一部分扫描探测。我在实际项目里经常遇到一种心态觉得一个编辑器而已就算有漏洞也影响不大。但安全事件的路径从来不是单点问题而是多个看似小的风险叠加出来的。文件上传是其中最直接的入口一旦失控后续所有防御都失去了意义。每次排查这类漏洞我都会特意看一眼上传目录里有没有不该出现的文件以及上传接口本身有没有被业务系统“遗忘”在公网。这个习惯建议大家也保持住。