首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

Web 文件上傳漏洞系列(一):文件名檢測繞過導致任意文件寫入

     編輯:PHP 相關知識

文件上傳是 Web 應用高危功能區。本系列首篇分析**「使用用戶原始文件名保存」**導致的經典漏洞:攻擊者構造惡意文件名(含路徑遍歷、截斷、雙重擴展名),繞過後綴檢查,實現任意文件寫入、甚至 Getshell。

存在漏洞的典型代碼

<?php
if (isset($_FILES['img'])) {
    $file = save_file($_FILES['img']);
    if ($file === false) exit('上傳失敗!');
    echo "上傳成功!$file";
}

function check_file($img) {
    if ($img['error'] > 0) return false;
    $filename = $img['name']; // 直接取用戶文件名
    $len = strrpos($filename, '.');
    if ($len === false) return false;
    $ext = strtolower(substr($filename, $len + 1));
    return in_array($ext, ['jpg', 'jpeg', 'png']); // 僅檢查後綴
}

function save_file($img) {
    if (!check_file($img)) return false;
    $filename = $img['name']; // 再次直接使用
    $newfile = "upload/" . $filename; // 拼接路徑
    if (!move_uploaded_file($img['tmp_name'], $newfile)) return false;
    return $newfile;
}
?>

三大繞過手法

1. 雙重擴展名 + 截斷 (PHP 4 / 早期 PHP 5 / ASP / JSP)

構造文件名:`shell.php.jpg` 或 `shell.php%00.jpg`

  • 後綴檢查取最右 `.` 後 → `jpg` ✅ 通過
  • 文件系統保存時,`%00` (null byte) 截斷字符串 → 實際創建 `shell.php` 💥

現狀:PHP 5.3.4+ 核心修復了 null byte 截斷,但老版本、ASP/JSP 仍大量存在。

2. 路徑遍歷 `../`

構造文件名:`../../../var/www/html/shell.php.jpg`

  • 後綴檢查通過
  • `move_uploaded_file` 配合相對路徑跳出 `upload/` 目錄,寫入 Web 根目錄 💥

3. 大小寫繞過 / 解析漏洞利用

  • 文件名:`shell.PhP`、`shell.pHp5`、`shell.php.htaccess`
  • 配合 Apache `AddHandler php5-script .php .php5` 或 Nginx 錯誤配置解析

根因分析

  1. 信任客戶端輸入:`$_FILES['name']`、`['type']` 全來自 HTTP 請求,可任意偽造。
  2. 黑名單/後綴檢查不足:僅檢查後綴,忽略文件內容、路徑、編碼。
  3. 直接拼接用戶文件名入路徑:未做基準路徑限制、隨機化重命名。

修復方案:白名單 + 隨機名 + 目錄隔離 + 權限控制

<?php
function secure_upload($file, $allowedExt = ['jpg', 'png', 'gif'], $saveDir = '/var/www/uploads/') {
    // 1. 基礎校驗
    if ($file['error'] !== UPLOAD_ERR_OK) return ['success' => false, 'msg' => '上傳錯誤'];
    if ($file['size'] > 5 * 1024 * 1024) return ['success' => false, 'msg' => '文件過大'];

    // 2. 後綴白名單 (從原始名提取,僅作參考)
    $origName = basename($file['name']); // 防路徑遍歷
    $ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION));
    if (!in_array($ext, $allowedExt)) return ['success' => false, 'msg' => '禁止的文件類型'];

    // 3. 內容檢測 (MIME + 魔數) - 關鍵!
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime  = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);
    $allowedMime = ['image/jpeg', 'image/png', 'image/gif'];
    if (!in_array($mime, $allowedMime)) return ['success' => false, 'msg' => '文件內容類型不合法'];

    // 4. 隨機文件名 + 正確後綴
    $newName = bin2hex(random_bytes(16)) . '.' . $ext; // 如 a3f2...jpg
    $destPath = $saveDir . $newName;

    // 5. 保存目錄權限:不可執行 (Nginx/Apache 配置 location ~ \.php$ { deny all; })
    if (!move_uploaded_file($file['tmp_name'], $destPath)) {
        return ['success' => false, 'msg' => '保存失敗'];
    }

    // 6. 可選:二次渲染/壓縮 (GD/Imagick) 徹底破壞圖片馬
    // $img = imagecreatefromjpeg($destPath); imagejpeg($img, $destPath);

    return ['success' => true, 'path' => $destPath, 'url' => '/uploads/' . $newName];
}
?>

防禦深度清單

層級措施作用
應用層隨機文件名 + 白名單後綴 + MIME/魔數驗證源頭阻斷惡意文件
應用層圖片二次渲染 (GD重寫)破壞隱藏在圖片數據段的 Shell 代碼
存儲層上傳目錄獨立分區/掛載 `noexec,nodev,nosuid`系統級禁止執行
Web服務器上傳目錄禁用腳本解析 (Nginx `location /uploads { deny all; }` 或 Apache `SetHandler default-handler`)即使寫入 PHP 也無法執行
網絡層上傳域名分離 (upload.example.com),Cookie 隔離縮小 XSS/CSRF 影響面
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved