PHPEMS 11.0 参数 XSS 漏洞CVE-2026-3946
PHPEMS 是一款基于 PHP 开发的国产开源无纸化模拟考试系统,兼具易用性和功能灵活性,也是国内主流的开源在线考试系统之一。
一、基本情况
PHPEMS 是基于 PHP+MySQL 开发的在线考试管理系统,开源免费为主,商业版本补充,系统操作界面简洁,部署和二次开发难度低。

PHPEMS 符合国内开发者的技术使用习惯,核心是面向教育机构、企业、培训机构等场景提供在线考试、练习、测评一体化解决方案。
栋科技漏洞库关注到 PHPEMS 11.0 中的askcontent 参数 XSS 漏洞,漏洞现在已经被追踪为CVE-2026-3946,漏洞CVSS 4.0评分5.1。
二、漏洞分析
CVE-2026-3946漏洞是存在于 PHPEMS 11.0 版本中的一个跨站脚本(XSS)漏洞,该漏洞是存在于 askcontent 参数的一个 XSS 漏洞。
具体来说,漏洞源于PHPEMS的 /index.php 接口在处理ask=app-ask相关的业务逻辑时未对askcontent参数进行充分HTML转义或过滤。
远程攻击者利用漏洞,构造包含恶意 JavaScript 脚本的链接诱导受害者访问,在受害者的浏览器环境中执行任意脚本,潜在风险较大。
从而导致 Cookie 窃取、会话劫持或网页篡改。
三、POC概念验证
(一)漏洞复现
1、注册普通用户账号:用户名 test,密码 test123
2、登录后,访问以下地址:http://192.168.184.1:8094/index.php?ask,随后点击页面中的「提问」按钮。

3、输入相关信息:标题可自定义,在内容输入框中填写恶意代码<script>alert(document.cookie)</script>,填写完成后点击「提问」。

4、接下来,注销并以管理员身份登录:peadmin-peadmin。打开URL:http://192.168.184.1:8094/index.php?ask-master-ask

5、你可以看到你刚才问的问题的标题,“测试”。然后单击“操作”部分下方的类似编辑图标。

6、然后会弹出XSS攻击语句:

(二)本地代码审核验证:
1、首先,点击此页面上的“提问”,并使用Burp Suite捕获网络数据包:


2、我们提交的XSS攻击语句作为参数通过args[askcontent]传递。


3、前端分析:
由于此框架是一个MVC框架,因此`args[askcontent]`参数可以追溯到`phpems\app\ask\controller\ask.app.php`的第46行。

4、如您所见,`askcontent`参数由`tidyHtml`函数控制。`tidyHtml`函数可以追溯到`phpems\lib\safe.cls.php`中的第14-19行。

5、进一步追踪“purify”函数,它出现在lib/include/htmlpurizer/htmlpurifier.php的第146行和第229行之间。

6、简而言之,`tidyHtml->purify `函数的目的是过滤危险参数。通常,XSS攻击语句应该过滤掉“<script>”。
然而,经过动态调试,发现从`ask.app.php`中的第45行开始,
原始XSS语句是`<script>alert(document.cookie)</script>`,传递的XSS语句为`<script>alert(document.cookie)</script>'。
在第46行被`tidyHtml`过滤后,语句`<script>alert(document.cookie)</script>`仍出现在第48行。
因此,有两种可能性:
(1)无法筛选此语句“<script>”;
(2)`tidyHtml`函数无效。
因此,最终的XSS语句“<script>alert(document.cookie)</script>”将原封不动地传递给后端。

(三)后端分析:
因为触发漏洞的URL是:http://192.168.184.1:8094/index.php?ask-师父问,我们查了这个页面的源代码。
在以下路径中:phpems\data\compile\ask\master\%%cpl%%ask_answers.php,代码的第31行触发了XSS攻击语句:

如您所见,之前提交的XSS语句中的参数“askcontent”被传递给这行代码。
在`args[askcontent]中,`args `是前端参数的容器,`ask`是后端业务数据的容器。因此,这里的`args `变为`ask`。
让我们进一步分析这段代码:
`$this->tpl_var`是自定义PHP模板引擎的变量容器,其核心功能是“模板的后端数据”;
`stripslashes()用于删除由`addslashes()`添加的反斜杠,例如将`\“`转换回`”,从而消除对恶意语句的过滤;
`html_entity_decode()是html实体解码(例如,将“<”转换回“<”)。
这意味着,即使在存储过程中进行了简单的实体编码,
这步也会将“<script>alert(document.cookie)</script>”重新解码为“<script>alert(document.cookie-)</script>”,从而导致XSS漏洞;
最后,攻击的XSS语句通过PHP语句的`echo`直接输出到页面,导致存储的XSS漏洞。
四、影响范围
PHPEMS 11.0
五、修复建议
未知
六、参考链接
管理员已设置登录后刷新可查看