file_get_contents与trim组合漏洞解析

13 人参与

在一次 CTF 赛后复盘时,团队发现一道题目只用了两行 PHP 代码,却让选手们在几分钟内完成了全局变量注入和文件读取的组合攻击。核心代码是 $c = trim(file_get_contents($b));,看似普通的输入过滤,却在特定的 $b 取值下把整个读取结果清空,从而绕过后续的空值检查。

漏洞成因剖析

PHP 的 file_get_contents 可以接受本地路径、URL 甚至 php://input 等包装协议,只要传入的字符串合法就会把目标内容直接返回。随后 trim 默认去除两端的空白字符、换行、制表符和 Unicode 的空格符。如果读取的文件恰好只包含这些字符,trim 的返回值便是空字符串。

攻击者只需控制 $b,将其指向一个仅包含空白的文件(例如 /tmp/blank.txt),或者利用 php://filter 包装把原始内容全部过滤掉,trim 便把返回值压成 ''。随后代码中常见的 if ($c) 检查就会失效,导致后续逻辑误以为读取失败,从而触发错误信息泄露或默认路径 fallback,进而执行任意文件包含。

利用链路示例

<?php
// 漏洞片段
$b = $_GET['file'];               // 可控参数
$c = trim(file_get_contents($b)); // 关键点
if ($c) {
    // 正常业务处理
    echo "Data: ".$c;
} else {
    // 触发错误路径,包含 flag.php
    include 'flag.php';
}
?>

利用者把 file 参数设为 php://filter/convert.base64-encode/resource=flag.php,读取的原始二进制会被 Base64 编码后返回。因为编码结果以字母和数字组成,不会被 trim 剔除,$c 非空,导致 if 分支走向正常路径,进而把 flag 直接打印出来。

防御建议

  • 严禁直接把用户输入传给 file_get_contents,应使用白名单校验或固定目录前缀。
  • 对返回值进行内容完整性检查,而非仅靠 trim 判断空值。
  • 禁用不必要的包装协议(如 php://filterdata),或在 php.ini 中关闭 allow_url_fopen
  • 在关键路径使用 realpathis_file 双重验证,防止路径遍历。

把这些细节写进代码审计清单,哪怕是看似无害的 trim,也能在特定场景下成为攻防的分水岭。别再掉进同样的坑了。

参与讨论

13 条评论
  • 月光奶昔

    php://filter这招绝了,直接绕过空值检查

    回复
  • 社恐の小石

    求问下白名单具体怎么实现?有没有现成的库推荐

    回复
  • Blade_刃舞

    踩过类似的坑,上次用file_get_contents读取日志文件就被注入了

    回复
  • 幻境拾贝者

    trim清空结果这思路确实骚🤔

    回复
  • 快乐的小蜜蜂

    realpath验证真的有用吗?感觉还是会被绕过

    回复
  • 夜叉童子

    所以只要文件里全是空格就会被当成空值?这设计有点坑啊

    回复
  • 笑红尘

    flag.php直接被base64打印出来也太秀了

    回复
  • 寒夜星辰

    这种漏洞太隐蔽了,之前写代码完全没注意到trim还能这么玩

    回复
  • 瑜伽生活家

    白名单校验确实比黑名单靠谱点

    回复
    1. 漂泊鲸

      @ 瑜伽生活家 黑名单很容易被绕过去

      回复
  • 龙吟叟

    原来trim还能这样绕,以后得注意了

    回复
  • 话少小鲸鱼

    两行代码能玩出花,CTF题真会挖坑

    回复
    1. 倒流时光的理发师

      @ 话少小鲸鱼 CTF题总是能挖出意想不到的漏洞

      回复