PHP Trick

PHP Trick

反序列化

首先肯定要把所有的魔术方法看一看啦

__construct() 	构造函数
__destruct()  	析构函数
__call()				在对象中调用一个不可访问方法时,__call() 会被调用
__callStatic()	在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用
__get()					读取不可访问或不存在的属性的值时,__get() 会被调用
__set()					在给不可访问或不存在的属性赋值时,__set() 会被调用
__isset()				当对不可访问或不存在的属性调用 isset()empty() 时,__isset() 会被调用
__unset()				当对不可访问或不存在的属性调用 unset() 时,__unset() 会被调用
__sleep()				当对一个对象进行序列化操作时,会先调用__sleep()方法再进行序列化操作
__wakeup() 			当对一个对象进行反序列化操作时,会先调用__wakeup()方法再进行序列化操作
__serialize()__sleep()方法作用基本相同
__unserialize()__wakeup()方法作用基本相同
__toString()		__toString()方法用于一个类被当成字符串时应怎样回应,只能返回字符串不然会飙错
__invoke()			当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用
__set_state()		当调用 var_export()导出类时,此静态方法会被调用
__clone()				对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。
__debugInfo()		当通过 var_dump() 转储对象,获取应该要显示的属性的时候, 该函数就会被调用。如果对象中没有	定义该方法,那么将会展示所有的公有、受保护和私有的属性。

__wakeup()

经典的CVE绕过wakeup方法CVE-2016-7124

影响范围:

PHP5 < 5.6.25
PHP7 < 7.0.10

只需要构造出序列化的字符串并将属性数改为大于真实属性数即可

__destruct()

对于php版本在8.0以下的,只要让程序运行过程中抛出异常,就不会执行__destruct()方法,但是die方法实惠正常进行垃圾回收并触发__destruct()方法的

__call()(__callStatic())

先来看以下call的官方说明格式

function __call(string $function_name, array $arguments)
{
    // 方法体
}

也就是说你传一个参数也好多个参数也罢,这些内容都会被以数组的形式存储起来,所以在__call()方法中调用参数内容时一定一定要按照array的形式调用

原生类

有时候反序列化会碰到没有给出足够使用的类的时候,这时我们可以利用一把PHP自带的原生类进行构造(以下内容都在PHP7环境中进行测试)

Exception

<?php
		$a=new Exception("1");
		echo $a;
		//Exception: 1 in /Users/jlan/PhpstormProjects/untitled/index.php:2 Stack trace: #0 {main}
?>

我们发现传出的内容为:Exception: 传入的字符串如果此时正好有eval包裹了该类变量我们就能通过传入xx;恶意代码;/*达到任意命令执行的效果

FilesystemIterator

<?php
  	$a=new FilesystemIterator("./");
  	echo $a;
  	//index.php
?>

传入目录返回一个迭代器,toString返回迭代器的第一项,可使用glob协议进行通配

DirectoryIterator

<?php
  	$a=new DirectoryIterator("./");
  	echo $a;
  	//index.php
?>

同上

GlobIterator

<?php
$a=new GlobIterator("./f*");
echo $a;
//flag.txt
?>

GlobIterator和上方这两个类差不多,不过glob是GlobIterator类本身自带的,因此在遍历的时候,就不需要带上glob协议头了,只需要后面的相关内容

SplFileObject

<?php
    $a=new SplFileObject("./flag.txt");
    echo $a;
		//读取文件首行内容
?>

SplFileObject这个类返回的也是一个迭代器,但是可以用伪协议啊

SplFileInfo

<?php
    $a=new SplFileInfo("phpinfo();");
    echo $a;
    //phpinfo();
    eval($a);
?>

原封不动返回传入内容

Error

Exception完全一致

URL解析

文件包含

很重要的一定是各种伪协议了

file://	直接读取文件,不受allow_url_fopen和allow_url_include影响
data://[<MIME-type>][;cherset=<encoding>][;base64],<data>	可以直接往里面放内容
zip://[压缩包绝对路径]#[压缩包内文件]	可以直接读取压缩包中的文件
php://input	直接读取请求体的所有内容
php://output	纯写入个人感觉还没啥大用
php://fd		包含文件描述符指向的文件
php://memory	读写内存的临时文件,没感觉有啥利用方法(
php://temp		上面的升级版,在临时文件>2MB时就会从内存中拉出来变成在默认sys_get_temp_dir目录下的文件
php://filter	文件读取过滤器
phar://	就是phar啊你还想要什么(其实也可以和zip一样读取压缩包内容)

php://filter

最能玩出花来的一个协议,最基础的当然是base64读取了

php://filter/convert.base64-encode/resource=xxx.php

中间convert可用的内容有

convert.quoted-printable-encode	将文本中的不可见字符转换为可打印的字符进行输出
convert.quoted-printable-decode 上述逆过程

说实话还没怎么见过这个用法,倒也确实没啥卵用

其中可用的最多的就是这个

convert.iconv.<input-encoding>.<output-encoding>

这个转换器可以将内容从任意一个编码转换为另一种编码,https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d

文件上传