反序列化
记录一下反序列化相关的函数
这篇文章蛮好的:PHP-反序列化(超细的) | spaceman’blog (gitee.io)
常见的魔术方法
__construct() :当对象被创建时触发
__destruct() :当对象被销毁时触发
__toString() :当对象被当作一个字符串使用时触发
__sleep() :序列化对象前调用(其返回需要是一个数组)
__wakeup() :反序列化恢复对象前调用,当字符串表示的对象属性个数大于真实个数时会跳过该函数执行
__call() :当调用对象不存在的方法时自动调用
__get() :从不可访问的属性读取数据时调用,或者不存在的属性
__invoke() :把一个实例对象当作函数使用时被调用
__clone() : 进行对象clone时被调用,用来调整对象的克隆行为
__callStatic() :调用不可访问或不存在的静态方法时自动调用
__isset() :在不可访问的属性上调用 isset() 或 empty() 时触发
__set() :当给不可访问或不存在属性赋值时被调用
__unset() :在不可访问的属性上使用 unset() 时触发
__ set_state() :
当调用 var_export() 导出类时,此静态方法被调用。用 __set_state() 的返回值做为 var_export() 的返回值
__debuginfo() :当调用 var_dump() 打印对象时被调用(当你不想打印所有属性),适用于PHP5.6版本
php代码执行有关的函数
eval()函数:会将字符串当作php代码执行,需要以分号结尾,但比较特殊的是它不能被当作变量执行,例如:$a(“phpinfo();”),a为’eval’;这样子会报函数未定义的错误。
这里去了解一下发现:eval是因为是一个语言构造器而不是一个函数,不能被可变函数调用。
可变函数即变量名加括号,PHP系统会尝试解析成函数,如果有当前变量中的值为命名的函数,就会调用。如果没有就报错。
可变函数不能用于例如:echo,print,unset(),isset(),empty(),include,require eval() 以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。assert()函数:也是将字符串当作php代码执行,不需要以分号结尾,但在php7.1版本后就默认不再可以执行代码了
命令执行相关函数
- system()函数:将字符串作为OS命令执行,自带输出功能。
- passthru()函数:将字符串作为OS命令执行,不需要输出执行结果,且输出全部的内容。
- exec()函数:将字符串作为OS命令执行,需要输出执行结果,比如使用echo将他打印出来,且它只会输出最后一行的内容。
- shell_exec():将字符串作为OS命令执行,需要输出执行结果,且输出全部的内容。
- 反引号``:里面的代码也会当作OS命令执行,需要输出执行结果。
- popen()/proc_open()函数:该函数也可以将字符串当作OS命令来执行,但是该函数返回的是文件指针而非命令执行结果。该函数有两个参数。
以GeekGame的一题为例来进行学习:
unsign
题目的源码如下:
|
可以分析知道有三个魔术方法,分别是:**__destruct(),__invoke(),__get()**。
最终我们是要进入$eva1 里面进行命令执行,所以先给eva1和interesting变量进行赋值,注意不能赋值eval,因为eval不能动态调用,然后这是要进入到**__get()方法中才能进行命令执行,再网上看能看到lover类里面返回了变量,即我们可以通过这里访问web的实例对象不存在的属性从而触发__get()方法,看到最后访问的是QW变量,那只要设置一个web对象中不存在的属性即可;要访问该属性,我们又要触发__invoke()方法,看到有syc的__destruct()**方法返回了一个变量当作函数,那我们给这个变量传入lover对象即可;
所以调用链为这样:**__destruct()=>__invoke()=>__get()**
payload如下:
|
|