1. 项目概述:从“信息泄露”到“攻击跳板”的认知升级
在渗透测试的实战中,我们常常会碰到一些看似不起眼,实则蕴含巨大价值的“信息金矿”。phpinfo()函数生成的信息页面,就是其中最典型、也最容易被低估的一个。很多刚入行的朋友,甚至一些有经验的测试者,看到phpinfo()页面,可能只是扫一眼服务器版本、PHP版本,觉得“哦,知道了”,然后就关掉了。这无异于在金矿门口捡了块石头就转身离开。
实际上,一个完整的phpinfo()输出,远不止是版本号那么简单。它是一个近乎“裸奔”的服务器配置全景图,从系统架构、环境变量、加载的扩展模块,到文件路径、数据库连接信息、甚至是一些临时或备份文件的存放位置,都一览无余。在渗透测试中,它的价值在于“信息不对称”——开发者或管理员无意中暴露了这些信息,而攻击者(或我们这些授权的测试者)则可以利用这些信息,构建出精确的攻击路径,将一处简单的信息泄露点,演变为获取服务器权限、窃取敏感数据的关键跳板。
我遇到过太多案例,一个对外开放的测试页面、一个忘记删除的备份文件、甚至是一个配置错误的临时脚本,里面就包含了对phpinfo()的调用。从发现这个页面开始,整个渗透测试的难度和方向都可能发生根本性转变。它不再是漫无目的的端口扫描和漏洞爆破,而是变成了一场有明确导航的“外科手术式”攻击。接下来,我就结合多年的实战经验,为你系统性地拆解phpinfo()页面中那些值得深挖的关键信息,并展示如何将它们转化为实际的攻击向量。
2. 核心信息挖掘:逐帧解析“服务器自白书”
拿到一个phpinfo()页面,切忌一目十行。我们需要像法医解剖一样,对每一个区块进行细致的检查。以下是我总结的几个核心信息区块及其潜在价值。
2.1 系统环境与配置路径
这是最基础,也最致命的信息。
System,Server API,Build Date: 直接告诉你操作系统的类型(如 Linux、Windows)、Web 服务器的运行模式(如 Apache 2.0 Handler, FPM-FCGI)和 PHP 的编译时间。结合编译时间,可以推测系统可能存在的未修复漏洞时间窗口。例如,一个2018年编译的PHP,很可能存在当时已知但未修补的漏洞。DOCUMENT_ROOT: 网站的根目录绝对路径。这是黄金信息。知道了这个,你就能精准定位网站的核心文件位置,为后续的文件包含、目录遍历攻击提供了绝对坐标。SCRIPT_FILENAME: 当前执行脚本的绝对路径。这有时会暴露出非预期的目录结构,比如脚本放在/var/www/html/admin/backup/下,这本身就是一个敏感路径提示。PATH环境变量: 显示了系统执行命令时的搜索路径。如果其中包含当前用户可写的目录,结合某些漏洞(如通过mail()函数执行命令),可能实现权限提升。_SERVER[“HTTP_HOST”]与_SERVER[“SERVER_NAME”]: 可以帮助你识别虚拟主机配置,有时能发现测试域名、内部域名,这些是扩大攻击面的重要线索。
注意:在 Windows 系统上,路径信息尤为重要。例如,暴露了
C:\xampp\htdocs\这样的路径,结合其他漏洞,攻击的精准度会大幅提高。
2.2 敏感配置与扩展模块
PHP的配置直接决定了攻击面的大小。
register_globals(PHP < 5.4): 如果为On,这几乎是一个“邀请函”级别的危险配置。它会将 GET、POST 等请求变量自动注册为全局变量,极易导致变量覆盖漏洞。虽然在现代PHP中已移除,但在一些老旧系统上仍可能遇到。allow_url_include: 如果为On,则允许include或require函数包含远程文件(如http://evil.com/shell.txt)。这是实现远程文件包含(RFI)攻击的必要条件。open_basedir: 限制PHP可访问的目录范围。如果配置不当或未设置,攻击者可以进行目录遍历,访问系统其他敏感文件(如/etc/passwd)。disable_functions: 列出了被禁用的函数。这是防守方的“黑名单”。仔细研究这个列表,可以了解管理员的安全意识水平,并寻找未被禁用的“替代函数”。例如,如果system、exec、shell_exec、passthru被禁用,但popen、proc_open或反引号`操作符未被禁用,那么命令执行的大门依然敞开。extension_dir: PHP扩展的加载目录。结合路径信息,可以尝试上传恶意.so(Linux) 或.dll(Windows) 扩展文件,并通过某些方式加载,但这属于高阶技巧。- 已加载的扩展: 查看
mysql,mysqli,pdo_mysql,pgsql,sqlite3等数据库扩展是否存在,可以判断后端数据库类型。curl、fileinfo、imagick等扩展的存在,也可能引入额外的攻击面(如 SSRF、文件解析漏洞)。
2.3 文件与目录权限线索
phpinfo()有时会泄露临时文件或会话文件的存储路径。
upload_tmp_dir: 文件上传时的临时存储目录。这个目录通常权限比较宽松。如果你能预测或控制临时文件名(在某些条件下是可能的),结合文件包含漏洞,就能执行上传的恶意代码,这就是经典的“临时文件竞争”攻击的一种前置信息获取。session.save_path: PHP会话文件的存储路径。如果该路径Web用户可读,可能会泄露会话ID,导致会话劫持。如果可写,可能允许注入恶意会话数据。error_log: PHP错误日志路径。如果该文件位于Web目录下且可访问,可能会泄露敏感信息,如数据库连接错误时的账号密码。
2.4 数据库与第三方服务配置
在phpinfo()的PHP Variables部分或通过$_ENV,有时会意外泄露环境变量中配置的数据库连接信息。
- 查找
DB_,MYSQL_,DATABASE_等前缀的环境变量。很多框架(如Laravel)或Docker环境会通过环境变量传递数据库密码,如果配置不当,这些变量可能会出现在phpinfo()中。 $_ENV数组: 仔细检查这个数组,里面可能包含各种API密钥、数据库密码、Redis连接字符串等。我曾在一个测试中,直接从一个陈旧的phpinfo()页面里找到了生产环境的Redis密码,从而直接访问了缓存数据库。
3. 实战利用:将信息转化为攻击链
仅仅收集信息是不够的,关键在于利用。下面我们看几个具体的实战场景。
3.1 场景一:精准定位与文件包含
信息利用:我们从phpinfo()中获得了DOCUMENT_ROOT为/var/www/html/vhosts/target.com/public_html。
攻击链构建:
- 目录遍历探测:尝试访问
https://target.com/../../../../etc/passwd。但更精准的方法是,结合DOCUMENT_ROOT,推算Web服务器上其他可能存在的路径。例如,尝试访问https://target.com/../config.php(对应/var/www/html/vhosts/target.com/config.php),或者https://target.com/../.env(Laravel框架的配置文件)。 - 本地文件包含(LFI):如果网站存在文件包含漏洞,例如参数
?page=../../../../etc/passwd,那么DOCUMENT_ROOT的知识能帮助你更准确地计算“穿越”的层级。例如,如果你的包含点在/public_html/includes/header.php,要包含/etc/passwd,你需要回溯的层级是:从/public_html/includes到/public_html(1层),再到/vhosts/target.com(2层),再到/html(3层),再到/www(4层),再到/var(5层),然后才能访问根目录下的/etc。所以路径可能是?page=../../../../../etc/passwd。没有DOCUMENT_ROOT,你只能盲目尝试。 - 日志文件注入:如果包含漏洞存在,且
error_log路径已知并Web可访问,你可以通过User-Agent等方式将PHP代码写入错误日志,然后包含该日志文件来执行代码。
3.2 场景二:命令执行函数绕过
信息利用:disable_functions列表显示禁用了system, exec, shell_exec, passthru,但popen, proc_open, pcntl_exec未被禁用,且mail()函数可用(sendmail_path配置为默认或可覆盖)。
攻击链构建:
- 利用
popen/proc_open:直接编写利用代码。// 利用 popen $handle = popen('whoami', 'r'); echo fread($handle, 2096); pclose($handle); // 利用 proc_open $descriptorspec = array(0 => array("pipe", "r"), 1 => array("pipe", "w")); $process = proc_open('id', $descriptorspec, $pipes); if (is_resource($process)) { echo stream_get_contents($pipes[1]); fclose($pipes[1]); proc_close($process); } - 利用
mail()函数:mail()函数在发送邮件时,会调用sendmail程序。如果php.ini中的sendmail_path配置项在phpinfo()中显示为默认值(如/usr/sbin/sendmail -t -i),并且我们能够控制mail()的第五个参数additional_parameters(在某些PHP版本和配置下可以),则可以注入命令。但更常见的是,如果服务器配置了safe_mode(已废弃)或某些特定环境,此方法可能无效。更可靠的利用是结合LD_PRELOAD劫持,但这需要能上传.so文件。 - 利用反引号操作符:检查反引号
`ls`是否可用。有时管理员会遗漏这个。 - 利用
imap_open:如果安装了imap扩展,imap_open在连接恶意服务器时可能导致命令执行。 - 利用
ImageMagick:如果imagick扩展已加载,可以研究已知的ImageMagick命令注入漏洞(如Ghostscript漏洞)。
实操心得:遇到
disable_functions,不要轻易放弃。把它当作一个挑战清单。首先尝试列表外的函数,其次研究PHP内置的、可能调用外部程序的函数(如mail(),imap_open(),gdImage*系列处理外部命令?通常不会),最后考虑通过扩展漏洞或PHP内核漏洞进行绕过。网上有大量公开的“绕过disable_functions”的脚本和思路,可以作为参考。
3.3 场景三:数据库凭证泄露与横向移动
信息利用:在$_ENV中发现了DB_PASSWORD=SuperSecret123!和DB_HOST=192.168.1.100。
攻击链构建:
- 直接连接数据库:使用获取的凭证,尝试直接连接内网数据库(
192.168.1.100)。如果Web服务器与数据库网络互通,而你的攻击机不通,你需要先获得一个WebShell,在服务器上使用mysql命令行或编写PHP脚本进行连接。$conn = new mysqli('192.168.1.100', 'root', 'SuperSecret123!'); if ($conn->connect_error) { die("连接失败: " . $conn->connect_error); } echo "连接成功"; // 接下来可以枚举数据库、表,窃取数据 - 权限提升与横向移动:
- 检查数据库用户权限。如果是高权限用户(如
root@%),可能允许执行系统命令(MySQL的sys_exec, PostgreSQL的COPY ... FROM PROGRAM, SQL Server的xp_cmdshell)。 - 利用数据库的“写文件”权限,将PHP WebShell写入Web目录。例如在MySQL中:
SELECT '<?php system($_GET[\"c\"]); ?>' INTO OUTFILE '/var/www/html/shell.php'。这需要secure_file_priv设置为空或对应目录,而phpinfo()中的相关变量可能给你提示。 - 以数据库为跳板,探测数据库主机 (
192.168.1.100) 上开放的其他端口(如SSH-22, RDP-3389),尝试用相同或弱密码进行横向移动。
- 检查数据库用户权限。如果是高权限用户(如
3.4 场景四:临时文件与竞争条件攻击
信息利用:upload_tmp_dir设置为/tmp,并且我们发现了一个文件上传点,但上传后会对文件内容进行检查(如杀毒、图片重渲染),不合格则删除。
攻击链构建:
- 原理:文件上传时,PHP会先将文件保存到
upload_tmp_dir,生成一个随机名称的临时文件(如/tmp/phpabc123),在脚本执行结束后才删除。如果存在一个本地文件包含(LFI)漏洞,我们可以在临时文件被删除前的一瞬间,通过LFI去包含并执行它。 - 实战难点:这个时间窗口极短(毫秒级),需要精确的时序控制。通常需要编写自动化脚本,同时发起大量上传请求和包含请求,进行“竞争”。
- 简化利用:如果服务器配置了
session.upload_progress.enabled = On,我们可以利用“会话上传进度”功能,配合一个已知的LFI漏洞,更稳定地实现文件包含和代码执行,而无需精确的时间竞争。这需要phpinfo()确认该配置已开启。
4. 自动化信息提取与工具化
手动分析phpinfo()页面效率低下,且容易遗漏细节。在实际渗透测试中,我们需要将其工具化。
4.1 手工快速检查清单
即使不用工具,也可以按照以下清单进行快速人工审计:
- Ctrl+F 搜索关键词:
disable_functionsopen_basedirallow_url_includeDOCUMENT_ROOTupload_tmp_dirsession.save_patherror_logsendmail_pathDB_,MYSQL_,REDIS_,AWS_(各种可能的凭证前缀)Extension(查看所有扩展)
- 重点关注
PHP Variables和Environment两个表格,这里最容易泄露配置信息。
4.2 使用自动化脚本
可以编写简单的Python脚本,使用requests库获取页面,然后用BeautifulSoup或正则表达式解析HTML,提取关键配置项,并高亮显示危险配置。
import requests import re from bs4 import BeautifulSoup def analyze_phpinfo(url): try: resp = requests.get(url, timeout=10) soup = BeautifulSoup(resp.text, 'html.parser') # 提取所有表格数据,这里简化处理,实际需要更精细的解析 text = soup.get_text() danger_patterns = { 'allow_url_include': r'allow_url_include.*?On', 'disable_functions': r'disable_functions.*?(no value|特定危险函数未禁用)', 'open_basedir': r'open_basedir.*?(no value)', 'DOCUMENT_ROOT': r'DOCUMENT_ROOT.*?(/var/www/|/home/)', 'upload_tmp_dir': r'upload_tmp_dir.*?(/tmp|可预测路径)', 'session.save_path': r'session\.save_path.*?(/tmp|Web可访问路径)', } print(f"[*] 分析报告 for {url}") for name, pattern in danger_patterns.items(): match = re.search(pattern, text, re.IGNORECASE | re.DOTALL) if match: print(f"[!] 发现潜在风险: {name} -> {match.group()[:100]}...") # 提取所有环境变量 env_section = re.search(r'Environment(.*?)(?=\n\w|$)', text, re.DOTALL | re.IGNORECASE) if env_section: env_vars = re.findall(r'(\w+)=([^\n]+)', env_section.group(1)) for key, value in env_vars: if any(secret in key.lower() for secret in ['pass', 'key', 'secret', 'token', 'auth']): print(f"[CRITICAL] 发现敏感环境变量: {key} = {value}") except Exception as e: print(f"[-] 分析失败: {e}") if __name__ == '__main__': analyze_phpinfo('http://target.com/phpinfo.php')4.3 集成到侦察框架
成熟的渗透测试框架如Metasploit和Burp Suite也具备相关功能。
- Metasploit:有
auxiliary/scanner/http/phpinfo模块,可以扫描并提取信息。 - Burp Suite:
- 使用
Content Discovery功能扫描phpinfo.php,test.php,info.php等常见路径。 - 发现后,使用
Extensions->BApp Store中的PHPInfo LFI Exploiter等插件进行自动分析,并尝试与LFI扫描器联动。 - 将
phpinfo页面的URL发送到Burp Intruder,用于作为其他攻击的“有效载荷”判断依据(例如,在SSRF漏洞中,用phpinfo来探测内网服务)。
- 使用
5. 防御措施与安全建议
作为渗透测试者,我们挖掘漏洞,同样也需要理解如何修复。以下是给开发和管理员的安全建议:
- 严禁在生产环境部署
phpinfo():这是铁律。任何包含phpinfo()的文件都必须在开发、测试完成后彻底删除。使用版本控制工具(如Git)的.gitignore文件忽略这类测试脚本。 - 严格控制错误信息输出:在
php.ini中设置display_errors = Off,log_errors = On,并确保error_log指向一个Web不可访问的安全路径。在生产环境,错误不应展示给用户。 - 强化PHP配置:
allow_url_fopen = Offallow_url_include = Offopen_basedir设置为必要的目录范围,并用冒号分隔。disable_functions应尽可能包含所有不必要的命令执行和系统函数:system, exec, shell_exec, passthru, proc_open, popen, pcntl_exec, eval等。注意,禁用函数不是银弹,需结合其他安全措施。expose_php = Off可以隐藏HTTP响应头中的PHP版本信息。
- 使用安全的凭证管理方式:绝对不要将数据库密码、API密钥等硬编码在源码中或通过
$_ENV直接暴露。使用操作系统级别的环境变量(如通过Web服务器配置传入),或使用专门的密钥管理服务(如HashiCorp Vault, AWS Secrets Manager)。 - 定期安全审计与配置检查:使用
php -i命令在命令行下查看配置,而非通过Web访问。定期进行漏洞扫描和渗透测试,主动发现此类信息泄露问题。 - 最小权限原则:运行PHP-FPM或Apache进程的用户权限应被严格限制,不能是
root。数据库用户也应遵循最小权限原则,禁止使用root账户进行Web应用连接。
6. 实战案例复盘与思考
最后,分享一个我印象深刻的案例。在一次外部测试中,目标网站只有一个简单的登录页面,扫描器没有发现任何常见漏洞。但在对目录进行模糊测试时,发现了一个/debug.php文件,返回403。尝试了/.debug.php.swp(vim备份文件) 和/debug.php.bak,成功下载到了源码备份。打开一看,里面赫然写着<?php phpinfo(); ?>。显然,这是开发者留下的调试文件,原文件被删除,但备份留下了。
访问这个备份文件,完整的phpinfo()页面出现了。在其中,我发现了:
DOCUMENT_ROOT: /home/ubuntu/app/current/public- 一个环境变量
BACKUP_DB_URL=mysql://backup:WeakBackupPass@10.0.1.5:3306/backup_db disable_functions列表很长,但mail()函数可用。
利用DOCUMENT_ROOT,我尝试了路径遍历,发现了 Laravel 框架的.env文件,但里面是空的(因为环境变量已通过其他方式设置)。真正的突破口是那个备份数据库的凭证。通过已获得的一个低权限WebShell(通过另一个细微的注入点获得),我在服务器上使用mysql客户端连接了内网的10.0.1.5,发现backup用户权限极高,可以读写文件。最终,通过数据库的INTO OUTFILE功能,将WebShell写入了主应用目录,获得了主Web服务的控制权。
这个案例告诉我们:第一,信息泄露的源头往往很不起眼(备份文件);第二,phpinfo()泄露的信息需要关联分析(数据库凭证+内网IP);第三,防御是一个整体,一个环节的疏漏(备份文件未删除、内网数据库弱密码、数据库权限过大)可能导致全线崩溃。作为测试者,我们必须具备这种将零散信息拼接成完整攻击链的耐心和能力。每一次查看phpinfo(),都不要把它当作终点,而应视为一个充满可能性的新起点。