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