file_get_contents与trim组合漏洞解析

在一次 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,也能在特定场景下成为攻防的分水岭。别再掉进同样的坑了。

参与讨论

0 条评论

    暂无评论,快来发表你的观点吧!